Mercurial > games > semicongine
changeset 1182:e9a212e9cdf7 compiletime-tests
sync from bedroom to office
author | sam <sam@basx.dev> |
---|---|
date | Wed, 03 Jul 2024 00:08:19 +0700 |
parents | 6b66e6c837bc |
children | 850450bfe2a2 |
files | static_utils.nim |
diffstat | 1 files changed, 224 insertions(+), 280 deletions(-) [+] |
line wrap: on
line diff
--- a/static_utils.nim Tue Jul 02 17:50:14 2024 +0700 +++ b/static_utils.nim Wed Jul 03 00:08:19 2024 +0700 @@ -19,13 +19,19 @@ template PassFlat {.pragma.} template ShaderOutput {.pragma.} template VertexIndices {.pragma.} -template DescriptorSet {.pragma.} + +const INFLIGHTFRAMES = 2'u32 +const MEMORY_ALIGNMENT = 65536'u64 # Align buffers inside memory along this alignment +const BUFFER_ALIGNMENT = 64'u64 # align offsets inside buffers along this alignment # some globals that will (likely?) never change during the life time of the engine type - DescriptorSetType = enum - GlobalSet - MaterialSet + SupportedGPUType = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64] + + ShaderObject[TShader] = object + vertexShader: VkShaderModule + fragmentShader: VkShaderModule + VulkanGlobals = object instance: VkInstance device: VkDevice @@ -33,19 +39,59 @@ queueFamilyIndex: uint32 queue: VkQueue -const INFLIGHTFRAMES = 2'u32 -const MAX_DESCRIPTORSETS = tt.enumLen(DescriptorSetType) -const MEMORY_ALIGNMENT = 65536'u64 # Align buffers inside memory along this alignment -const BUFFER_ALIGNMENT = 64'u64 # align offsets inside buffers along this alignment + IndexType = enum + None, UInt8, UInt16, UInt32 + + IndirectGPUMemory = object + vk: VkDeviceMemory + size: uint64 + needsTransfer: bool # usually true + DirectGPUMemory = object + vk: VkDeviceMemory + size: uint64 + data: pointer + needsFlush: bool # usually true + GPUMemory = IndirectGPUMemory | DirectGPUMemory + + Buffer[TMemory: GPUMemory] = object + memory: TMemory + vk: VkBuffer + offset: uint64 + size: uint64 + + GPUArray[T: SupportedGPUType, TMemory: GPUMemory] = object + data: seq[T] + buffer: Buffer[TMemory] + offset: uint64 + GPUValue[T: object|array, TMemory: GPUMemory] = object + data: T + buffer: Buffer[TMemory] + offset: uint64 + GPUData = GPUArray | GPUValue + + DescriptorSetType = enum + GlobalSet + MaterialSet + DescriptorSet[T: object, sType: static DescriptorSetType] = object + data: T + vk: array[INFLIGHTFRAMES, VkDescriptorSet] + + Pipeline[TShader] = object + vk: VkPipeline + layout: VkPipelineLayout + descriptorSetLayouts: array[DescriptorSetType, VkDescriptorSetLayout] + BufferType = enum + VertexBuffer, IndexBuffer, UniformBuffer + RenderData = object + descriptorPool: VkDescriptorPool + # tuple is memory and offset to next free allocation in that memory + indirectMemory: seq[tuple[memory: IndirectGPUMemory, usedOffset: uint64]] + directMemory: seq[tuple[memory: DirectGPUMemory, usedOffset: uint64]] + indirectBuffers: seq[tuple[buffer: Buffer[IndirectGPUMemory], btype: BufferType, usedOffset: uint64]] + directBuffers: seq[tuple[buffer: Buffer[DirectGPUMemory], btype: BufferType, usedOffset: uint64]] var vulkan: VulkanGlobals -type - SupportedGPUType = float32 | float64 | int8 | int16 | int32 | int64 | uint8 | uint16 | uint32 | uint64 | TVec2[int32] | TVec2[int64] | TVec3[int32] | TVec3[int64] | TVec4[int32] | TVec4[int64] | TVec2[uint32] | TVec2[uint64] | TVec3[uint32] | TVec3[uint64] | TVec4[uint32] | TVec4[uint64] | TVec2[float32] | TVec2[float64] | TVec3[float32] | TVec3[float64] | TVec4[float32] | TVec4[float64] | TMat2[float32] | TMat2[float64] | TMat23[float32] | TMat23[float64] | TMat32[float32] | TMat32[float64] | TMat3[float32] | TMat3[float64] | TMat34[float32] | TMat34[float64] | TMat43[float32] | TMat43[float64] | TMat4[float32] | TMat4[float64] - ShaderObject[TShader] = object - vertexShader: VkShaderModule - fragmentShader: VkShaderModule - func alignedTo[T: SomeInteger](value: T, alignment: T): T = let remainder = value mod alignment if remainder == 0: @@ -151,17 +197,6 @@ const `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute) body -template ForDescriptorSets(shader: typed, setNumber, descriptorSet, body: untyped): untyped = - var n = DescriptorSetType.low - for theFieldname, value in fieldPairs(shader): - when value.hasCustomPragma(DescriptorSet): - block: - let `setNumber` {.inject.} = n - let `descriptorSet` {.inject.} = value - body - if n < DescriptorSetType.high: - n.inc - template ForDescriptorFields(shader: typed, fieldname, typename, countname, bindingNumber, body: untyped): untyped = var `bindingNumber` {.inject.} = 1'u32 for theFieldname, value in fieldPairs(shader): @@ -233,50 +268,8 @@ else: return 1 -type - IndexType = enum - None, UInt8, UInt16, UInt32 - - IndirectGPUMemory = object - vk: VkDeviceMemory - size: uint64 - needsTransfer: bool # usually true - DirectGPUMemory = object - vk: VkDeviceMemory - size: uint64 - data: pointer - needsFlush: bool # usually true - GPUMemory = IndirectGPUMemory | DirectGPUMemory - - Buffer[TMemory: GPUMemory] = object - memory: TMemory - vk: VkBuffer - offset: uint64 - size: uint64 - - GPUArray[T: SupportedGPUType, TMemory: GPUMemory] = object - data: seq[T] - buffer: Buffer[TMemory] - offset: uint64 - GPUValue[T: object|array, TMemory: GPUMemory] = object - data: T - buffer: Buffer[TMemory] - offset: uint64 - GPUData = GPUArray | GPUValue - - Pipeline[TShader] = object - pipeline: VkPipeline - layout: VkPipelineLayout - descriptorSetLayouts: array[MAX_DESCRIPTORSETS, VkDescriptorSetLayout] - BufferType = enum - VertexBuffer, IndexBuffer, UniformBuffer - RenderData = object - descriptorPool: VkDescriptorPool - # tuple is memory and offset to next free allocation in that memory - indirectMemory: seq[tuple[memory: IndirectGPUMemory, usedOffset: uint64]] - directMemory: seq[tuple[memory: DirectGPUMemory, usedOffset: uint64]] - indirectBuffers: seq[tuple[buffer: Buffer[IndirectGPUMemory], btype: BufferType, usedOffset: uint64]] - directBuffers: seq[tuple[buffer: Buffer[DirectGPUMemory], btype: BufferType, usedOffset: uint64]] +template sType(descriptorSet: DescriptorSet): untyped = + get(genericParams(typeof(gpuData)), 1) template UsesIndirectMemory(gpuData: GPUData): untyped = get(genericParams(typeof(gpuData)), 1) is IndirectGPUMemory @@ -471,28 +464,100 @@ when typeof(fieldvalue) is GPUData: UpdateGPUBuffer(fieldvalue) -proc AssertCompatible(TShader, TDescriptorSet: typedesc, descriptorSetType: static DescriptorSetType) = - ForDescriptorSets(default(TShader), setNumber, descriptorSet): - if setNumber == descriptorSetType: - assert typeof(descriptorSet) is TDescriptorSet - -proc CreateDescriptorSet[T, TShader]( +proc InitDescriptorSet( renderData: RenderData, - pipeline: Pipeline[TShader], - value: T, - descriptorSetType: static DescriptorSetType -): array[INFLIGHTFRAMES.int, VkDescriptorSet] = + layout: VkDescriptorSetLayout, + descriptorSet: var DescriptorSet, +) = + for name, value in descriptorSet.data.fieldPairs: + when typeof(value) is GPUValue: + assert value.buffer.vk.valid + # TODO: + # when typeof(value) is Texture: + # assert value.texture.vk.valid - static: AssertCompatible(TShader, T, descriptorSetType) - - var layouts = newSeqWith(result.len, pipeline.descriptorSetLayouts[descriptorSetType.int]) + # allocate + var layouts = newSeqWith(descriptorSet.vk.len, layout) var allocInfo = VkDescriptorSetAllocateInfo( sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, descriptorPool: renderData.descriptorPool, descriptorSetCount: uint32(layouts.len), pSetLayouts: layouts.ToCPointer, ) - checkVkResult vkAllocateDescriptorSets(vulkan.device, addr(allocInfo), result.ToCPointer) + checkVkResult vkAllocateDescriptorSets(vulkan.device, addr(allocInfo), descriptorSet.vk.ToCPointer) + + # write + var descriptorSetWrites: newSeq[VkWriteDescriptorSet](descriptorSet.vk.len) + for i in 0 ..< descriptorSet.vk.len: + descriptorSetWrites.add + + + vkUpdateDescriptorSets(vulkan.device, descriptorSetWrites.len.uint32, descriptorSetWrites.ToCPointer, 0, nil) + +#[ +proc WriteDescriptors[TShader, TUniforms, TGlobals](renderData: RenderData, uniforms: TUniforms, globals: TGlobals) = + var descriptorSetWrites: seq[VkWriteDescriptorSet] + ForDescriptorFields(default(TShader), fieldName, descriptorType, descriptorCount, descriptorBindingNumber): + for frameInFlight in 0 ..< renderData.descriptorSets.len: + when descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: + when HasGPUValueField[TUniforms](fieldName): + WithGPUValueField(uniforms, fieldName, gpuValue): + let bufferInfo = VkDescriptorBufferInfo( + buffer: gpuValue.buffer.vk, + offset: gpuValue.buffer.offset, + range: gpuValue.buffer.size, + ) + descriptorSetWrites.add VkWriteDescriptorSet( + sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + dstSet: renderData.descriptorSets[frameInFlight], + dstBinding: descriptorBindingNumber, + dstArrayElement: uint32(0), + descriptorType: descriptorType, + descriptorCount: descriptorCount, + pImageInfo: nil, + pBufferInfo: addr(bufferInfo), + ) + elif HasGPUValueField[TGlobals](fieldName): + WithGPUValueField(globals, fieldName, theValue): + let bufferInfo = VkDescriptorBufferInfo( + buffer: theValue.buffer.vk, + offset: theValue.buffer.offset, + range: theValue.buffer.size, + ) + descriptorSetWrites.add VkWriteDescriptorSet( + sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + dstSet: renderData.descriptorSets[frameInFlight], + dstBinding: descriptorBindingNumber, + dstArrayElement: uint32(0), + descriptorType: descriptorType, + descriptorCount: descriptorCount, + pImageInfo: nil, + pBufferInfo: addr(bufferInfo), + ) + else: + {.error: "Unable to find field '" & fieldName & "' in uniforms or globals".} + elif descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: + # TODO + let imageInfo = VkDescriptorImageInfo( + sampler: VkSampler(0), + imageView: VkImageView(0), + imageLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, + ) + descriptorSetWrites.add VkWriteDescriptorSet( + sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + dstSet: renderData.descriptorSets[frameInFlight], + dstBinding: descriptorBindingNumber, + dstArrayElement: 0'u32, + descriptorType: descriptorType, + descriptorCount: descriptorCount, + pImageInfo: addr(imageInfo), + pBufferInfo: nil, + ) + else: + assert false, "Unsupported descriptor type" + vkUpdateDescriptorSets(vulkan.device, uint32(descriptorSetWrites.len), descriptorSetWrites.ToCPointer, 0, nil) +]# + converter toVkIndexType(indexType: IndexType): VkIndexType = case indexType: @@ -640,11 +705,11 @@ # descriptor sets # need to consider 4 cases: uniform block, texture, uniform block array, texture array - elif hasCustomPragma(value, DescriptorSet): - assert descriptorSetCount < MAX_DESCRIPTORSETS, &"{tt.name(TShader)}: maximum {MAX_DESCRIPTORSETS} allowed" + elif typeof(value) is DescriptorSet: + assert descriptorSetCount <= DescriptorSetType.high.int, &"{tt.name(TShader)}: maximum {DescriptorSetType.high} allowed" var descriptorBinding = 0 - for descriptorName, descriptorValue in fieldPairs(value): + for descriptorName, descriptorValue in fieldPairs(value.data): when typeof(descriptorValue) is Texture: samplers.add "layout(set=" & $descriptorSetCount & ", binding = " & $descriptorBinding & ") uniform " & GlslType(descriptorValue) & " " & descriptorName & ";" @@ -721,27 +786,28 @@ ): Pipeline[TShader] = # create pipeline - ForDescriptorSets(default(TShader), setNumber, descriptorSet): - var layoutbindings: seq[VkDescriptorSetLayoutBinding] - ForDescriptorFields(descriptorSet, fieldName, descriptorType, descriptorCount, descriptorBindingNumber): - layoutbindings.add VkDescriptorSetLayoutBinding( - binding: descriptorBindingNumber, - descriptorType: descriptorType, - descriptorCount: descriptorCount, - stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS), - pImmutableSamplers: nil, + for theFieldname, value in fieldPairs(default(TShader)): + when typeof(value) is DescriptorSet: + var layoutbindings: seq[VkDescriptorSetLayoutBinding] + ForDescriptorFields(value.data, fieldName, descriptorType, descriptorCount, descriptorBindingNumber): + layoutbindings.add VkDescriptorSetLayoutBinding( + binding: descriptorBindingNumber, + descriptorType: descriptorType, + descriptorCount: descriptorCount, + stageFlags: VkShaderStageFlags(VK_SHADER_STAGE_ALL_GRAPHICS), + pImmutableSamplers: nil, + ) + var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( + sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + bindingCount: layoutbindings.len.uint32, + pBindings: layoutbindings.ToCPointer ) - var layoutCreateInfo = VkDescriptorSetLayoutCreateInfo( - sType: VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - bindingCount: layoutbindings.len.uint32, - pBindings: layoutbindings.ToCPointer - ) - checkVkResult vkCreateDescriptorSetLayout( - vulkan.device, - addr(layoutCreateInfo), - nil, - addr(result.descriptorSetLayouts[setNumber.int]) - ) + checkVkResult vkCreateDescriptorSetLayout( + vulkan.device, + addr(layoutCreateInfo), + nil, + addr(result.descriptorSetLayouts[value.sType]) + ) let pipelineLayoutInfo = VkPipelineLayoutCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, setLayoutCount: result.descriptorSetLayouts.len.uint32, @@ -874,7 +940,7 @@ 1, addr(createInfo), nil, - addr(result.pipeline) + addr(result.vk) ) proc AllocateIndirectMemory(size: uint64): IndirectGPUMemory = @@ -1073,12 +1139,16 @@ when typeof(value) is GPUData: when UsesIndirectMemory(value): result += value.size + BUFFER_ALIGNMENT +proc GetIndirectBufferSizes(data: DescriptorSet): uint64 = + GetIndirectBufferSizes(data.data) proc GetDirectBufferSizes[T](data: T): uint64 = for name, value in fieldPairs(data): when not hasCustomPragma(value, VertexIndices): when typeof(value) is GPUData: when UsesDirectMemory(value): result += value.size + BUFFER_ALIGNMENT +proc GetDirectBufferSizes(data: DescriptorSet): uint64 = + GetDirectBufferSizes(data.data) proc GetIndirectIndexBufferSizes[T](data: T): uint64 = for name, value in fieldPairs(data): when hasCustomPragma(value, VertexIndices): @@ -1111,6 +1181,8 @@ foundBuffer = true break assert foundBuffer, &"Unable to find large enough '{btype}' for '{data}'" +proc AssignIndirectBuffers(renderdata: var RenderData, btype: BufferType, data: var DescriptorSet) = + AssignIndirectBuffers(renderdata, btype, data.data) proc AssignDirectBuffers[T](renderdata: var RenderData, btype: BufferType, data: var T) = for name, value in fieldPairs(data): when typeof(value) is GPUData: @@ -1128,6 +1200,8 @@ foundBuffer = true break assert foundBuffer, &"Unable to find large enough '{btype}' for '{data}'" +proc AssignDirectBuffers(renderdata: var RenderData, btype: BufferType, data: var DescriptorSet) = + AssignDirectBuffers(renderdata, btype, data.data) proc HasGPUValueField[T](name: static string): bool {.compileTime.} = for fieldname, value in default(T).fieldPairs(): @@ -1142,71 +1216,8 @@ let `fieldvalue` {.inject.} = value body -proc WriteDescriptors[TShader, TUniforms, TGlobals](renderData: RenderData, uniforms: TUniforms, globals: TGlobals) = - var descriptorSetWrites: seq[VkWriteDescriptorSet] - ForDescriptorFields(default(TShader), fieldName, descriptorType, descriptorCount, descriptorBindingNumber): - for frameInFlight in 0 ..< renderData.descriptorSets.len: - when descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER: - when HasGPUValueField[TUniforms](fieldName): - WithGPUValueField(uniforms, fieldName, gpuValue): - let bufferInfo = VkDescriptorBufferInfo( - buffer: gpuValue.buffer.vk, - offset: gpuValue.buffer.offset, - range: gpuValue.buffer.size, - ) - descriptorSetWrites.add VkWriteDescriptorSet( - sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - dstSet: renderData.descriptorSets[frameInFlight], - dstBinding: descriptorBindingNumber, - dstArrayElement: uint32(0), - descriptorType: descriptorType, - descriptorCount: descriptorCount, - pImageInfo: nil, - pBufferInfo: addr(bufferInfo), - ) - elif HasGPUValueField[TGlobals](fieldName): - WithGPUValueField(globals, fieldName, theValue): - let bufferInfo = VkDescriptorBufferInfo( - buffer: theValue.buffer.vk, - offset: theValue.buffer.offset, - range: theValue.buffer.size, - ) - descriptorSetWrites.add VkWriteDescriptorSet( - sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - dstSet: renderData.descriptorSets[frameInFlight], - dstBinding: descriptorBindingNumber, - dstArrayElement: uint32(0), - descriptorType: descriptorType, - descriptorCount: descriptorCount, - pImageInfo: nil, - pBufferInfo: addr(bufferInfo), - ) - else: - {.error: "Unable to find field '" & fieldName & "' in uniforms or globals".} - elif descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER: - # TODO - let imageInfo = VkDescriptorImageInfo( - sampler: VkSampler(0), - imageView: VkImageView(0), - imageLayout: VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - ) - descriptorSetWrites.add VkWriteDescriptorSet( - sType: VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - dstSet: renderData.descriptorSets[frameInFlight], - dstBinding: descriptorBindingNumber, - dstArrayElement: 0'u32, - descriptorType: descriptorType, - descriptorCount: descriptorCount, - pImageInfo: addr(imageInfo), - pBufferInfo: nil, - ) - else: - assert false, "Unsupported descriptor type" - echo descriptorSetWrites - vkUpdateDescriptorSets(vulkan.device, uint32(descriptorSetWrites.len), descriptorSetWrites.ToCPointer, 0, nil) - proc Bind[T](pipeline: Pipeline[T], commandBuffer: VkCommandBuffer, currentFrameInFlight: int) = - commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) + commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.vk) #[ commandBuffer.vkCmdBindDescriptorSets( VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -1219,125 +1230,57 @@ ) ]# -proc AssertCompatible(TShader, TMesh, TInstance, TUniforms, TGlobals: typedesc) = +proc AssertCompatible(TShader, TMesh, TInstance, TGlobals, TMaterial: typedesc) = var descriptorSetCount = 0 - for inputName, inputValue in default(TShader).fieldPairs: + for shaderAttributeName, shaderAttribute in default(TShader).fieldPairs: var foundField = false # Vertex input data - when hasCustomPragma(inputValue, VertexAttribute): - assert typeof(inputValue) is SupportedGPUType + when hasCustomPragma(shaderAttribute, VertexAttribute): + assert typeof(shaderAttribute) is SupportedGPUType for meshName, meshValue in default(TMesh).fieldPairs: - when meshName == inputName: + when meshName == shaderAttributeName: assert meshValue is GPUArray, "Mesh attribute '" & meshName & "' must be of type 'GPUArray' but is of type " & tt.name(typeof(meshValue)) - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert elementType(meshValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue.data)) & "'" + assert foundField == false, "Shader input '" & tt.name(TShader) & "." & shaderAttributeName & "' has been found more than once" + assert elementType(meshValue.data) is typeof(shaderAttribute), "Shader input " & tt.name(TShader) & "." & shaderAttributeName & " is of type '" & tt.name(typeof(shaderAttribute)) & "' but mesh attribute is of type '" & tt.name(elementType(meshValue.data)) & "'" foundField = true - assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "'" + assert foundField, "Shader input '" & tt.name(TShader) & "." & shaderAttributeName & ": " & tt.name(typeof(shaderAttribute)) & "' not found in '" & tt.name(TMesh) & "'" # Instance input data - elif hasCustomPragma(inputValue, InstanceAttribute): - assert typeof(inputValue) is SupportedGPUType + elif hasCustomPragma(shaderAttribute, InstanceAttribute): + assert typeof(shaderAttribute) is SupportedGPUType for instanceName, instanceValue in default(TInstance).fieldPairs: - when instanceName == inputName: + when instanceName == shaderAttributeName: assert instanceValue is GPUArray, "Instance attribute '" & instanceName & "' must be of type 'GPUArray' but is of type " & tt.name(typeof(instanceName)) - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert elementType(instanceValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but instance attribute is of type '" & tt.name(elementType(instanceValue.data)) & "'" + assert foundField == false, "Shader input '" & tt.name(TShader) & "." & shaderAttributeName & "' has been found more than once" + assert elementType(instanceValue.data) is typeof(shaderAttribute), "Shader input " & tt.name(TShader) & "." & shaderAttributeName & " is of type '" & tt.name(typeof(shaderAttribute)) & "' but instance attribute is of type '" & tt.name(elementType(instanceValue.data)) & "'" foundField = true - assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TInstance) & "'" + assert foundField, "Shader input '" & tt.name(TShader) & "." & shaderAttributeName & ": " & tt.name(typeof(shaderAttribute)) & "' not found in '" & tt.name(TInstance) & "'" + + # descriptors + elif typeof(shaderAttribute) is DescriptorSet: + assert descriptorSetCount <= DescriptorSetType.high.int, &"{tt.name(TShader)}: maximum {DescriptorSetType.high} allowed" + descriptorSetCount.inc - elif hasCustomPragma(inputValue, DescriptorSet): - assert descriptorSetCount < MAX_DESCRIPTORSETS, &"{tt.name(TShader)}: maximum {MAX_DESCRIPTORSETS} allowed" - descriptorSetCount.inc - echo "DescriptorSet: ", inputName - - for descriptorName, descriptorValue in inputValue.fieldPairs(): - when typeof(descriptorValue) is Texture: - echo " Texture: ", descriptorName - elif typeof(descriptorValue) is GPUValue: - echo " Uniform block: ", descriptorName - - #[ - # Texture - elif typeof(inputValue) is Texture: - for uniformName, uniformValue in default(TUniforms).fieldPairs: - when uniformName == inputName: - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert typeof(uniformValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but uniform attribute is of type '" & tt.name(typeof(uniformValue)) & "'" - foundField = true - for globalName, globalValue in default(TGlobals).fieldPairs: - when globalName == inputName: - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert typeof(globalValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but global attribute is of type '" & tt.name(typeof(globalValue)) & "'" - foundField = true - assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" - - # Uniform block - elif typeof(inputValue) is object: - for uniformName, uniformValue in default(TUniforms).fieldPairs: - when uniformName == inputName: - assert uniformValue is GPUValue, "global attribute '" & uniformName & "' must be of type 'GPUValue' but is of type " & tt.name(typeof(uniformValue)) - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert typeof(uniformValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but uniform attribute is of type '" & tt.name(typeof(uniformValue.data)) & "'" - foundField = true - for globalName, globalValue in default(TGlobals).fieldPairs: - when globalName == inputName: - assert globalValue is GPUValue, "global attribute '" & globalName & "' must be of type 'GPUValue' but is of type " & tt.name(typeof(globalValue)) - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert typeof(globalValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but global attribute is of type '" & tt.name(typeof(globalValue.data)) & "'" - foundField = true - assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" - - # array - elif typeof(inputValue) is array: - - # texture-array - when elementType(inputValue) is Texture: - for uniformName, uniformValue in default(TUniforms).fieldPairs: - when uniformName == inputName: - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert typeof(uniformValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but uniform attribute is of type '" & tt.name(typeof(uniformValue)) & "'" - foundField = true - for globalName, globalValue in default(TGlobals).fieldPairs: - when globalName == inputName: - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert typeof(globalValue) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but global attribute is of type '" & tt.name(typeof(globalValue)) & "'" - foundField = true - assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" - - # uniform-block array - elif elementType(inputValue) is object: - for uniformName, uniformValue in default(TUniforms).fieldPairs: - when uniformName == inputName: - assert uniformValue is GPUValue, "global attribute '" & uniformName & "' must be of type 'GPUValue' but is of type " & tt.name(typeof(uniformValue)) - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert typeof(uniformValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but uniform attribute is of type '" & tt.name(typeof(uniformValue.data)) & "'" - foundField = true - for globalName, globalValue in default(TGlobals).fieldPairs: - when globalName == inputName: - assert globalValue is GPUValue, "global attribute '" & globalName & "' must be of type 'GPUValue' but is of type " & tt.name(typeof(globalValue)) - assert foundField == false, "Shader input '" & tt.name(TShader) & "." & inputName & "' has been found more than once" - assert typeof(globalValue.data) is typeof(inputValue), "Shader input " & tt.name(TShader) & "." & inputName & " is of type '" & tt.name(typeof(inputValue)) & "' but global attribute is of type '" & tt.name(typeof(globalValue.data)) & "'" - foundField = true - assert foundField, "Shader input '" & tt.name(TShader) & "." & inputName & ": " & tt.name(typeof(inputValue)) & "' not found in '" & tt.name(TMesh) & "|" & tt.name(TGlobals) & "'" - ]# + when shaderAttribute.sType == GlobalSet: + assert shaderAttribute.sType == default(TGlobals).sType, "Shader has global descriptor set of type '" & $shaderAttribute.sType & "' but matching provided type is '" & $default(TGlobals).sType & "'" + assert typeof(shaderAttribute) is TGlobals, "Shader has global descriptor set type '" & tt.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & tt.name(TGlobals) + elif shaderAttribute.sType == MaterialSet: + assert shaderAttribute.sType == default(TMaterial).sType, "Shader has material descriptor set of type '" & $shaderAttribute.sType & "' but matching provided type is '" & $default(TMaterial).sType & "'" + assert typeof(shaderAttribute) is TMaterial, "Shader has materialdescriptor type '" & tt.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & tt.name(TMaterial) - - - - -proc Render[TShader, TUniforms, TGlobals, TMesh, TInstance]( +proc Render[TShader, TGlobals, TMaterial, TMesh, TInstance]( commandBuffer: VkCommandBuffer, pipeline: Pipeline[TShader], - uniforms: TUniforms, - globals: TGlobals, + globalSet: TGlobals, + materialSet: TMaterial, mesh: TMesh, instances: TInstance, ) = - static: AssertCompatible(TShader, TMesh, TInstance, TUniforms, TGlobals) + static: AssertCompatible(TShader, TMesh, TInstance, TGlobals, TMaterial) #[ if renderable.vertexBuffers.len > 0: commandBuffer.vkCmdBindVertexBuffers( @@ -1407,8 +1350,8 @@ # output color {.ShaderOutput.}: Vec4f # descriptor sets - globals {.DescriptorSet.}: GlobalsA - uniforms {.DescriptorSet.}: UniformsA + globals: DescriptorSet[GlobalsA, GlobalSet] + uniforms: DescriptorSet[UniformsA, MaterialSet] # code vertexCode: string = "void main() {}" fragmentCode: string = "void main() {}" @@ -1445,11 +1388,12 @@ var myMesh1 = MeshA( position: GPUArray[Vec3f, IndirectGPUMemory](data: @[NewVec3f(0, 0, ), NewVec3f(0, 0, ), NewVec3f(0, 0, )]), ) - var uniforms1 = UniformsA( - materials: GPUValue[array[3, MaterialA], IndirectGPUMemory](data: [ - MaterialA(reflection: 0, baseColor: NewVec3f(1, 0, 0)), - MaterialA(reflection: 0.1, baseColor: NewVec3f(0, 1, 0)), - MaterialA(reflection: 0.5, baseColor: NewVec3f(0, 0, 1)), + var uniforms1 = DescriptorSet[UniformsA, MaterialSet]( + data: UniformsA( + materials: GPUValue[array[3, MaterialA], IndirectGPUMemory](data: [ + MaterialA(reflection: 0, baseColor: NewVec3f(1, 0, 0)), + MaterialA(reflection: 0.1, baseColor: NewVec3f(0, 1, 0)), + MaterialA(reflection: 0.5, baseColor: NewVec3f(0, 0, 1)), ]), materialTextures: [ Texture(isGrayscale: false, colorImage: Image[RGBAPixel](width: 1, height: 1, imagedata: @[[255'u8, 0'u8, 0'u8, 255'u8]])), @@ -1457,11 +1401,12 @@ Texture(isGrayscale: false, colorImage: Image[RGBAPixel](width: 1, height: 1, imagedata: @[[0'u8, 0'u8, 255'u8, 255'u8]])), ] ) + ) var instances1 = InstanceA( rotation: GPUArray[Vec4f, IndirectGPUMemory](data: @[NewVec4f(1, 0, 0, 0.1), NewVec4f(0, 1, 0, 0.1)]), objPosition: GPUArray[Vec3f, IndirectGPUMemory](data: @[NewVec3f(0, 0, 0), NewVec3f(1, 1, 1)]), ) - var myGlobals: GlobalsA + var myGlobals = DescriptorSet[GlobalsA, GlobalSet]() # setup for rendering (TODO: swapchain & framebuffers) let renderpass = CreateRenderPass(GetSurfaceFormat()) @@ -1545,12 +1490,11 @@ # descriptors # TODO: I think we can write and assign descriptors directly after creation - var s1 = CreateDescriptorSet(renderdata, pipeline1, myGlobals, GlobalSet) - var s2 = CreateDescriptorSet(renderdata, pipeline1, uniforms1, MaterialSet) + InitDescriptorSet(renderdata, pipeline1.descriptorSetLayouts[GlobalSet], myGlobals) + InitDescriptorSet(renderdata, pipeline1.descriptorSetLayouts[MaterialSet], uniforms1) # WriteDescriptors[ShaderA, UniformsA, GlobalsA](renderdata, uniforms1, myGlobals) - # command buffer var commandBufferPool: VkCommandPool @@ -1622,7 +1566,7 @@ # render object, will be loop block: - Render(cmd, pipeline1, uniforms1, myGlobals, myMesh1, instances1) + Render(cmd, pipeline1, myGlobals, uniforms1, myMesh1, instances1) vkCmdEndRenderPass(cmd) checkVkResult cmd.vkEndCommandBuffer()