# HG changeset patch # User sam # Date 1720429506 -25200 # Node ID 90bf0cab1d02217e7a1b044996f2a291af371b33 # Parent 239adab121a33ba1f82253a0e75114015d0c06c1 did: so many, many things diff -r 239adab121a3 -r 90bf0cab1d02 semicongine/platform/linux/window.nim --- a/semicongine/platform/linux/window.nim Mon Jul 08 01:16:53 2024 +0700 +++ b/semicongine/platform/linux/window.nim Mon Jul 08 16:05:06 2024 +0700 @@ -21,7 +21,7 @@ " returned " & $value) proc XErrorLogger(display: PDisplay, event: PXErrorEvent): cint {.cdecl.} = - error &"Xlib: {event[]}" + logging.error &"Xlib: {event[]}" proc CreateWindow*(title: string): NativeWindow = checkXlibResult XInitThreads() diff -r 239adab121a3 -r 90bf0cab1d02 semicongine/platform/windows/surface.nim --- a/semicongine/platform/windows/surface.nim Mon Jul 08 01:16:53 2024 +0700 +++ b/semicongine/platform/windows/surface.nim Mon Jul 08 16:05:06 2024 +0700 @@ -1,6 +1,3 @@ -import ../../core -import ../../platform/window - proc CreateNativeSurface*(instance: VkInstance, window: NativeWindow): VkSurfaceKHR = assert instance.Valid var surfaceCreateInfo = VkWin32SurfaceCreateInfoKHR( diff -r 239adab121a3 -r 90bf0cab1d02 semicongine/rendering.nim --- a/semicongine/rendering.nim Mon Jul 08 01:16:53 2024 +0700 +++ b/semicongine/rendering.nim Mon Jul 08 16:05:06 2024 +0700 @@ -1,3 +1,12 @@ +# in this file: +# - const defintions for rendering +# - custom pragma defintions for rendering +# - type defintions for rendering +# - some utils code that is used in mutiple rendering files +# - inclusion of all rendering files + + +# const definitions const INFLIGHTFRAMES = 2'u32 const BUFFER_ALIGNMENT = 64'u64 # align offsets inside buffers along this alignment const MEMORY_BLOCK_ALLOCATION_SIZE = 100_000_000'u64 # ca. 100mb per block, seems reasonable @@ -23,7 +32,7 @@ GlobalSet MaterialSet DescriptorSet*[T: object, sType: static DescriptorSetType] = object - data: T + data*: T vk: array[INFLIGHTFRAMES.int, VkDescriptorSet] Pipeline*[TShader] = object vk: VkPipeline @@ -52,15 +61,15 @@ vk: VkImage imageview: VkImageView sampler: VkSampler - width: uint32 - height: uint32 - data: seq[T] + width*: uint32 + height*: uint32 + data*: seq[T] GPUArray*[T: SupportedGPUType, TBuffer: static BufferType] = object - data: seq[T] + data*: seq[T] buffer: Buffer offset: uint64 GPUValue*[T: object|array, TBuffer: static BufferType] = object - data: T + data*: T buffer: Buffer offset: uint64 GPUData = GPUArray | GPUValue @@ -70,6 +79,45 @@ memory: array[VK_MAX_MEMORY_TYPES.int, seq[MemoryBlock]] buffers: array[BufferType, seq[Buffer]] +template ForDescriptorFields(shader: typed, fieldname, valuename, typename, countname, bindingNumber, body: untyped): untyped = + var `bindingNumber` {.inject.} = 1'u32 + for theFieldname, value in fieldPairs(shader): + when typeof(value) is Texture: + block: + const `fieldname` {.inject.} = theFieldname + const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + const `countname` {.inject.} = 1'u32 + let `valuename` {.inject.} = value + body + `bindingNumber`.inc + elif typeof(value) is object: + block: + const `fieldname` {.inject.} = theFieldname + const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER + const `countname` {.inject.} = 1'u32 + let `valuename` {.inject.} = value + body + `bindingNumber`.inc + elif typeof(value) is array: + when elementType(value) is Texture: + block: + const `fieldname` {.inject.} = theFieldname + const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER + const `countname` {.inject.} = uint32(typeof(value).len) + let `valuename` {.inject.} = value + body + `bindingNumber`.inc + elif elementType(value) is object: + block: + const `fieldname` {.inject.} = theFieldname + const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER + const `countname` {.inject.} = uint32(typeof(value).len) + let `valuename` {.inject.} = value + body + `bindingNumber`.inc + else: + {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).} + include ./rendering/vulkan_wrappers include ./rendering/shaders include ./rendering/renderer diff -r 239adab121a3 -r 90bf0cab1d02 semicongine/rendering/renderer.nim --- a/semicongine/rendering/renderer.nim Mon Jul 08 01:16:53 2024 +0700 +++ b/semicongine/rendering/renderer.nim Mon Jul 08 16:05:06 2024 +0700 @@ -56,168 +56,6 @@ else: return value + alignment - remainder -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 TMat2[float32]: VK_FORMAT_R32G32_SFLOAT - elif T is TMat2[float64]: VK_FORMAT_R64G64_SFLOAT - elif T is TMat23[float32]: VK_FORMAT_R32G32B32_SFLOAT - elif T is TMat23[float64]: VK_FORMAT_R64G64B64_SFLOAT - elif T is TMat32[float32]: VK_FORMAT_R32G32_SFLOAT - elif T is TMat32[float64]: VK_FORMAT_R64G64_SFLOAT - elif T is TMat3[float32]: VK_FORMAT_R32G32B32_SFLOAT - elif T is TMat3[float64]: VK_FORMAT_R64G64B64_SFLOAT - elif T is TMat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT - elif T is TMat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT - elif T is TMat43[float32]: VK_FORMAT_R32G32B32_SFLOAT - elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT - elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT - elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT - else: {.error: "Unsupported data type on GPU".} - -func GlslType[T: SupportedGPUType|Texture](value: T): string = - when T is float32: "float" - elif T is float64: "double" - elif T is int8 or T is int16 or T is int32 or T is int64: "int" - elif T is uint8 or T is uint16 or T is uint32 or T is uint64: "uint" - elif T is TVec2[int32]: "ivec2" - elif T is TVec2[int64]: "ivec2" - elif T is TVec3[int32]: "ivec3" - elif T is TVec3[int64]: "ivec3" - elif T is TVec4[int32]: "ivec4" - elif T is TVec4[int64]: "ivec4" - elif T is TVec2[uint32]: "uvec2" - elif T is TVec2[uint64]: "uvec2" - elif T is TVec3[uint32]: "uvec3" - elif T is TVec3[uint64]: "uvec3" - elif T is TVec4[uint32]: "uvec4" - elif T is TVec4[uint64]: "uvec4" - elif T is TVec2[float32]: "vec2" - elif T is TVec2[float64]: "dvec2" - elif T is TVec3[float32]: "vec3" - elif T is TVec3[float64]: "dvec3" - elif T is TVec4[float32]: "vec4" - elif T is TVec4[float64]: "dvec4" - elif T is TMat2[float32]: "mat2" - elif T is TMat2[float64]: "dmat2" - elif T is TMat23[float32]: "mat23" - elif T is TMat23[float64]: "dmat23" - elif T is TMat32[float32]: "mat32" - elif T is TMat32[float64]: "dmat32" - elif T is TMat3[float32]: "mat3" - elif T is TMat3[float64]: "dmat3" - elif T is TMat34[float32]: "mat34" - elif T is TMat34[float64]: "dmat34" - elif T is TMat43[float32]: "mat43" - elif T is TMat43[float64]: "dmat43" - elif T is TMat4[float32]: "mat4" - elif T is TMat4[float64]: "dmat4" - elif T is Texture: "sampler2D" - else: {.error: "Unsupported data type on GPU".} - -template ForDescriptorFields(shader: typed, fieldname, valuename, typename, countname, bindingNumber, body: untyped): untyped = - var `bindingNumber` {.inject.} = 1'u32 - for theFieldname, value in fieldPairs(shader): - when typeof(value) is Texture: - block: - const `fieldname` {.inject.} = theFieldname - const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER - const `countname` {.inject.} = 1'u32 - let `valuename` {.inject.} = value - body - `bindingNumber`.inc - elif typeof(value) is object: - block: - const `fieldname` {.inject.} = theFieldname - const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER - const `countname` {.inject.} = 1'u32 - let `valuename` {.inject.} = value - body - `bindingNumber`.inc - elif typeof(value) is array: - when elementType(value) is Texture: - block: - const `fieldname` {.inject.} = theFieldname - const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER - const `countname` {.inject.} = uint32(typeof(value).len) - let `valuename` {.inject.} = value - body - `bindingNumber`.inc - elif elementType(value) is object: - block: - const `fieldname` {.inject.} = theFieldname - const `typename` {.inject.} = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER - const `countname` {.inject.} = uint32(typeof(value).len) - let `valuename` {.inject.} = value - body - `bindingNumber`.inc - else: - {.error: "Unsupported descriptor type: " & typetraits.name(typeof(value)).} - -func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType|Texture](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|Texture](value: T): uint32 = - #[ - single location: - - any scalar - - any 16-bit vector - - any 32-bit vector - - any 64-bit vector that has max. 2 components - 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 T is TVec3[int64] or - T is TVec4[int64] or - T is TVec3[uint64] or - T is TVec4[uint64] or - T is TVec3[float64] or - T is TVec4[float64] or - T is TMat23[float64] or - T is TMat3[float64] or - T is TMat34[float64] or - T is TMat43[float64] or - T is TMat4[float64]: - return 2 - else: - return 1 - template sType(descriptorSet: DescriptorSet): untyped = get(genericParams(typeof(descriptorSet)), 1) @@ -245,53 +83,20 @@ template rawPointer(gpuValue: GPUValue): pointer = addr(gpuValue.data) -proc GetPhysicalDevice(instance: VkInstance): VkPhysicalDevice = - var nDevices: uint32 - checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), nil) - var devices = newSeq[VkPhysicalDevice](nDevices) - checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), devices.ToCPointer) - - var score = 0'u32 - for pDevice in devices: - var props: VkPhysicalDeviceProperties - # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet) - vkGetPhysicalDeviceProperties(pDevice, addr(props)) - if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU and props.limits.maxImageDimension2D > score: - score = props.limits.maxImageDimension2D - result = pDevice - - if score == 0: - for pDevice in devices: - var props: VkPhysicalDeviceProperties - # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet) - vkGetPhysicalDeviceProperties(pDevice, addr(props)) - if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU and props.limits.maxImageDimension2D > score: - score = props.limits.maxImageDimension2D - result = pDevice - - assert score > 0, "Unable to find integrated or discrete GPU" - proc IsMappable(memoryTypeIndex: uint32): bool = var physicalProperties: VkPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties(vulkan.physicalDevice, addr(physicalProperties)) let flags = toEnums(physicalProperties.memoryTypes[memoryTypeIndex].propertyFlags) return VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT in flags -proc GetQueueFamily(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 = - var nQueuefamilies: uint32 - vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, nil) - var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies) - vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, queuFamilies.ToCPointer) - for i in 0'u32 ..< nQueuefamilies: - if qType in toEnums(queuFamilies[i].queueFlags): - return i - assert false, &"Queue of type {qType} not found" - proc GetSurfaceFormat(): VkFormat = # EVERY windows driver and almost every linux driver should support this VK_FORMAT_B8G8R8A8_SRGB -proc InitDescriptorSet( +proc GetLayoutFor*(pipeline: Pipeline, dType: DescriptorSetType): VkDescriptorSetLayout = + pipeline.descriptorSetLayouts[dType] + +proc InitDescriptorSet*( renderData: RenderData, layout: VkDescriptorSetLayout, descriptorSet: var DescriptorSet, @@ -394,18 +199,30 @@ of UInt16: VK_INDEX_TYPE_UINT16 of UInt32: VK_INDEX_TYPE_UINT32 -proc CreateRenderPass(format: VkFormat): VkRenderPass = +proc CreatePresentationRenderPass*(samples = VK_SAMPLE_COUNT_1_BIT): VkRenderPass = + let format = GetSurfaceFormat() + var attachments = @[VkAttachmentDescription( + format: format, + samples: samples, + loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, + storeOp: VK_ATTACHMENT_STORE_OP_STORE, + stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, + stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, + initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, + finalLayout: if samples == VK_SAMPLE_COUNT_1_BIT: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR else: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + )] + if samples != VK_SAMPLE_COUNT_1_BIT: + attachments.add VkAttachmentDescription( + format: format, + samples: VK_SAMPLE_COUNT_1_BIT, + loadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, + storeOp: VK_ATTACHMENT_STORE_OP_STORE, + stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, + stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, + initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, + finalLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + ) var - attachments = @[VkAttachmentDescription( - format: format, - samples: VK_SAMPLE_COUNT_1_BIT, - loadOp: VK_ATTACHMENT_LOAD_OP_CLEAR, - storeOp: VK_ATTACHMENT_STORE_OP_STORE, - stencilLoadOp: VK_ATTACHMENT_LOAD_OP_DONT_CARE, - stencilStoreOp: VK_ATTACHMENT_STORE_OP_DONT_CARE, - initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, - finalLayout: VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - )] dependencies = @[VkSubpassDependency( srcSubpass: VK_SUBPASS_EXTERNAL, dstSubpass: 0, @@ -414,34 +231,33 @@ dstStageMask: toBits [VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT], dstAccessMask: toBits [VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT], )] - outputs = @[ - VkAttachmentReference( - attachment: 0, - layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - ) - ] + colorAttachment = VkAttachmentReference( + attachment: 0, + layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + ) + resolveAttachment = VkAttachmentReference( + attachment: 1, + layout: VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + ) - var subpassesList = [ - VkSubpassDescription( - flags: VkSubpassDescriptionFlags(0), - pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, - inputAttachmentCount: 0, - pInputAttachments: nil, - colorAttachmentCount: uint32(outputs.len), - pColorAttachments: outputs.ToCPointer, - pResolveAttachments: nil, - pDepthStencilAttachment: nil, - preserveAttachmentCount: 0, - pPreserveAttachments: nil, - ) - ] - + var subpass = VkSubpassDescription( + flags: VkSubpassDescriptionFlags(0), + pipelineBindPoint: VK_PIPELINE_BIND_POINT_GRAPHICS, + inputAttachmentCount: 0, + pInputAttachments: nil, + colorAttachmentCount: 1, + pColorAttachments: addr(colorAttachment), + pResolveAttachments: if samples == VK_SAMPLE_COUNT_1_BIT: nil else: addr(resolveAttachment), + pDepthStencilAttachment: nil, + preserveAttachmentCount: 0, + pPreserveAttachments: nil, + ) var createInfo = VkRenderPassCreateInfo( sType: VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, attachmentCount: uint32(attachments.len), pAttachments: attachments.ToCPointer, - subpassCount: uint32(subpassesList.len), - pSubpasses: subpassesList.ToCPointer, + subpassCount: 1, + pSubpasses: addr(subpass), dependencyCount: uint32(dependencies.len), pDependencies: dependencies.ToCPointer, ) @@ -465,7 +281,7 @@ ppData = addr(result.rawPointer) ) -proc FlushAllMemory(renderData: RenderData) = +proc FlushAllMemory*(renderData: RenderData) = var flushRegions = newSeq[VkMappedMemoryRange]() for memoryBlocks in renderData.memory: for memoryBlock in memoryBlocks: @@ -517,7 +333,7 @@ result.rawPointer = selectedBlock.rawPointer.pointerAddOffset(selectedBlock.offsetNextFree) renderData.memory[memoryType][selectedBlockI].offsetNextFree += memoryRequirements.size -proc AssignBuffers[T](renderdata: var RenderData, data: var T) = +proc AssignBuffers*[T](renderdata: var RenderData, data: var T) = for name, value in fieldPairs(data): when typeof(value) is GPUData: @@ -545,7 +361,7 @@ value.buffer = selectedBuffer value.offset = renderdata.buffers[value.bufferType][selectedBufferI].offsetNextFree renderdata.buffers[value.bufferType][selectedBufferI].offsetNextFree += value.size -proc AssignBuffers(renderdata: var RenderData, descriptorSet: var DescriptorSet) = +proc AssignBuffers*(renderdata: var RenderData, descriptorSet: var DescriptorSet) = AssignBuffers(renderdata, descriptorSet.data) proc UpdateGPUBuffer(gpuData: GPUData) = @@ -557,13 +373,13 @@ WithStagingBuffer((gpuData.buffer.vk, gpuData.offset), gpuData.size, stagingPtr): copyMem(stagingPtr, gpuData.rawPointer, gpuData.size) -proc UpdateAllGPUBuffers[T](value: T) = +proc UpdateAllGPUBuffers*[T](value: T) = for name, fieldvalue in value.fieldPairs(): when typeof(fieldvalue) is GPUData: UpdateGPUBuffer(fieldvalue) -proc InitRenderData(descriptorPoolLimit = 1024'u32): RenderData = +proc InitRenderData*(descriptorPoolLimit = 1024'u32): RenderData = # allocate descriptor pools var poolSizes = [ VkDescriptorPoolSize(thetype: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, descriptorCount: descriptorPoolLimit), @@ -725,7 +541,7 @@ TransitionImageLayout(texture.vk, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) -proc UploadTextures(renderdata: var RenderData, descriptorSet: var DescriptorSet) = +proc UploadTextures*(renderdata: var RenderData, descriptorSet: var DescriptorSet) = for name, value in fieldPairs(descriptorSet.data): when typeof(value) is Texture: echo "Upload texture '", name, "'" diff -r 239adab121a3 -r 90bf0cab1d02 semicongine/rendering/shaders.nim --- a/semicongine/rendering/shaders.nim Mon Jul 08 01:16:53 2024 +0700 +++ b/semicongine/rendering/shaders.nim Mon Jul 08 16:05:06 2024 +0700 @@ -1,7 +1,131 @@ type ShaderObject[TShader] = object - vertexShader: VkShaderModule - fragmentShader: VkShaderModule + vertexShaderModule: VkShaderModule + fragmentShaderModule: VkShaderModule + +func GlslType[T: SupportedGPUType|Texture](value: T): string = + when T is float32: "float" + elif T is float64: "double" + elif T is int8 or T is int16 or T is int32 or T is int64: "int" + elif T is uint8 or T is uint16 or T is uint32 or T is uint64: "uint" + elif T is TVec2[int32]: "ivec2" + elif T is TVec2[int64]: "ivec2" + elif T is TVec3[int32]: "ivec3" + elif T is TVec3[int64]: "ivec3" + elif T is TVec4[int32]: "ivec4" + elif T is TVec4[int64]: "ivec4" + elif T is TVec2[uint32]: "uvec2" + elif T is TVec2[uint64]: "uvec2" + elif T is TVec3[uint32]: "uvec3" + elif T is TVec3[uint64]: "uvec3" + elif T is TVec4[uint32]: "uvec4" + elif T is TVec4[uint64]: "uvec4" + elif T is TVec2[float32]: "vec2" + elif T is TVec2[float64]: "dvec2" + elif T is TVec3[float32]: "vec3" + elif T is TVec3[float64]: "dvec3" + elif T is TVec4[float32]: "vec4" + elif T is TVec4[float64]: "dvec4" + elif T is TMat2[float32]: "mat2" + elif T is TMat2[float64]: "dmat2" + elif T is TMat23[float32]: "mat23" + elif T is TMat23[float64]: "dmat23" + elif T is TMat32[float32]: "mat32" + elif T is TMat32[float64]: "dmat32" + elif T is TMat3[float32]: "mat3" + elif T is TMat3[float64]: "dmat3" + elif T is TMat34[float32]: "mat34" + elif T is TMat34[float64]: "dmat34" + elif T is TMat43[float32]: "mat43" + elif T is TMat43[float64]: "dmat43" + elif T is TMat4[float32]: "mat4" + elif T is TMat4[float64]: "dmat4" + elif T is Texture: "sampler2D" + else: {.error: "Unsupported data type on GPU".} + +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 TMat2[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is TMat2[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is TMat23[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TMat23[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TMat32[float32]: VK_FORMAT_R32G32_SFLOAT + elif T is TMat32[float64]: VK_FORMAT_R64G64_SFLOAT + elif T is TMat3[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TMat3[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TMat34[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is TMat34[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + elif T is TMat43[float32]: VK_FORMAT_R32G32B32_SFLOAT + elif T is TMat43[float64]: VK_FORMAT_R64G64B64_SFLOAT + elif T is TMat4[float32]: VK_FORMAT_R32G32B32A32_SFLOAT + elif T is TMat4[float64]: VK_FORMAT_R64G64B64A64_SFLOAT + else: {.error: "Unsupported data type on GPU".} + + +func NumberOfVertexInputAttributeDescriptors[T: SupportedGPUType|Texture](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|Texture](value: T): uint32 = + #[ + single location: + - any scalar + - any 16-bit vector + - any 32-bit vector + - any 64-bit vector that has max. 2 components + 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 T is TVec3[int64] or + T is TVec4[int64] or + T is TVec3[uint64] or + T is TVec4[uint64] or + T is TVec3[float64] or + T is TVec4[float64] or + T is TMat23[float64] or + T is TMat3[float64] or + T is TMat34[float64] or + T is TMat43[float64] or + T is TMat4[float64]: + return 2 + else: + return 1 proc generateShaderSource[TShader](shader: TShader): (string, string) {.compileTime.} = const GLSL_VERSION = "450" @@ -39,7 +163,7 @@ # descriptor sets # need to consider 4 cases: uniform block, texture, uniform block array, texture array elif typeof(value) is DescriptorSet: - assert descriptorSetCount <= DescriptorSetType.high.int, &"{typetraits.name(TShader)}: maximum {DescriptorSetType.high} allowed" + assert descriptorSetCount <= DescriptorSetType.high.int, typetraits.name(TShader) & ": maximum " & $DescriptorSetType.high & " allowed" var descriptorBinding = 0 for descriptorName, descriptorValue in fieldPairs(value.data): @@ -111,8 +235,7 @@ echo "shader of type ", stage for i, line in enumerate(shaderSource.splitlines()): echo " ", i + 1, " ", line - # var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator" - var glslExe = currentSourcePath.parentDir / "tools" / "glslangValidator" + var glslExe = currentSourcePath.parentDir.parentDir.parentDir / "tools" / "glslangValidator" when defined(windows): glslExe = glslExe & "." & ExeExt let command = &"{glslExe} --entry-point main -V --stdin -S {stagename} -o {shaderfile}" @@ -151,13 +274,13 @@ codeSize: csize_t(vertexBinary.len * sizeof(uint32)), pCode: vertexBinary.ToCPointer, ) - checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoVertex), nil, addr(result.vertexShader)) + checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoVertex), nil, addr(result.vertexShaderModule)) var createInfoFragment = VkShaderModuleCreateInfo( sType: VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, codeSize: csize_t(fragmentBinary.len * sizeof(uint32)), pCode: fragmentBinary.ToCPointer, ) - checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoFragment), nil, addr(result.fragmentShader)) + checkVkResult vulkan.device.vkCreateShaderModule(addr(createInfoFragment), nil, addr(result.fragmentShaderModule)) template ForVertexDataFields(shader: typed, fieldname, valuename, isinstancename, body: untyped): untyped = for theFieldname, value in fieldPairs(shader): @@ -172,9 +295,8 @@ const `isinstancename` {.inject.} = hasCustomPragma(value, InstanceAttribute) body -proc CreatePipeline[TShader]( +proc CreatePipeline*[TShader]( renderPass: VkRenderPass, - shader: ShaderObject[TShader], topology: VkPrimitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, @@ -183,6 +305,9 @@ ): Pipeline[TShader] = # create pipeline + const shader = default(TShader) + let shaderObject = CompileShader(shader) + for theFieldname, value in fieldPairs(default(TShader)): when typeof(value) is DescriptorSet: var layoutbindings: seq[VkDescriptorSetLayoutBinding] @@ -218,13 +343,13 @@ VkPipelineShaderStageCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, stage: VK_SHADER_STAGE_VERTEX_BIT, - module: shader.vertexShader, + module: shaderObject.vertexShaderModule, pName: "main", ), VkPipelineShaderStageCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, stage: VK_SHADER_STAGE_FRAGMENT_BIT, - module: shader.fragmentShader, + module: shaderObject.fragmentShaderModule, pName: "main", ), ] diff -r 239adab121a3 -r 90bf0cab1d02 semicongine/rendering/vulkan_wrappers.nim --- a/semicongine/rendering/vulkan_wrappers.nim Mon Jul 08 01:16:53 2024 +0700 +++ b/semicongine/rendering/vulkan_wrappers.nim Mon Jul 08 16:05:06 2024 +0700 @@ -9,12 +9,56 @@ physicalDevice*: VkPhysicalDevice surface: VkSurfaceKHR window: NativeWindow - queueFamilyIndex*: uint32 - queue*: VkQueue + graphicsQueueFamily*: uint32 + graphicsQueue*: VkQueue anisotropy*: float32 = 0 # needs to be enable during device creation var vulkan*: VulkanGlobals +proc GetBestPhysicalDevice(instance: VkInstance): VkPhysicalDevice = + var nDevices: uint32 + checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), nil) + var devices = newSeq[VkPhysicalDevice](nDevices) + checkVkResult vkEnumeratePhysicalDevices(instance, addr(nDevices), devices.ToCPointer) + + var score = 0'u32 + for pDevice in devices: + var props: VkPhysicalDeviceProperties + # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet) + vkGetPhysicalDeviceProperties(pDevice, addr(props)) + if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU and props.limits.maxImageDimension2D > score: + score = props.limits.maxImageDimension2D + result = pDevice + + if score == 0: + for pDevice in devices: + var props: VkPhysicalDeviceProperties + # CANNOT use svkGetPhysicalDeviceProperties (not initialized yet) + vkGetPhysicalDeviceProperties(pDevice, addr(props)) + if props.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU and props.limits.maxImageDimension2D > score: + score = props.limits.maxImageDimension2D + result = pDevice + + assert score > 0, "Unable to find integrated or discrete GPU" + +proc GetQueueFamily(pDevice: VkPhysicalDevice, qType: VkQueueFlagBits): uint32 = + var nQueuefamilies: uint32 + vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, nil) + var queuFamilies = newSeq[VkQueueFamilyProperties](nQueuefamilies) + vkGetPhysicalDeviceQueueFamilyProperties(pDevice, addr nQueuefamilies, queuFamilies.ToCPointer) + for i in 0'u32 ..< nQueuefamilies: + if qType in toEnums(queuFamilies[i].queueFlags): + return i + assert false, &"Queue of type {qType} not found" + +proc svkGetDeviceQueue*(device: VkDevice, queueFamilyIndex: uint32, qType: VkQueueFlagBits): VkQueue = + vkGetDeviceQueue( + device, + queueFamilyIndex, + 0, + addr(result), + ) + proc hasValidationLayer*(): bool = var n_layers: uint32 checkVkResult vkEnumerateInstanceLayerProperties(addr(n_layers), nil) @@ -28,6 +72,8 @@ proc initVulkan*(platformLayers: seq[string], appName: string = "semicongine app") = + # instance creation + # when not defined(release): let requiredExtensions = REQUIRED_PLATFORM_EXTENSIONS & @["VK_KHR_surface", "VK_EXT_debug_utils"] let layers: seq[string] = if hasValidationLayer(): @["VK_LAYER_KHRONOS_validation"] else: @[] @@ -38,6 +84,11 @@ var layersC = allocCStringArray(layers) instanceExtensionsC = allocCStringArray(requiredExtensions) + defer: + deallocCStringArray(layersC) + deallocCStringArray(instanceExtensionsC) + + var appinfo = VkApplicationInfo( sType: VK_STRUCTURE_TYPE_APPLICATION_INFO, pApplicationName: appName, @@ -54,14 +105,64 @@ ) checkVkResult vkCreateInstance(addr(createinfo), nil, addr(vulkan.instance)) loadVulkan(vulkan.instance) - deallocCStringArray(layersC) - deallocCStringArray(instanceExtensionsC) + + # load extensions + # for extension in requiredExtensions: loadExtension(vulkan.instance, $extension) vulkan.window = CreateWindow(appName) vulkan.surface = CreateNativeSurface(vulkan.instance, vulkan.window) + + + # logical device creation + + + + + # TODO: allowing support for physical devices without hasUniformBufferStandardLayout + # would require us to ship different shaders, so we don't support standard layout + # if that will be added, check the function vulkan/shaders.nim:glslUniforms and update accordingly + # let hasUniformBufferStandardLayout = "VK_KHR_uniform_buffer_standard_layout" in physicalDevice.getExtensions() + # var deviceExtensions = @["VK_KHR_swapchain", "VK_KHR_uniform_buffer_standard_layout"] + var deviceExtensions = @["VK_KHR_swapchain"] + for extension in deviceExtensions: + loadExtension(vulkan.instance, extension) + + # get physical device and graphics queue family + vulkan.physicalDevice = GetBestPhysicalDevice(vulkan.instance) + vulkan.graphicsQueueFamily = GetQueueFamily(vulkan.physicalDevice, VK_QUEUE_GRAPHICS_BIT) + + let + priority = cfloat(1) + queueInfo = VkDeviceQueueCreateInfo( + sType: VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + queueFamilyIndex: vulkan.graphicsQueueFamily, + queueCount: 1, + pQueuePriorities: addr(priority), + ) + deviceExtensionsC = allocCStringArray(deviceExtensions) + defer: deallocCStringArray(deviceExtensionsC) + var createDeviceInfo = VkDeviceCreateInfo( + sType: VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + queueCreateInfoCount: 1, + pQueueCreateInfos: addr(queueInfo), + enabledLayerCount: 0, + ppEnabledLayerNames: nil, + enabledExtensionCount: uint32(deviceExtensions.len), + ppEnabledExtensionNames: deviceExtensionsC, + pEnabledFeatures: nil, + ) + checkVkResult vkCreateDevice( + physicalDevice = vulkan.physicalDevice, + pCreateInfo = addr createDeviceInfo, + pAllocator = nil, + pDevice = addr vulkan.device + ) + vulkan.graphicsQueue = svkGetDeviceQueue(vulkan.device, vulkan.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) + + proc svkGetPhysicalDeviceProperties*(): VkPhysicalDeviceProperties = vkGetPhysicalDeviceProperties(vulkan.physicalDevice, addr(result)) @@ -120,14 +221,6 @@ ) checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result)) -proc svkGetDeviceQueue*(device: VkDevice, queueFamilyIndex: uint32, qType: VkQueueFlagBits): VkQueue = - vkGetDeviceQueue( - device, - queueFamilyIndex, - 0, - addr(result), - ) - proc svkGetBufferMemoryRequirements*(buffer: VkBuffer): tuple[size: uint64, alignment: uint64, memoryTypes: seq[uint32]] = var reqs: VkMemoryRequirements vkGetBufferMemoryRequirements(vulkan.device, buffer, addr(reqs)) @@ -173,7 +266,7 @@ createInfo = VkCommandPoolCreateInfo( sType: VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, flags: toBits [VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT], - queueFamilyIndex: vulkan.queueFamilyIndex, + queueFamilyIndex: vulkan.graphicsQueueFamily, ) checkVkResult vkCreateCommandPool(vulkan.device, addr createInfo, nil, addr(commandBufferPool)) var @@ -207,7 +300,7 @@ # flags: toBits [VK_FENCE_CREATE_SIGNALED_BIT] ) checkVkResult vulkan.device.vkCreateFence(addr(fenceInfo), nil, addr(fence)) - checkVkResult vkQueueSubmit(vulkan.queue, 1, addr(submitInfo), fence) + checkVkResult vkQueueSubmit(vulkan.graphicsQueue, 1, addr(submitInfo), fence) checkVkResult vkWaitForFences(vulkan.device, 1, addr fence, false, high(uint64)) vkDestroyCommandPool(vulkan.device, commandBufferPool, nil) diff -r 239adab121a3 -r 90bf0cab1d02 test1.nim --- a/test1.nim Mon Jul 08 01:16:53 2024 +0700 +++ b/test1.nim Mon Jul 08 16:05:06 2024 +0700 @@ -40,35 +40,11 @@ vertexCode: string = "void main() {}" fragmentCode: string = "void main() {}" -let w = CreateWindow("test2") putEnv("VK_LAYER_ENABLES", "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD,VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXTVK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT,VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT") -# TODO: remove those ugly wrappers -let theInstance = w.CreateInstance( - vulkanVersion = VK_MAKE_API_VERSION(0, 1, 3, 0), - instanceExtensions = @[], - layers = @["VK_LAYER_KHRONOS_validation"], -) - -let dev = theInstance.CreateDevice( - theInstance.GetPhysicalDevices().FilterBestGraphics(), - enabledExtensions = @[], - theInstance.GetPhysicalDevices().FilterBestGraphics().FilterForGraphicsPresentationQueues() -).vk let frameWidth = 100'u32 let frameHeight = 100'u32 -# TODO: pack this stuff into a setup method and condense everything a bit -let pDevice = theInstance.vk.GetPhysicalDevice() -let qfi = pDevice.GetQueueFamily(VK_QUEUE_GRAPHICS_BIT) -vulkan = VulkanGlobals( - instance: theInstance.vk, - device: dev, - physicalDevice: pDevice, - queueFamilyIndex: qfi, - queue: svkGetDeviceQueue(dev, qfi, VK_QUEUE_GRAPHICS_BIT) -) - var myMesh1 = MeshA( position: GPUArray[Vec3f, VertexBuffer](data: @[NewVec3f(0, 0, ), NewVec3f(0, 0, ), NewVec3f(0, 0, )]), ) @@ -98,13 +74,10 @@ ) ) -# setup for rendering (TODO: swapchain & framebuffers) -let renderpass = CreateRenderPass(GetSurfaceFormat()) +let renderpass = CreatePresentationRenderPass() # shaders -const shader = ShaderA() -let shaderObject = CompileShader(shader) -var pipeline1 = CreatePipeline(renderPass = renderpass, shader = shaderObject) +var pipeline1 = CreatePipeline[ShaderA](renderPass = renderpass) var renderdata = InitRenderData() @@ -130,8 +103,8 @@ # descriptors echo "Writing descriptors" -InitDescriptorSet(renderdata, pipeline1.descriptorSetLayouts[GlobalSet], myGlobals) -InitDescriptorSet(renderdata, pipeline1.descriptorSetLayouts[MaterialSet], uniforms1) +InitDescriptorSet(renderdata, pipeline1.GetLayoutFor(GlobalSet), myGlobals) +InitDescriptorSet(renderdata, pipeline1.GetLayoutFor(MaterialSet), uniforms1) # command buffer