diff --git a/samples/mesh_shaders/mesh_shaders.c b/samples/mesh_shaders/mesh_shaders.c new file mode 100644 index 0000000..20e98c3 --- /dev/null +++ b/samples/mesh_shaders/mesh_shaders.c @@ -0,0 +1,372 @@ +#include +#include +#include +#include + +#define GLFW_INCLUDE_NONE +#include + +evstring PROJECT_NAME = evstr("mesh_triangle"); + +char vertexShaderBytes[] = { +#embed "shaders/mesh_triangle.vert" + ,'\0' +}; +char fragmentShaderBytes[] = { +#embed "shaders/mesh_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_KHR_maintenance5"), + evstr("VK_EXT_descriptor_heap"), + evstr(VK_EXT_SHADER_OBJECT_EXTENSION_NAME), + evstr(VK_KHR_SHADER_UNTYPED_POINTERS_EXTENSION_NAME), + }), + }); + + 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("mesh_triangle.vert"),vertexShaderText, VK_SHADER_STAGE_VERTEX_BIT); + evkShader fragShader = evkInitShaderFromString(device, compiler, evstr("mesh_triangle.frag"), fragmentShaderText, VK_SHADER_STAGE_FRAGMENT_BIT); + + 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}), + ); + + evkBuffer resourceDescriptorHeap = evkCreateBuffer(&device, (evkBufferCreateInfo) { + .sizeInBytes = 16 * 4, // bufferDescriptorSize is 16 on my current device + .usage = VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + .exclusive = true, + .allocationCreateInfo = { + .allocationFlags = EVK_GPU_ALLOCATION_CREATE_MAPPED_BIT | EVK_GPU_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, + .allocator = allocator, + } + }); + + + 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* 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); + + VkResourceDescriptorInfoEXT resource = { + .sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT, + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .data.pAddressRange = &uniBuf.addressRange + }; + + VkHostAddressRangeEXT descriptor = { + .address = resourceDescriptorHeap.allocData.allocationInfo.vma.pMappedData, + .size = 16 + }; + + vkWriteResourceDescriptorsEXT(device.vk, 1, &resource, &descriptor); + + 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); + + vkCmdBindResourceHeapEXT(cmdbuf.vk, &(VkBindHeapInfoEXT) { + .sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT, + .heapRange = resourceDescriptorHeap.addressRange, + }); + + 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/mesh_shaders/meson.build b/samples/mesh_shaders/meson.build new file mode 100644 index 0000000..aa1dd70 --- /dev/null +++ b/samples/mesh_shaders/meson.build @@ -0,0 +1,10 @@ + +executable( + 'mesh_shaders', + 'mesh_shaders.c', + dependencies: [ + dependency('evk'), + dependency('glfw3'), + ], + c_args: evk_c_args, +) \ No newline at end of file diff --git a/samples/mesh_shaders/shaders/mesh_triangle.frag b/samples/mesh_shaders/shaders/mesh_triangle.frag new file mode 100644 index 0000000..7e97017 --- /dev/null +++ b/samples/mesh_shaders/shaders/mesh_triangle.frag @@ -0,0 +1,10 @@ +#version 450 +#pragma shader_stage(fragment) + +#extension GL_EXT_descriptor_heap: require + +layout(location = 0) out vec4 outColor; + +void main() { + outColor = vec4(0.f, 1.f, 0.f, 1.f); +} \ No newline at end of file diff --git a/samples/mesh_shaders/shaders/mesh_triangle.vert b/samples/mesh_shaders/shaders/mesh_triangle.vert new file mode 100644 index 0000000..8a1ad24 --- /dev/null +++ b/samples/mesh_shaders/shaders/mesh_triangle.vert @@ -0,0 +1,12 @@ +#version 450 +#pragma shader_stage(vertex) + +#extension GL_EXT_descriptor_heap: require + +layout(descriptor_heap) uniform data { + vec4 positions[3]; +} vertexData[]; + +void main() { + gl_Position = vertexData[0].positions[gl_VertexIndex]; +} \ No newline at end of file diff --git a/samples/meson.build b/samples/meson.build index cf252dd..f259631 100644 --- a/samples/meson.build +++ b/samples/meson.build @@ -1,2 +1,3 @@ # subdir('basic_triangle') -subdir('descriptor_heap') \ No newline at end of file +# subdir('descriptor_heap') +subdir('mesh_shaders') \ No newline at end of file