# HG changeset patch # User sam # Date 1718637719 -25200 # Node ID e7cbb13999e4859f2d7a2932a13d3083379bcf0d # Parent f32359ffd8828ea3a88bc5c47c7a703e5b58a0a4 add: changes and static tools diff -r f32359ffd882 -r e7cbb13999e4 semicongine/mesh.nim --- a/semicongine/mesh.nim Sat Jun 15 21:02:21 2024 +0700 +++ b/semicongine/mesh.nim Mon Jun 17 22:21:59 2024 +0700 @@ -18,7 +18,6 @@ type MeshIndexType* = enum None - Tiny # up to 2^8 vertices # TODO: need to check and enable support for this Small # up to 2^16 vertices Big # up to 2^32 vertices MeshObject* = object @@ -26,7 +25,6 @@ vertexCount*: int case indexType*: MeshIndexType of None: discard - of Tiny: tinyIndices*: seq[array[3, uint8]] of Small: smallIndices*: seq[array[3, uint16]] of Big: bigIndices*: seq[array[3, uint32]] material*: MaterialData @@ -65,7 +63,6 @@ ( case mesh.indexType of None: 0 - of Tiny: mesh.tinyIndices.len of Small: mesh.smallIndices.len of Big: mesh.bigIndices.len ) * 3 @@ -93,7 +90,6 @@ converter ToVulkan*(indexType: MeshIndexType): VkIndexType = case indexType: of None: VK_INDEX_TYPE_NONE_KHR - of Tiny: VK_INDEX_TYPE_UINT8_EXT of Small: VK_INDEX_TYPE_UINT16 of Big: VK_INDEX_TYPE_UINT32 @@ -154,9 +150,7 @@ var indexType = None if indices.len > 0: indexType = Big - if autoResize and uint32(positions.len) < uint32(high(uint8)) and false: # TODO: check feature support - indexType = Tiny - elif autoResize and uint32(positions.len) < uint32(high(uint16)): + if autoResize and uint32(positions.len) < uint32(high(uint16)): indexType = Small result = Mesh( @@ -178,10 +172,7 @@ assert int(i[2]) < result[].vertexCount # cast index values to appropiate type - if result[].indexType == Tiny and uint32(positions.len) < uint32(high(uint8)) and false: # TODO: check feature support - for i, tri in enumerate(indices): - result[].tinyIndices.add [uint8(tri[0]), uint8(tri[1]), uint8(tri[2])] - elif result[].indexType == Small and uint32(positions.len) < uint32(high(uint16)): + if result[].indexType == Small and uint32(positions.len) < uint32(high(uint16)): for i, tri in enumerate(indices): result[].smallIndices.add [uint16(tri[0]), uint16(tri[1]), uint16(tri[2])] elif result[].indexType == Big: @@ -228,7 +219,6 @@ func IndexSize*(mesh: MeshObject): uint64 = case mesh.indexType of None: 0'u64 - of Tiny: uint64(mesh.tinyIndices.len * sizeof(get(genericParams(typeof(mesh.tinyIndices)), 0))) of Small: uint64(mesh.smallIndices.len * sizeof(get(genericParams(typeof(mesh.smallIndices)), 0))) of Big: uint64(mesh.bigIndices.len * sizeof(get(genericParams(typeof(mesh.bigIndices)), 0))) @@ -241,7 +231,6 @@ func GetRawIndexData*(mesh: MeshObject): (pointer, uint64) = case mesh.indexType: of None: raise newException(Exception, "Trying to get index data for non-indexed mesh") - of Tiny: rawData(mesh.tinyIndices) of Small: rawData(mesh.smallIndices) of Big: rawData(mesh.bigIndices) @@ -352,7 +341,6 @@ proc AppendIndicesData*(mesh: var MeshObject, v1, v2, v3: int) = case mesh.indexType of None: raise newException(Exception, "Mesh does not support indexed data") - of Tiny: mesh.tinyIndices.add([uint8(v1), uint8(v2), uint8(v3)]) of Small: mesh.smallIndices.add([uint16(v1), uint16(v2), uint16(v3)]) of Big: mesh.bigIndices.add([uint32(v1), uint32(v2), uint32(v3)]) @@ -424,13 +412,6 @@ result.instanceData[attribute] = datalist.Copy() var i = 0 case mesh.indexType - of Tiny: - for indices in mesh.tinyIndices: - for attribute, value in mesh.vertexData.pairs: - result.vertexData[attribute].AppendFrom(i, mesh.vertexData[attribute], int(indices[0])) - result.vertexData[attribute].AppendFrom(i + 1, mesh.vertexData[attribute], int(indices[1])) - result.vertexData[attribute].AppendFrom(i + 2, mesh.vertexData[attribute], int(indices[2])) - i += 3 of Small: for indices in mesh.smallIndices: for attribute, value in mesh.vertexData.pairs: @@ -586,10 +567,6 @@ case a.indexType: of None: discard - of Tiny: - let offset = uint8(originalOffset) - for i in b.tinyIndices: - a.tinyIndices.add [i[0] + offset, i[1] + offset, i[2] + offset] of Small: let offset = uint16(originalOffset) for i in b.smallIndices: diff -r f32359ffd882 -r e7cbb13999e4 semicongine/renderer.nim --- a/semicongine/renderer.nim Sat Jun 15 21:02:21 2024 +0700 +++ b/semicongine/renderer.nim Mon Jun 17 22:21:59 2024 +0700 @@ -125,7 +125,6 @@ if mesh[].indexType != MeshIndexType.None: let indexAlignment = case mesh[].indexType of MeshIndexType.None: 0'u64 - of Tiny: 1'u64 of Small: 2'u64 of Big: 4'u64 # index value alignment required by Vulkan @@ -205,7 +204,6 @@ if indexed: let indexAlignment = case mesh.indexType of MeshIndexType.None: 0'u64 - of Tiny: 1'u64 of Small: 2'u64 of Big: 4'u64 # index value alignment required by Vulkan diff -r f32359ffd882 -r e7cbb13999e4 static_utils.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/static_utils.nim Mon Jun 17 22:21:59 2024 +0700 @@ -0,0 +1,311 @@ +import std/macros +import std/typetraits + +import semicongine/core/vector +import semicongine/core/matrix +import semicongine/core/vulkanapi + +template VertexAttribute* {.pragma.} +template InstanceAttribute* {.pragma.} + +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] + +func VkType[T: SupportedGPUType](value: T): VkFormat = + when T is float32: VK_FORMAT_R32_SFLOAT + elif T is float64: VK_FORMAT_R64_SFLOAT + elif T is int8: VK_FORMAT_R8_SINT + elif T is int16: VK_FORMAT_R16_SINT + elif T is int32: VK_FORMAT_R32_SINT + elif T is int64: VK_FORMAT_R64_SINT + elif T is uint8: VK_FORMAT_R8_UINT + elif T is uint16: VK_FORMAT_R16_UINT + elif T is uint32: VK_FORMAT_R32_UINT + elif T is uint64: VK_FORMAT_R64_UINT + elif T is TVec2[int32]: VK_FORMAT_R32G32_SINT + elif T is TVec2[int64]: VK_FORMAT_R64G64_SINT + elif T is TVec3[int32]: VK_FORMAT_R32G32B32_SINT + elif T is TVec3[int64]: VK_FORMAT_R64G64B64_SINT + elif T is TVec4[int32]: VK_FORMAT_R32G32B32A32_SINT + elif T is TVec4[int64]: VK_FORMAT_R64G64B64A64_SINT + elif T is TVec2[uint32]: VK_FORMAT_R32G32_UINT + elif T is TVec2[uint64]: VK_FORMAT_R64G64_UINT + elif T is TVec3[uint32]: VK_FORMAT_R32G32B32_UINT + elif T is TVec3[uint64]: VK_FORMAT_R64G64B64_UINT + elif T is TVec4[uint32]: VK_FORMAT_R32G32B32A32_UINT + elif T is TVec4[uint64]: VK_FORMAT_R64G64B64A64_UINT + elif T is TVec2[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is TVec2[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is TVec3[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TVec3[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TVec4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is TVec4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + elif T is Mat2[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is Mat2[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is Mat23[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is Mat23[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is Mat32[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is Mat32[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is Mat3[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is Mat3[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is Mat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is Mat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + elif T is Mat43[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is Mat43[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is Mat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is Mat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + else: {.error: "Unsupported data type on GPU".} + +template getElementType(field: typed): untyped = + when not (typeof(field) is seq or typeof(field) is array): + {.error: "getElementType can only be used with seq or array".} + genericParams(typeof(field)).get(0) + +proc isVertexAttribute[T](value: T): bool {.compileTime.} = + hasCustomPragma(T, VertexAttribute) + +proc isInstanceAttribute[T](value: T): bool {.compileTime.} = + hasCustomPragma(T, InstanceAttribute) + +template ForAttributeFields*(inputData: typed, fieldname, valuename, isinstancename, body: untyped): untyped = + for theFieldname, value in fieldPairs(inputData): + when isVertexAttribute(value) or isInstanceAttribute(value): + when not typeof(value) is seq: + {.error: "field '" & theFieldname & "' needs to be a seq".} + when not typeof(value) is SupportedGPUType: + {.error: "field '" & theFieldname & "' is not a supported GPU type".} + block: + let `fieldname` {.inject.} = theFieldname + let `valuename` {.inject.} = default(getElementType(value)) + let `isinstancename` {.inject.} = value.isInstanceAttribute() + body + +func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType](value: T): uint32 = + when T is TMat2[float32] or T is TMat2[float64] or T is TMat23[float32] or T is TMat23[float64]: + 2 + elif T is TMat32[float32] or T is TMat32[float64] or T is TMat3[float32] or T is TMat3[float64] or T is TMat34[float32] or T is TMat34[float64]: + 3 + elif T is TMat43[float32] or T is TMat43[float64] or T is TMat4[float32] or T is TMat4[float64]: + 4 + else: + 1 + +func NLocationSlots[T: SupportedGPUType](value: T): uint32 = + #[ + single location: + 16-bit scalar and vector types, and + 32-bit scalar and vector types, and + 64-bit scalar and 2-component vector types. + two locations + 64-bit three- and four-component vectors + ]# + when typeof(value) is TVec3 and sizeof(getElementType(value)) == 8: + return 2 + elif typeof(value) is TVec4 and sizeof(getElementType(value)) == 8: + return 2 + else: + return 1 + + +type + Renderable[IndexType: static VkIndexType] = object + buffers: seq[VkBuffer] + offsets: seq[VkDeviceSize] + instanceCount: uint32 + when IndexType == VK_INDEX_TYPE_NONE_KHR: + vertexCount: uint32 + else: + indexBuffer: VkBuffer + indexCount: uint32 + indexBufferOffset: VkDeviceSize + Pipeline = object + pipeline: VkPipeline + layout: VkPipelineLayout + descriptorSets: array[2, seq[VkDescriptorSet]] + ShaderSet[ShaderInputType, ShaderDescriptorType] = object + vertexShader: VkShaderModule + fragmentShader: VkShaderModule + # TODO: I think this needs more fields? + +proc CreatePipeline*[ShaderInputType, ShaderDescriptorType]( + device: VkDevice, + renderPass: VkRenderPass, + shaderSet: ShaderSet[ShaderInputType, ShaderDescriptorType], + topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, + polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, + cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, + frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, +): Pipeline = + # assumptions/limitations: + # - we are only using vertex and fragment shaders (2 stages) + # - we only support one subpass + + # CONTINUE HERE, WITH PIPELINE LAYOUT!!!! + # Rely on ShaderDescriptorType + checkVkResult vkCreatePipelineLayout(device.vk, addr(pipelineLayoutInfo), nil, addr(result.layout)) + + let stages = [ + VkPipelineShaderStageCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: VK_SHADER_STAGE_VERTEX_BIT, + module: shaderSet.vertexShader, + pName: "main", + ), + VkPipelineShaderStageCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + stage: VK_SHADER_STAGE_FRAGMENT_BIT, + module: shaderSet.fragmentShader, + pName: "main", + ), + ] + let + bindings: var seq[VkVertexInputBindingDescription] + attributes: var seq[VkVertexInputAttributeDescription] + var inputBindingNumber = 0'u32 + var inputLocationNumber = 0'u32 + ForAttributeFields(default(ShaderInputType), fieldname, value, isInstanceAttr): + bindings.add VkVertexInputBindingDescription( + binding: inputBindingNumber, + stride: sizeof(value).uint32, + inputRate: if isInstanceAttr: VK_VERTEX_INPUT_RATE_INSTANCE else: VK_VERTEX_INPUT_RATE_VERTEX, + ) + # allows to submit larger data structures like Mat44, for most other types will be 1 + let perDescriptorSize = sizeof(value).uint32 div NumberOfVertexInputAttributeDescriptors(value) + for i in 0'u32 ..< NumberOfVertexInputAttributeDescriptors(value): + attributes.add VkVertexInputAttributeDescription( + binding: inputBindingNumber, + inputLocationNumber: inputLocationNumber, + format: VkType(value), + offset: i * perDescriptorSize, + ) + inputLocationNumber += NLocationSlots(value) + inc inputBindingNumber + + let + vertexInputInfo = VkPipelineVertexInputStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, + vertexBindingDescriptionCount: uint32(bindings.len), + pVertexBindingDescriptions: bindings.ToCPointer, + vertexAttributeDescriptionCount: uint32(attributes.len), + pVertexAttributeDescriptions: attributes.ToCPointer, + ) + inputAssembly = VkPipelineInputAssemblyStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, + topology: topology, + primitiveRestartEnable: false, + ) + viewportState = VkPipelineViewportStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + viewportCount: 1, + scissorCount: 1, + ) + rasterizer = VkPipelineRasterizationStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, + depthClampEnable: VK_FALSE, + rasterizerDiscardEnable: VK_FALSE, + polygonMode: polygonMode, + lineWidth: 1.0, + cullMode: toBits [cullMode], + frontFace: frontFace, + depthBiasEnable: VK_FALSE, + depthBiasConstantFactor: 0.0, + depthBiasClamp: 0.0, + depthBiasSlopeFactor: 0.0, + ) + multisampling = VkPipelineMultisampleStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, + sampleShadingEnable: VK_FALSE, + rasterizationSamples: VK_SAMPLE_COUNT_1_BIT, + minSampleShading: 1.0, + pSampleMask: nil, + alphaToCoverageEnable: VK_FALSE, + alphaToOneEnable: VK_FALSE, + ) + colorBlendAttachment = VkPipelineColorBlendAttachmentState( + colorWriteMask: toBits [VK_COLOR_COMPONENT_R_BIT, VK_COLOR_COMPONENT_G_BIT, VK_COLOR_COMPONENT_B_BIT, VK_COLOR_COMPONENT_A_BIT], + blendEnable: VK_TRUE, + srcColorBlendFactor: VK_BLEND_FACTOR_SRC_ALPHA, + dstColorBlendFactor: VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + colorBlendOp: VK_BLEND_OP_ADD, + srcAlphaBlendFactor: VK_BLEND_FACTOR_ONE, + dstAlphaBlendFactor: VK_BLEND_FACTOR_ZERO, + alphaBlendOp: VK_BLEND_OP_ADD, + ) + colorBlending = VkPipelineColorBlendStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + logicOpEnable: false, + attachmentCount: 1, + pAttachments: addr(colorBlendAttachment), + ) + dynamicStates = [VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR] + dynamicState = VkPipelineDynamicStateCreateInfo( + sType: VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + dynamicStateCount: dynamicStates.len.uint32, + pDynamicStates: dynamicStates.ToCPointer, + ) + let createInfo = VkGraphicsPipelineCreateInfo( + sType: VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + stageCount: 2, + pStages: addr(stages), + pVertexInputState: addr(vertexInputInfo), + pInputAssemblyState: addr(inputAssembly), + pViewportState: addr(viewportState), + pRasterizationState: addr(rasterizer), + pMultisampleState: addr(multisampling), + pDepthStencilState: nil, + pColorBlendState: addr(colorBlending), + pDynamicState: addr(dynamicState), + layout: result.layout, + renderPass: renderPass, + subpass: 0, + basePipelineHandle: VkPipeline(0), + basePipelineIndex: -1, + ) + checkVkResult vkCreateGraphicsPipelines( + device, + VkPipelineCache(0), + 1, + addr(createInfo), + nil, + addr(result.pipeline) + ) + + +proc Render*[IndexType: static VkIndexType](renderable: Renderable[IndexType], commandBuffer: VkCommandBuffer, pipeline: Pipeline, frameInFlight: int) = + assert 0 <= frameInFlight and frameInFlight < 2 + commandBuffer.vkCmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline.pipeline) + commandBuffer.vkCmdBindDescriptorSets( + VK_PIPELINE_BIND_POINT_GRAPHICS, + pipeline.layout, + 0, + pipeline.descriptorSets[frameInFlight].len, + pipeline.descriptorSets[frameInFlight], + 0, + nil, + ) + commandBuffer.vkCmdBindVertexBuffers( + firstBinding = 0'u32, + bindingCount = uint32(renderable.buffers.len), + pBuffers = renderable.buffers.ToCPointer(), + pOffsets = renderable.offsets.ToCPointer() + ) + when IndexType != VK_INDEX_TYPE_NONE_KHR: + commandBuffer.vkCmdBindIndexBuffer( + renderable.indexBuffer, + renderable.indexBufferOffset, + IndexType, + ) + commandBuffer.vkCmdDrawIndexed( + indexCount = drawable.indexCount, + instanceCount = drawable.instanceCount, + firstIndex = 0, + vertexOffset = 0, + firstInstance = 0 + ) + else: + commandBuffer.vkCmdDraw( + vertexCount = drawable.vertexCount, + instanceCount = drawable.instanceCount, + firstVertex = 0, + firstInstance = 0 + ) diff -r f32359ffd882 -r e7cbb13999e4 test2.nim --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test2.nim Mon Jun 17 22:21:59 2024 +0700 @@ -0,0 +1,10 @@ +type + ShaderInputA = object + positions {.VertexAttribute.}: seq[Vec3f] + colors {.VertexAttribute.}: seq[Vec3f] + transforms {.InstanceAttribute.}: seq[Vec3f] + other: bool + Enemy = object + shaderData: ShaderInputA + +proc initEnemy()