# HG changeset patch # User sam # Date 1721062277 -25200 # Node ID e2901100a5963d9829687bc7f9cf66a78952885c # Parent 6360c8d17ce0129435c92028d565f4a95354ccd2 add: tests, some fixes, some helpers diff -r 6360c8d17ce0 -r e2901100a596 semicongine/rendering.nim --- a/semicongine/rendering.nim Mon Jul 15 20:06:42 2024 +0700 +++ b/semicongine/rendering.nim Mon Jul 15 23:51:17 2024 +0700 @@ -42,7 +42,7 @@ # parameters to InitSwapchain, required for swapchain recreation renderPass: VkRenderPass vSync: bool - samples: VkSampleCountFlagBits + samples*: VkSampleCountFlagBits # populated through InitSwapchain proc vk: VkSwapchainKHR width: uint32 @@ -74,8 +74,11 @@ # shader related types DescriptorSetType* = enum - GlobalSet - MaterialSet + First + Second + # only two supported for now, but more should be easy to add + # Third + # Fourth DescriptorSet*[T: object, sType: static DescriptorSetType] = object data*: T vk: array[INFLIGHTFRAMES.int, VkDescriptorSet] @@ -196,7 +199,7 @@ raise newException(Exception, errorMsg) return false -proc InitVulkan(appName: string = "semicongine app"): VulkanGlobals = +proc InitVulkan*(appName: string = "semicongine app") = include ./platform/vulkan_extensions # for REQUIRED_PLATFORM_EXTENSIONS @@ -233,15 +236,15 @@ enabledExtensionCount: requiredExtensions.len.uint32, ppEnabledExtensionNames: instanceExtensionsC ) - checkVkResult vkCreateInstance(addr(createinfo), nil, addr(result.instance)) - loadVulkan(result.instance) + checkVkResult vkCreateInstance(addr(createinfo), nil, addr(vulkan.instance)) + loadVulkan(vulkan.instance) # load extensions # for extension in requiredExtensions: - loadExtension(result.instance, $extension) - result.window = CreateWindow(appName) - result.surface = CreateNativeSurface(result.instance, result.window) + loadExtension(vulkan.instance, $extension) + vulkan.window = CreateWindow(appName) + vulkan.surface = CreateNativeSurface(vulkan.instance, vulkan.window) # logical device creation @@ -252,7 +255,7 @@ # var deviceExtensions = @["VK_KHR_swapchain", "VK_KHR_uniform_buffer_standard_layout"] var deviceExtensions = @["VK_KHR_swapchain"] for extension in deviceExtensions: - loadExtension(result.instance, extension) + loadExtension(vulkan.instance, extension) when not defined(release): var debugMessengerCreateInfo = VkDebugUtilsMessengerCreateInfoEXT( @@ -263,21 +266,21 @@ pUserData: nil, ) checkVkResult vkCreateDebugUtilsMessengerEXT( - result.instance, + vulkan.instance, addr(debugMessengerCreateInfo), nil, - addr(result.debugMessenger) + addr(vulkan.debugMessenger) ) # get physical device and graphics queue family - result.physicalDevice = GetBestPhysicalDevice(result.instance) - result.graphicsQueueFamily = GetQueueFamily(result.physicalDevice, VK_QUEUE_GRAPHICS_BIT) + 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: result.graphicsQueueFamily, + queueFamilyIndex: vulkan.graphicsQueueFamily, queueCount: 1, pQueuePriorities: addr(priority), ) @@ -294,17 +297,15 @@ pEnabledFeatures: nil, ) checkVkResult vkCreateDevice( - physicalDevice = result.physicalDevice, + physicalDevice = vulkan.physicalDevice, pCreateInfo = addr createDeviceInfo, pAllocator = nil, - pDevice = addr result.device + pDevice = addr vulkan.device ) - result.graphicsQueue = svkGetDeviceQueue(result.device, result.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) + vulkan.graphicsQueue = svkGetDeviceQueue(vulkan.device, vulkan.graphicsQueueFamily, VK_QUEUE_GRAPHICS_BIT) proc DestroyVulkan*() = vkDestroyDevice(vulkan.device, nil) vkDestroySurfaceKHR(vulkan.instance, vulkan.surface, nil) vkDestroyDebugUtilsMessengerEXT(vulkan.instance, vulkan.debugMessenger, nil) vkDestroyInstance(vulkan.instance, nil) - -vulkan = InitVulkan() diff -r 6360c8d17ce0 -r e2901100a596 semicongine/rendering/renderer.nim --- a/semicongine/rendering/renderer.nim Mon Jul 15 20:06:42 2024 +0700 +++ b/semicongine/rendering/renderer.nim Mon Jul 15 23:51:17 2024 +0700 @@ -56,11 +56,6 @@ template sType(descriptorSet: DescriptorSet): untyped = get(genericParams(typeof(descriptorSet)), 1) -# template bufferType[T: SupportedGPUType, TBuffer: static BufferType](gpuArray: GPUArray[T, TBuffer]): untyped = - # TBuffer -# template bufferType[T: SupportedGPUType, TBuffer: static BufferType](gpuValue: GPUValue[T, TBuffer]): untyped = - # TBuffer - template bufferType(gpuData: GPUData): untyped = typeof(gpuData).TBuffer func NeedsMapping(bType: BufferType): bool = @@ -252,7 +247,21 @@ result.rawPointer = selectedBlock.rawPointer.pointerAddOffset(selectedBlock.offsetNextFree) renderData.memory[memoryType][selectedBlockI].offsetNextFree += memoryRequirements.size -proc AssignBuffers*[T](renderdata: var RenderData, data: var T) = +proc UpdateGPUBuffer(gpuData: GPUData) = + if gpuData.size == 0: + return + when NeedsMapping(gpuData): + copyMem(pointerAddOffset(gpuData.buffer.rawPointer, gpuData.offset), gpuData.rawPointer, gpuData.size) + else: + WithStagingBuffer((gpuData.buffer.vk, gpuData.offset), gpuData.size, stagingPtr): + copyMem(stagingPtr, gpuData.rawPointer, gpuData.size) + +proc UpdateAllGPUBuffers*[T](value: T) = + for name, fieldvalue in value.fieldPairs(): + when typeof(fieldvalue) is GPUData: + UpdateGPUBuffer(fieldvalue) + +proc AssignBuffers*[T](renderdata: var RenderData, data: var T, uploadData = true) = for name, value in fieldPairs(data): when typeof(value) is GPUData: @@ -280,23 +289,11 @@ 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) = - AssignBuffers(renderdata, descriptorSet.data) + if uploadData: + UpdateAllGPUBuffers(data) -proc UpdateGPUBuffer(gpuData: GPUData) = - if gpuData.size == 0: - return - when NeedsMapping(gpuData): - copyMem(pointerAddOffset(gpuData.buffer.rawPointer, gpuData.offset), gpuData.rawPointer, gpuData.size) - else: - WithStagingBuffer((gpuData.buffer.vk, gpuData.offset), gpuData.size, stagingPtr): - copyMem(stagingPtr, gpuData.rawPointer, gpuData.size) - -proc UpdateAllGPUBuffers*[T](value: T) = - for name, fieldvalue in value.fieldPairs(): - when typeof(fieldvalue) is GPUData: - UpdateGPUBuffer(fieldvalue) - +proc AssignBuffers*(renderdata: var RenderData, descriptorSet: var DescriptorSet, uploadData = true) = + AssignBuffers(renderdata, descriptorSet.data, uploadData = uploadData) proc InitRenderData*(descriptorPoolLimit = 1024'u32): RenderData = # allocate descriptor pools @@ -485,7 +482,7 @@ let `fieldvalue` {.inject.} = value body -proc AssertCompatible(TShader, TMesh, TInstance, TGlobals, TMaterial: typedesc) = +proc AssertCompatible(TShader, TMesh, TInstance, TFirstDescriptorSet, TSecondDescriptorSet: typedesc) = var descriptorSetCount = 0 for shaderAttributeName, shaderAttribute in default(TShader).fieldPairs: @@ -519,18 +516,18 @@ descriptorSetCount.inc - 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 '" & typetraits.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & typetraits.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 '" & typetraits.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & typetraits.name(TMaterial) + when shaderAttribute.sType == First: + assert shaderAttribute.sType == default(TFirstDescriptorSet).sType, "Shader has first descriptor set of type '" & $shaderAttribute.sType & "' but matching provided type is '" & $default(TFirstDescriptorSet).sType & "'" + assert typeof(shaderAttribute) is TFirstDescriptorSet, "Shader has first descriptor set type '" & typetraits.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & typetraits.name(TFirstDescriptorSet) + elif shaderAttribute.sType == Second: + assert shaderAttribute.sType == default(TSecondDescriptorSet).sType, "Shader has second descriptor set of type '" & $shaderAttribute.sType & "' but matching provided type is '" & $default(TSecondDescriptorSet).sType & "'" + assert typeof(shaderAttribute) is TSecondDescriptorSet, "Shader has seconddescriptor type '" & typetraits.name(get(genericParams(typeof(shaderAttribute)), 0)) & "' but provided type is " & typetraits.name(TSecondDescriptorSet) -template WithBind*[A, B](commandBuffer: VkCommandBuffer, globalDescriptorSet: DescriptorSet[A, GlobalSet], materialDescriptorSet: DescriptorSet[B, MaterialSet], pipeline: Pipeline, currentFiF: int, body: untyped): untyped = +template WithBind*[A, B](commandBuffer: VkCommandBuffer, firstDescriptorSet: DescriptorSet[A, First], secondDescriptorSet: DescriptorSet[B, Second], pipeline: Pipeline, currentFiF: int, body: untyped): untyped = block: - let sets = [globalDescriptorSet.vk[currentFiF], materialDescriptorSet.vk[currentFiF]] + let sets = [firstDescriptorSet.vk[currentFiF], secondDescriptorSet.vk[currentFiF]] vkCmdBindDescriptorSets( commandBuffer = commandBuffer, pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, @@ -543,16 +540,31 @@ ) body -proc Render*[TShader, TGlobals, TMaterial, TMesh, TInstance]( +template WithBind*[A](commandBuffer: VkCommandBuffer, firstDescriptorSet: DescriptorSet[A, First], pipeline: Pipeline, currentFiF: int, body: untyped): untyped = + block: + let sets = [firstDescriptorSet.vk[currentFiF]] + vkCmdBindDescriptorSets( + commandBuffer = commandBuffer, + pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + layout = pipeline.layout, + firstSet = 0, + descriptorSetCount = sets.len.uint32, + pDescriptorSets = sets.ToCPointer, + dynamicOffsetCount = 0, + pDynamicOffsets = nil + ) + body + +proc Render*[TFirstDescriptorSet, TSecondDescriptorSet: DescriptorSet, TShader, TMesh, TInstance]( commandBuffer: VkCommandBuffer, pipeline: Pipeline[TShader], - globalSet: TGlobals, - materialSet: TMaterial, + firstSet: TFirstDescriptorSet, + secondSet: TSecondDescriptorSet, mesh: TMesh, instances: TInstance, ) = when not defined(release): - static: AssertCompatible(TShader, TMesh, TInstance, TGlobals, TMaterial) + static: AssertCompatible(TShader, TMesh, TInstance, TFirstDescriptorSet, TSecondDescriptorSet) var vertexBuffers: seq[VkBuffer] var vertexBuffersOffsets: seq[uint64] @@ -630,23 +642,48 @@ ) type EMPTY = object +type EMPTY_FIRST_DESCRIPTORSET = DescriptorSet[EMPTY, First] +type EMPTY_SECOND_DESCRIPTORSET = DescriptorSet[EMPTY, Second] -proc Render*[TShader, TGlobals, TMaterial, TMesh]( +proc Render*[TFirstDescriptorSet, TSecondDescriptorSet: DescriptorSet, TShader, TMesh]( commandBuffer: VkCommandBuffer, pipeline: Pipeline[TShader], - globalSet: TGlobals, - materialSet: TMaterial, + firstSet: TFirstDescriptorSet, + secondSet: TSecondDescriptorSet, + mesh: TMesh, +) = + Render(commandBuffer, pipeline, firstSet, secondSet, mesh, EMPTY()) +proc Render*[TFirstDescriptorSet: DescriptorSet, TMesh, TShader]( + commandBuffer: VkCommandBuffer, + pipeline: Pipeline[TShader], + firstSet: TFirstDescriptorSet, mesh: TMesh, ) = - Render(commandBuffer, pipeline, globalSet, materialSet, mesh, EMPTY()) - -#[ -proc `@`*[T: SupportedGPUType, BT: static BufferType]( - bufferType: BT, - data: openArray[T] -): GPUArray[T, BT] = - GPUArray[T, BT](data: @data) -]# + Render(commandBuffer, pipeline, firstSet, EMPTY_SECOND_DESCRIPTORSET(), mesh, EMPTY()) +proc Render*[TShader, TMesh]( + commandBuffer: VkCommandBuffer, + pipeline: Pipeline[TShader], + mesh: TMesh, +) = + Render(commandBuffer, pipeline, EMPTY_FIRST_DESCRIPTORSET(), EMPTY_SECOND_DESCRIPTORSET(), mesh, EMPTY()) +proc Render*[TFirstDescriptorSet: DescriptorSet, TMesh, TShader, TInstance]( + commandBuffer: VkCommandBuffer, + pipeline: Pipeline[TShader], + firstSet: TFirstDescriptorSet, + mesh: TMesh, + instances: TInstance, +) = + Render(commandBuffer, pipeline, firstSet, EMPTY_SECOND_DESCRIPTORSET(), mesh, instances) +proc Render*[TShader, TMesh, TInstance]( + commandBuffer: VkCommandBuffer, + pipeline: Pipeline[TShader], + mesh: TMesh, + instances: TInstance, +) = + Render(commandBuffer, pipeline, EMPTY_FIRST_DESCRIPTORSET(), EMPTY_SECOND_DESCRIPTORSET(), mesh, instances) proc asGPUArray*[T](data: openArray[T], bufferType: static BufferType): auto = GPUArray[T, bufferType](data: @data) + +proc asGPUValue*[T](data: T, bufferType: static BufferType): auto = + GPUValue[T, bufferType](data: data) diff -r 6360c8d17ce0 -r e2901100a596 semicongine/rendering/renderpasses.nim --- a/semicongine/rendering/renderpasses.nim Mon Jul 15 20:06:42 2024 +0700 +++ b/semicongine/rendering/renderpasses.nim Mon Jul 15 23:51:17 2024 +0700 @@ -1,4 +1,6 @@ proc CreatePresentationRenderPass*(samples = VK_SAMPLE_COUNT_1_BIT): VkRenderPass = + assert vulkan.instance.Valid, "Vulkan not initialized" + let format = DefaultSurfaceFormat() var attachments = @[VkAttachmentDescription( format: format, @@ -86,9 +88,9 @@ ) viewport = VkViewport( x: 0.0, - y: 0.0, + y: renderHeight.float32, width: renderWidth.float32, - height: renderHeight.float32, + height: -renderHeight.float32, minDepth: 0.0, maxDepth: 1.0, ) diff -r 6360c8d17ce0 -r e2901100a596 semicongine/rendering/shaders.nim --- a/semicongine/rendering/shaders.nim Mon Jul 15 20:06:42 2024 +0700 +++ b/semicongine/rendering/shaders.nim Mon Jul 15 23:51:17 2024 +0700 @@ -296,13 +296,15 @@ polygonMode: VkPolygonMode = VK_POLYGON_MODE_FILL, cullMode: VkCullModeFlagBits = VK_CULL_MODE_BACK_BIT, frontFace: VkFrontFace = VK_FRONT_FACE_CLOCKWISE, - descriptorPoolLimit = 1024 + descriptorPoolLimit = 1024, + samples = VK_SAMPLE_COUNT_1_BIT, ): Pipeline[TShader] = # create pipeline const shader = default(TShader) (result.vertexShaderModule, result.fragmentShaderModule) = CompileShader(shader) + var n = 0'u32 for theFieldname, value in fieldPairs(default(TShader)): when typeof(value) is DescriptorSet: var layoutbindings: seq[VkDescriptorSetLayoutBinding] @@ -325,10 +327,11 @@ nil, addr(result.descriptorSetLayouts[value.sType]) ) + inc n let pipelineLayoutInfo = VkPipelineLayoutCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - setLayoutCount: result.descriptorSetLayouts.len.uint32, - pSetLayouts: result.descriptorSetLayouts.ToCPointer, + setLayoutCount: n, + pSetLayouts: if n == 0: nil else: result.descriptorSetLayouts.ToCPointer, # pushConstantRangeCount: uint32(pushConstants.len), # pPushConstantRanges: pushConstants.ToCPointer, ) @@ -405,7 +408,7 @@ multisampling = VkPipelineMultisampleStateCreateInfo( sType: VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, sampleShadingEnable: VK_FALSE, - rasterizationSamples: VK_SAMPLE_COUNT_1_BIT, + rasterizationSamples: samples, minSampleShading: 1.0, pSampleMask: nil, alphaToCoverageEnable: VK_FALSE, diff -r 6360c8d17ce0 -r e2901100a596 semicongine/rendering/swapchain.nim --- a/semicongine/rendering/swapchain.nim Mon Jul 15 20:06:42 2024 +0700 +++ b/semicongine/rendering/swapchain.nim Mon Jul 15 23:51:17 2024 +0700 @@ -6,7 +6,7 @@ samples = VK_SAMPLE_COUNT_1_BIT, oldSwapchain: ref Swapchain = nil, ): Option[Swapchain] = - assert vulkan.instance.Valid + assert vulkan.instance.Valid, "Vulkan not initialized" var capabilities: VkSurfaceCapabilitiesKHR checkVkResult vkGetPhysicalDeviceSurfaceCapabilitiesKHR(vulkan.physicalDevice, vulkan.surface, addr(capabilities)) @@ -62,6 +62,7 @@ height = height, format = format, usage = [VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT], + samples = samples, ) let requirements = svkGetImageMemoryRequirements(swapchain.msaaImage) swapchain.msaaMemory = svkAllocateMemory( @@ -114,6 +115,11 @@ proc DestroySwapchain*(swapchain: Swapchain) = + if swapchain.samples != VK_SAMPLE_COUNT_1_BIT: + vkDestroyImageView(vulkan.device, swapchain.msaaImageView, nil) + vkDestroyImage(vulkan.device, swapchain.msaaImage, nil) + vkFreeMemory(vulkan.device, swapchain.msaaMemory, nil) + for fence in swapchain.queueFinishedFence: vkDestroyFence(vulkan.device, fence, nil) diff -r 6360c8d17ce0 -r e2901100a596 semicongine/rendering/vulkan_wrappers.nim --- a/semicongine/rendering/vulkan_wrappers.nim Mon Jul 15 20:06:42 2024 +0700 +++ b/semicongine/rendering/vulkan_wrappers.nim Mon Jul 15 23:51:17 2024 +0700 @@ -113,7 +113,7 @@ addr(result), ) -proc svkCreate2DImage*(width, height: uint32, format: VkFormat, usage: openArray[VkImageUsageFlagBits]): VkImage = +proc svkCreate2DImage*(width, height: uint32, format: VkFormat, usage: openArray[VkImageUsageFlagBits], samples = VK_SAMPLE_COUNT_1_BIT): VkImage = var imageProps: VkImageFormatProperties checkVkResult vkGetPhysicalDeviceImageFormatProperties( vulkan.physicalDevice, @@ -136,7 +136,7 @@ initialLayout: VK_IMAGE_LAYOUT_UNDEFINED, usage: usage.toBits, sharingMode: VK_SHARING_MODE_EXCLUSIVE, - samples: VK_SAMPLE_COUNT_1_BIT, + samples: samples, ) checkVkResult vkCreateImage(vulkan.device, addr imageInfo, nil, addr(result)) diff -r 6360c8d17ce0 -r e2901100a596 test1.nim --- a/test1.nim Mon Jul 15 20:06:42 2024 +0700 +++ b/test1.nim Mon Jul 15 23:51:17 2024 +0700 @@ -47,9 +47,11 @@ color = vec4(1, 0, 0, 1); }""" +InitVulkan() + var myMesh1 = MeshA( - position: GPUArray[Vec3f, VertexBuffer](data: @[NewVec3f(-0.5, 0.5, ), NewVec3f(0, -0.5, ), NewVec3f(0.5, 0.5, )]), - indices: GPUArray[uint16, IndexBuffer](data: @[0'u16, 1'u16, 2'u16]) + position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(0, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer), + indices: asGPUArray([0'u16, 1'u16, 2'u16], IndexBuffer), ) var uniforms1 = DescriptorSet[UniformsA, MaterialSet]( data: UniformsA( @@ -96,10 +98,6 @@ # copy everything to GPU echo "Copying all data to GPU memory" -UpdateAllGPUBuffers(myMesh1) -UpdateAllGPUBuffers(instances1) -UpdateAllGPUBuffers(uniforms1) -UpdateAllGPUBuffers(myGlobals) renderdata.FlushAllMemory() # descriptors diff -r 6360c8d17ce0 -r e2901100a596 tests/test_rendering Binary file tests/test_rendering has changed diff -r 6360c8d17ce0 -r e2901100a596 tests/test_rendering.nim --- a/tests/test_rendering.nim Mon Jul 15 20:06:42 2024 +0700 +++ b/tests/test_rendering.nim Mon Jul 15 23:51:17 2024 +0700 @@ -1,11 +1,13 @@ import std/options +import std/random + import ../semicongine var mainRenderpass: VkRenderPass swapchain: Swapchain -proc test_01_gl_triangle() = +proc test_01_triangle(nFrames: int) = var renderdata = InitRenderData() type @@ -23,41 +25,188 @@ TriangleMesh = object position: GPUArray[Vec3f, VertexBuffer] color: GPUArray[Vec3f, VertexBuffer] - Empty = object var mesh = TriangleMesh( - position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(-0.5, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer), + position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(0, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer), color: asGPUArray([NewVec3f(0, 0, 1), NewVec3f(0, 1, 0), NewVec3f(1, 0, 0)], VertexBuffer), ) + AssignBuffers(renderdata, mesh) + renderdata.FlushAllMemory() var - pipeline = CreatePipeline[TrianglShader](renderPass = mainRenderpass) - a, b: Empty + pipeline = CreatePipeline[TrianglShader](renderPass = mainRenderpass, samples = swapchain.samples) - while UpdateInputs(): + var c = 0 + while UpdateInputs() and c < nFrames: WithNextFrame(swapchain, framebuffer, commandbuffer): WithRenderPass(mainRenderpass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): WithPipeline(commandbuffer, pipeline): - # WithBind(commandBuffer, a, b, pipeline, swapchain.currentFiF.int): - Render( - commandbuffer = commandbuffer, - pipeline = pipeline, - globalSet = a, - materialSet = b, - mesh = mesh, - ) + Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = mesh) + inc c # cleanup + checkVkResult vkDeviceWaitIdle(vulkan.device) DestroyPipeline(pipeline) DestroyRenderData(renderdata) -when isMainModule: - mainRenderpass = CreatePresentationRenderPass() - swapchain = InitSwapchain(renderpass = mainRenderpass).get() +proc test_02_triangle_quad_instanced(nFrames: int) = + var renderdata = InitRenderData() + + type + SomeShader = object + position {.VertexAttribute.}: Vec3f + color {.VertexAttribute.}: Vec3f + pos {.InstanceAttribute.}: Vec3f + scale {.InstanceAttribute.}: float32 + fragmentColor {.Pass.}: Vec3f + outColor {.ShaderOutput.}: Vec4f + # code + vertexCode: string = """void main() { + fragmentColor = color; + gl_Position = vec4((position * scale) + pos, 1);}""" + fragmentCode: string = """void main() { + outColor = vec4(fragmentColor, 1);}""" + TriangleMesh = object + position: GPUArray[Vec3f, VertexBuffer] + color: GPUArray[Vec3f, VertexBuffer] + QuadMesh = object + position: GPUArray[Vec3f, VertexBuffer] + color: GPUArray[Vec3f, VertexBuffer] + indices: GPUArray[uint16, IndexBuffer] + Instances = object + pos: GPUArray[Vec3f, VertexBuffer] + scale: GPUArray[float32, VertexBuffer] + var tri = TriangleMesh( + position: asGPUArray([NewVec3f(-0.5, -0.5), NewVec3f(0, 0.5), NewVec3f(0.5, -0.5)], VertexBuffer), + color: asGPUArray([NewVec3f(0, 0, 1), NewVec3f(0, 1, 0), NewVec3f(1, 0, 0)], VertexBuffer), + ) + var quad = QuadMesh( + position: asGPUArray([NewVec3f(-0.3, -0.3), NewVec3f(-0.3, 0.3), NewVec3f(0.3, 0.3), NewVec3f(0.3, -0.3)], VertexBuffer), + indices: asGPUArray([0'u16, 1'u16, 2'u16, 2'u16, 3'u16, 0'u16], IndexBuffer), + color: asGPUArray([NewVec3f(1, 1, 1), NewVec3f(1, 1, 1), NewVec3f(1, 1, 1), NewVec3f(1, 1, 1)], VertexBuffer), + ) + + var instancesA: Instances + for n in 1..100: + instancesA.pos.data.add NewVec3f(rand(-0.8'f32 .. 0.8'f32), rand(-0.8'f32 .. 0'f32)) + instancesA.scale.data.add rand(0.3'f32 .. 0.4'f32) + var instancesB: Instances + for n in 1..100: + instancesB.pos.data.add NewVec3f(rand(-0.8'f32 .. 0.8'f32), rand(0'f32 .. 0.8'f32)) + instancesB.scale.data.add rand(0.1'f32 .. 0.2'f32) + + AssignBuffers(renderdata, tri) + AssignBuffers(renderdata, quad) + AssignBuffers(renderdata, instancesA) + AssignBuffers(renderdata, instancesB) + renderdata.FlushAllMemory() + + var pipeline = CreatePipeline[SomeShader](renderPass = mainRenderpass, samples = swapchain.samples) + + var c = 0 + while UpdateInputs() and c < nFrames: + WithNextFrame(swapchain, framebuffer, commandbuffer): + WithRenderPass(mainRenderpass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): + WithPipeline(commandbuffer, pipeline): + Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad, instances = instancesA) + Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = quad, instances = instancesB) + Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = tri, instances = instancesA) + Render(commandbuffer = commandbuffer, pipeline = pipeline, mesh = tri, instances = instancesB) + inc c + + # cleanup + checkVkResult vkDeviceWaitIdle(vulkan.device) + DestroyPipeline(pipeline) + DestroyRenderData(renderdata) + +proc test_03_global_descriptorset(nFrames: int) = + var renderdata = InitRenderData() - test_01_gl_triangle() + type + RenderSettings = object + gamma: float32 + FirstDS = object + settings: GPUValue[RenderSettings, UniformBuffer] + QuadShader = object + position {.VertexAttribute.}: Vec3f + color {.VertexAttribute.}: Vec3f + fragmentColor {.Pass.}: Vec3f + outColor {.ShaderOutput.}: Vec4f + firstDS: DescriptorSet[FirstDS, First] + # code + vertexCode: string = """void main() { + fragmentColor = vec3(pow(color.r, settings.gamma), pow(color.g, settings.gamma), pow(color.b, settings.gamma)); + gl_Position = vec4(position, 1);}""" + fragmentCode: string = """void main() { + outColor = vec4(fragmentColor, 1);}""" + QuadMesh = object + position: GPUArray[Vec3f, VertexBuffer] + color: GPUArray[Vec3f, VertexBuffer] + indices: GPUArray[uint16, IndexBuffer] + var quad = QuadMesh( + position: asGPUArray([NewVec3f(-0.3, -0.3), NewVec3f(-0.3, 0.3), NewVec3f(0.3, 0.3), NewVec3f(0.3, -0.3)], VertexBuffer), + indices: asGPUArray([0'u16, 1'u16, 2'u16, 2'u16, 3'u16, 0'u16], IndexBuffer), + color: asGPUArray([NewVec3f(1, 1, 1), NewVec3f(1, 1, 1), NewVec3f(1, 1, 1), NewVec3f(1, 1, 1)], VertexBuffer), + ) + var firstDs = DescriptorSet[FirstDS, First]( + data: FirstDS( + settings: asGPUValue(RenderSettings( + gamma: 1.0'f32 + ), UniformBuffer) + ) + ) + AssignBuffers(renderdata, quad) + AssignBuffers(renderdata, firstDs) + renderdata.FlushAllMemory() + + var pipeline = CreatePipeline[QuadShader](renderPass = mainRenderpass, samples = swapchain.samples) + + var c = 0 + while UpdateInputs() and c < nFrames: + WithNextFrame(swapchain, framebuffer, commandbuffer): + WithBind(commandbuffer, firstDs, pipeline, swapchain.currentFiF): + WithRenderPass(mainRenderpass, framebuffer, commandbuffer, swapchain.width, swapchain.height, NewVec4f(0, 0, 0, 0)): + WithPipeline(commandbuffer, pipeline): + Render(commandbuffer = commandbuffer, pipeline = pipeline, firstSet = firstDs, mesh = quad) + inc c + + # cleanup checkVkResult vkDeviceWaitIdle(vulkan.device) - vkDestroyRenderPass(vulkan.device, mainRenderpass, nil) - DestroySwapchain(swapchain) + DestroyPipeline(pipeline) + DestroyRenderData(renderdata) + +when isMainModule: + var nFrames = 100 + InitVulkan() + + # test normal + block: + mainRenderpass = CreatePresentationRenderPass() + swapchain = InitSwapchain(renderpass = mainRenderpass).get() + + # tests a simple triangle with minimalistic shader and vertex format + test_01_triangle(nFrames) + + # tests instanced triangles and quads, mixing meshes and instances + test_02_triangle_quad_instanced(nFrames) + + # tests + test_03_global_descriptorset(nFrames) + + checkVkResult vkDeviceWaitIdle(vulkan.device) + vkDestroyRenderPass(vulkan.device, mainRenderpass, nil) + DestroySwapchain(swapchain) + + # test MSAA + block: + mainRenderpass = CreatePresentationRenderPass(samples = VK_SAMPLE_COUNT_4_BIT) + swapchain = InitSwapchain(renderpass = mainRenderpass, samples = VK_SAMPLE_COUNT_4_BIT).get() + + test_01_triangle(nFrames) + + checkVkResult vkDeviceWaitIdle(vulkan.device) + vkDestroyRenderPass(vulkan.device, mainRenderpass, nil) + DestroySwapchain(swapchain) + DestroyVulkan()