From f9332d904739a2412a23c5f4a6128ee7c99c3a9b Mon Sep 17 00:00:00 2001 From: Robear Selwans Date: Sun, 10 May 2026 22:41:11 +0300 Subject: [PATCH] Added descriptor_heap sample --- samples/descriptor_heap/descriptor_heap.c | 371 ++++++++++++++++++ samples/descriptor_heap/meson.build | 10 + .../shaders/heap_triangle.frag | 9 + .../shaders/heap_triangle.vert | 16 + samples/meson.build | 1 + 5 files changed, 407 insertions(+) create mode 100644 samples/descriptor_heap/descriptor_heap.c create mode 100644 samples/descriptor_heap/meson.build create mode 100644 samples/descriptor_heap/shaders/heap_triangle.frag create mode 100644 samples/descriptor_heap/shaders/heap_triangle.vert diff --git a/samples/descriptor_heap/descriptor_heap.c b/samples/descriptor_heap/descriptor_heap.c new file mode 100644 index 0000000..5695019 --- /dev/null +++ b/samples/descriptor_heap/descriptor_heap.c @@ -0,0 +1,371 @@ +#include +#include +#include +#include + +#define GLFW_INCLUDE_NONE +#include + +evstring PROJECT_NAME = evstr("heap_triangle"); + +char vertexShaderBytes[] = { +#embed "shaders/heap_triangle.vert" + ,'\0' +}; +char fragmentShaderBytes[] = { +#embed "shaders/heap_triangle.frag" + ,'\0' +}; + +int main(void) +{ + u32 width = 1280; + u32 height = 800; + + evkInstance instance = evkCreateInstance((evkInstanceCreateInfo){ + .applicationInfo = EV_DEFAULT(evkApplicationInfo), + .layers = svec_init(evstring, { + evstr("VK_LAYER_KHRONOS_validation"), + }), + .extensions = svec_init(evstring, { + evstr("VK_KHR_surface"), +// TODO Get these from GLFW +#if EV_OS_WINDOWS + evstr("VK_KHR_win32_surface"), +#elif EV_OS_LINUX + evstr("VK_KHR_xcb_surface"), +#endif + }), + }); + + if(instance.vk == EV_INVALID(VkInstance)) + { + ev_log_error("Instance creation failed."); + goto InstanceCreationFailed; + } + ev_log_info("Instance was created successfully."); + + evkDevice device = evkCreateDevice((evkDeviceCreateInfo) { + .instance = instance, + // TODO Add a fallback physical device option. + // .physicalDeviceType = VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU, + .physicalDeviceType = VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU, + .queueRequirements = svec_init(evkDeviceQueueRequirement, { + { VK_QUEUE_GRAPHICS_BIT, 1 }, + { VK_QUEUE_COMPUTE_BIT , 1 }, + }), + .deviceExtensions = svec_init(evstring, { + evstr("VK_KHR_swapchain"), + evstr("VK_KHR_dynamic_rendering"), + evstr("VK_KHR_depth_stencil_resolve"), + evstr("VK_KHR_create_renderpass2"), + evstr("VK_KHR_synchronization2"), + evstr("VK_KHR_buffer_device_address"), + evstr("VK_EXT_descriptor_indexing"), + evstr("VK_EXT_descriptor_buffer"), + }), + }); + + if(device.vk == EV_INVALID(VkDevice)) + { + ev_log_error("Couldn't create a VkDevice"); + goto DeviceCreationFailed; + } + ev_log_info("Logical Device created successfully."); + + VkQueue graphicsQueue; + vkGetDeviceQueue(device.vk, device.queueFamilies[VK_QUEUE_GRAPHICS_BIT].familyIndex, 0, &graphicsQueue); + + if (!glfwInit()) + { + ev_log_error("GLFW Initialization failed."); + goto GLFWInitFailed; + } + + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + GLFWwindow* window = glfwCreateWindow(width, height, "evk", NULL, NULL); + if(!window) + { + ev_log_error("Window Creation Failed."); + goto WindowCreationFailed; + } + + VkSurfaceKHR surface; + VkResult err = EVK_CHECK(glfwCreateWindowSurface(instance.vk, window, NULL, &surface)); + if (err) + { + ev_log_error("Surface creation failed."); + goto VKSurfaceCreationFailed; + } + + evkGPUAllocator allocator = evkGPUCreateAllocator(device); + + evkSwapChain swapChain = evkCreateSwapChain((evkSwapChainCreateInfo){ + .device = device, + .surface = surface, + .width = width, + .height = height, + .imageCount = 3, + }); + + evkCommandPool commandPool = evkCreateCommandPool((evkCommandPoolCreateInfo) { + .device = device, + .queueFlags = VK_QUEUE_GRAPHICS_BIT, + .poolFlags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT + }); + + vec(evkCommandBuffer) commandBuffers = evkAllocateCommandBuffers(device, commandPool, vec_len(&swapChain.images), true); + + evkShaderCompiler compiler = evkCreateShaderCompiler(); + + evstring vertexShaderText = evstring_new(vertexShaderBytes); + evstring fragmentShaderText = evstring_new(fragmentShaderBytes); + + evkShader vertShader = evkInitShaderFromString(device, compiler, evstr("heap_triangle.vert"),vertexShaderText); + evkShader fragShader = evkInitShaderFromString(device, compiler, evstr("heap_triangle.frag"), fragmentShaderText); + + evstring_free(vertexShaderText); + evstring_free(fragmentShaderText); + + evkDestroyShaderCompiler(compiler); + + evkColorAttachment colorAttachment0 = { + swapChain.surfaceFormat.format, + EV_DEFAULT(VkPipelineColorBlendAttachmentState) + }; + + // TODO Get this from shader reflection data + // evkDescriptorSetLayout setLayout_0 = evkCreateDescriptorSetLayout( + // &device, svec_init(evkDescriptorBinding, { + // { + // .name = evstr("positions"), + // .binding = 0, + // .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + // .descriptorCount = 1, + // .stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS, + // }, + // }) + // ); + // evkDescriptorSetLayout setLayout_0 = evkCreateDescriptorSetLayoutFromBindings(&device, vertShader.reflect.bindings); + + evkDescriptorSetLayout setLayout_0 = evkCreateDescriptorSetLayoutFromShaders(&device, svec_init(evkShader, {vertShader, fragShader})); + + evkPipelineCreateInfo pipelineCreateInfo = EV_DEFAULT(evkPipelineCreateInfo, + dynamicStates = svec_init(VkDynamicState, { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }), + shaderStages = svec_init(evkShader, { + vertShader, + fragShader, + }), + colorAttachments = svec_init(evkColorAttachment, { + colorAttachment0, + }), + viewportCountOverride = 1, + + vertexBufferLayouts = svec_init(evkVertexBufferLayout, { + {{ // VB #0 + { EVK_VERTEX_ATTRIBUTE_POSITION, EVK_FMT_FLOAT32, 2 }, + }}, + }), + + setLayouts = svec_init(evkDescriptorSetLayout, {setLayout_0}), + ); + + evkDescriptorSet set_0 = evkCreateDescriptorSet(&(evkDescriptorSetCreateInfo){ + .device = &device, + .allocator = &allocator, + .layout = &setLayout_0 + }); + + + // // if stageflags is 0, it's all graphics + // // if descriptor count is 0, it's 1 + // // if + // evkDescriptorSetLayout set_0 = evkCreateDescriptorSetLayout(&device, svec_init(evkDescriptorBinding, { + // { "positions", VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER }, + // }) + // ); + + evkBuffer vertBuf = evkCreateBuffer(&device, (evkBufferCreateInfo) { + .queueFamilyIndices = svec_init(u32, {device.queueFamilies[VK_QUEUE_GRAPHICS_BIT].familyIndex}), + .sizeInBytes = 6 * 4, + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + .allocationCreateInfo = { + .allocationFlags = EVK_GPU_ALLOCATION_CREATE_MAPPED_BIT | EVK_GPU_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, + .allocator = allocator, + }, + .exclusive = true, + }); + + evkBuffer uniBuf = evkCreateBuffer(&device, (evkBufferCreateInfo) { + .sizeInBytes = 12 * 4, + .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + .allocationCreateInfo = { + .allocationFlags = EVK_GPU_ALLOCATION_CREATE_MAPPED_BIT | EVK_GPU_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT, + .allocator = allocator, + }, + .exclusive = true, + }); + + float* vert_arr = (float*)vertBuf.allocData.allocationInfo.vma.pMappedData; + vert_arr[0] = 0.0f; vert_arr[1] = -0.5f; + vert_arr[2] = 0.5f; vert_arr[3] = 0.5f; + vert_arr[4] = -0.5f; vert_arr[5] = 0.5f; + + float* uni_arr = (float*)uniBuf.allocData.allocationInfo.vma.pMappedData; + uni_arr[0] = 0.0f; uni_arr[1] = -0.5f; uni_arr[2] = 0.0f; uni_arr[3] = 1.0f; + uni_arr[4] = 0.5f; uni_arr[5] = 0.5f; uni_arr[6] = 0.0f; uni_arr[7] = 1.0f; + uni_arr[8] = -0.5f; uni_arr[9] = 0.5f; uni_arr[10] = 0.0f; uni_arr[11] = 1.0f; + + evkSetDescriptor(&set_0, evstr("vertexData"), &uniBuf); + + VkViewport viewport = EV_DEFAULT(VkViewport, width=width, height=height); + VkRect2D scissor = EV_DEFAULT(VkRect2D,extent.width=width, extent.height=height); + + evkPipeline graphicsPipeline = evkCreatePipeline(device, pipelineCreateInfo); + + u32 imageCount = vec_len(&swapChain.images); + + VkFence drawFence = evkCreateFence(device, false); + vec(VkSemaphore) imageAcquiredSemaphores = vec_init(VkSemaphore); + vec(VkSemaphore) drawFinishedSemaphores = vec_init(VkSemaphore); + for(u32 i = 0; i < imageCount; i++) + { + imageAcquiredSemaphores[i] = evkCreateSemaphore(device); + drawFinishedSemaphores[i] = evkCreateSemaphore(device); + } + // VkSemaphore imageAcquiredSemaphore = evkCreateSemaphore(device); + // VkSemaphore drawFinishedSemaphore = evkCreateSemaphore(device); + + i32 imageIdx = -1; + while(!glfwWindowShouldClose(window)) + { + imageIdx = (imageIdx + 1) % imageCount; + u32 swapChainImageIdx; + EVK_CHECK(vkAcquireNextImageKHR(device.vk, swapChain.vk, UInt64.MAX, imageAcquiredSemaphores[imageIdx], VK_NULL_HANDLE, &swapChainImageIdx)); + + evkCommandBuffer cmdbuf = commandBuffers[imageIdx]; + + VkRenderingAttachmentInfoKHR colorAttachment = EV_DEFAULT(VkRenderingAttachmentInfoKHR, + imageView = swapChain.imageViews[swapChainImageIdx].vk, + loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + storeOp= VK_ATTACHMENT_STORE_OP_STORE, + ); + + VkRenderingInfo renderingInfo = EV_DEFAULT(VkRenderingInfo, + renderArea.extent = ((VkExtent2D){width, height}), + pColorAttachments = &colorAttachment, + colorAttachmentCount = 1, + ); + + evkBeginPrimaryCommandBuffer(&cmdbuf); + { + evkCmdImageBarrier(&cmdbuf, EV_DEFAULT(VkImageMemoryBarrier, + image = swapChain.images[swapChainImageIdx].vk, + newLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR, + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT + )); + + VkDeviceSize offset = 0; + vkCmdBindVertexBuffers(cmdbuf.vk, 0, 1, &vertBuf.vk, &offset); + + evkCmdBindDescriptorSets(&cmdbuf, &graphicsPipeline, svec_init(evkDescriptorSet, { set_0 }), svec_init(u32, { 0 })); + + vkCmdSetScissor(cmdbuf.vk, 0, 1, &scissor); + vkCmdSetViewport(cmdbuf.vk, 0, 1, &viewport); + + vkCmdBeginRenderingKHR(cmdbuf.vk, &renderingInfo); + + evkCmdBindPipeline(&cmdbuf, &graphicsPipeline); + vkCmdDraw(cmdbuf.vk, 3, 1, 0, 0); + + vkCmdEndRenderingKHR(cmdbuf.vk); + + evkCmdImageBarrier(&cmdbuf, EV_DEFAULT(VkImageMemoryBarrier, + image = swapChain.images[swapChainImageIdx].vk, + oldLayout = VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL_KHR, + newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT + )); + } + evkEndCommandBuffer(&cmdbuf); + + VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT}; + + VkSubmitInfo submitInfo = { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pCommandBuffers = &cmdbuf.vk, // TODO This won't work with more than a single cmdbuf + .commandBufferCount = 1, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &imageAcquiredSemaphores[imageIdx], + .pWaitDstStageMask = waitStages, + .signalSemaphoreCount = 1, + .pSignalSemaphores = &drawFinishedSemaphores[swapChainImageIdx], + }; + + vkQueueSubmit(graphicsQueue, 1, &submitInfo, drawFence); + vkWaitForFences(device.vk, 1, &drawFence, VK_TRUE, UInt64.MAX); + vkResetFences(device.vk, 1, &drawFence); + + VkPresentInfoKHR presentInfo = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &drawFinishedSemaphores[swapChainImageIdx], + .swapchainCount = 1, + .pSwapchains = &swapChain.vk, + .pImageIndices = &swapChainImageIdx, + }; + vkQueuePresentKHR(graphicsQueue, &presentInfo); + + vkResetCommandBuffer(cmdbuf.vk,VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT); + + // Main Loop + glfwPollEvents(); + } + + vkQueueWaitIdle(graphicsQueue); + + vkDestroyFence(device.vk, drawFence, NULL); + for(u32 i = 0; i < imageCount; i++) + { + vkDestroySemaphore(device.vk, imageAcquiredSemaphores[i], NULL); + vkDestroySemaphore(device.vk, drawFinishedSemaphores[i], NULL); + } + // vkDestroySemaphore(device.vk, imageAcquiredSemaphore, NULL); + // vkDestroySemaphore(device.vk, drawFinishedSemaphore, NULL); + + evkDestroyPipeline(graphicsPipeline); + + evkDestroyBuffer(vertBuf); + evkDestroyBuffer(uniBuf); + + evkDestroyDescriptorSet(&device, &set_0); + evkDestroyDescriptorSetLayout(&device, &setLayout_0); + + evkDestroyShader(device, vertShader); + evkDestroyShader(device, fragShader); + + evkFreeCommandBuffers(device, commandPool, commandBuffers); + evkDestroyCommandPool(device, commandPool); + + evkGPUDestroyAllocator(allocator); + + evkDestroySwapChain(device, swapChain); + +// SwapchainCreationFailed: + vkDestroySurfaceKHR(instance.vk, swapChain.surface, NULL); + +VKSurfaceCreationFailed: +WindowCreationFailed: + glfwTerminate(); + +GLFWInitFailed: + evkDestroyDevice(device); + +DeviceCreationFailed: + evkDestroyInstance(instance); +InstanceCreationFailed: + return 0; +} \ No newline at end of file diff --git a/samples/descriptor_heap/meson.build b/samples/descriptor_heap/meson.build new file mode 100644 index 0000000..eeb25e9 --- /dev/null +++ b/samples/descriptor_heap/meson.build @@ -0,0 +1,10 @@ + +executable( + 'descriptor_heap', + 'descriptor_heap.c', + dependencies: [ + dependency('evk'), + dependency('glfw3'), + ], + c_args: evk_c_args, +) \ No newline at end of file diff --git a/samples/descriptor_heap/shaders/heap_triangle.frag b/samples/descriptor_heap/shaders/heap_triangle.frag new file mode 100644 index 0000000..4407c21 --- /dev/null +++ b/samples/descriptor_heap/shaders/heap_triangle.frag @@ -0,0 +1,9 @@ +#version 450 + +#pragma shader_stage(fragment) + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(0.f, 1.f, 0.f, 1.f); +} diff --git a/samples/descriptor_heap/shaders/heap_triangle.vert b/samples/descriptor_heap/shaders/heap_triangle.vert new file mode 100644 index 0000000..725a2c1 --- /dev/null +++ b/samples/descriptor_heap/shaders/heap_triangle.vert @@ -0,0 +1,16 @@ +#version 450 +#pragma shader_stage(vertex) + +in layout(location=0) vec2 position; + +layout(set=0, binding=0) uniform data { + vec4 positions[3]; +} vertexData; + +void main() { + // gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + // gl_Position = vec4(position, 0.0, 1.0); + // gl_Position = vec4(inputData.positions[gl_VertexIndex], 0.0, 1.0); + gl_Position = vertexData.positions[gl_VertexIndex]; + // gl_Position = vec4(position, 0.0, 1.0); +} \ No newline at end of file diff --git a/samples/meson.build b/samples/meson.build index 99fd94e..5bd627f 100644 --- a/samples/meson.build +++ b/samples/meson.build @@ -1 +1,2 @@ subdir('basic_triangle') +subdir('descriptor_heap') \ No newline at end of file