From d1d57ab682c191208ad5cc1656e4a9bbe07a4588 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 7 Dec 2025 23:26:21 -0500 Subject: [PATCH 01/13] Big cleanup to utilize C++20 designated initializers https://en.cppreference.com/w/cpp/language/aggregate_initialization.html#Designated_initializers --- vk_descriptors.cpp | 51 +++++------ vk_images.cpp | 91 ++++++++++--------- vk_initializers.cpp | 213 +++++++++++++++++++------------------------- vk_pipelines.cpp | 14 +-- 4 files changed, 170 insertions(+), 199 deletions(-) diff --git a/vk_descriptors.cpp b/vk_descriptors.cpp index bfad725..a8aca8b 100644 --- a/vk_descriptors.cpp +++ b/vk_descriptors.cpp @@ -3,10 +3,11 @@ void DescriptorLayoutBuilder::add_binding(uint32_t binding, VkDescriptorType type) { - VkDescriptorSetLayoutBinding newbind {}; - newbind.binding = binding; - newbind.descriptorCount = 1; - newbind.descriptorType = type; + VkDescriptorSetLayoutBinding newbind{ + .binding = binding, + .descriptorType = type, + .descriptorCount = 1, + }; bindings.push_back(newbind); } @@ -25,18 +26,16 @@ VkDescriptorSetLayout DescriptorLayoutBuilder::build(VkDevice device, b.stageFlags |= shaderStages; } - VkDescriptorSetLayoutCreateInfo info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO + VkDescriptorSetLayoutCreateInfo info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = pNext, + .flags = flags, + .bindingCount = (uint32_t)bindings.size(), + .pBindings = bindings.data(), }; - info.pNext = pNext; - - info.pBindings = bindings.data(); - info.bindingCount = (uint32_t)bindings.size(); - info.flags = flags; VkDescriptorSetLayout set; - VK_CHECK(vkCreateDescriptorSetLayout(device, - &info, nullptr, &set)); + VK_CHECK(vkCreateDescriptorSetLayout(device, &info, nullptr, &set)); return set; } @@ -46,19 +45,19 @@ void DescriptorAllocator::init_pool(VkDevice device, uint32_t maxSets, { std::vector poolSizes; for(PoolSizeRatio ratio : poolRatios) { - poolSizes.push_back(VkDescriptorPoolSize{ + poolSizes.push_back({ .type = ratio.type, .descriptorCount = uint32_t(ratio.ratio * maxSets) }); } - VkDescriptorPoolCreateInfo pool_info = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO + VkDescriptorPoolCreateInfo pool_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = 0, + .maxSets = maxSets, + .poolSizeCount = (uint32_t)poolSizes.size(), + .pPoolSizes = poolSizes.data(), }; - pool_info.flags = 0; - pool_info.maxSets = maxSets; - pool_info.poolSizeCount = (uint32_t)poolSizes.size(); - pool_info.pPoolSizes = poolSizes.data(); vkCreateDescriptorPool(device, &pool_info, nullptr, &pool); } @@ -75,15 +74,13 @@ void DescriptorAllocator::destroy_pool(VkDevice device) VkDescriptorSet DescriptorAllocator::allocate(VkDevice device, VkDescriptorSetLayout layout) { - VkDescriptorSetAllocateInfo allocInfo = { - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO + VkDescriptorSetAllocateInfo allocInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = pool, + .descriptorSetCount = 1, + .pSetLayouts = &layout, }; - allocInfo.pNext = nullptr; - allocInfo.descriptorPool = pool; - allocInfo.descriptorSetCount = 1; - allocInfo.pSetLayouts = &layout; - VkDescriptorSet ds; VK_CHECK(vkAllocateDescriptorSets(device, &allocInfo, &ds)); diff --git a/vk_images.cpp b/vk_images.cpp index a457d61..c97537e 100644 --- a/vk_images.cpp +++ b/vk_images.cpp @@ -3,28 +3,25 @@ void vkutil::transition_image(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout newLayout) { - VkImageMemoryBarrier2 imageBarrier{}; - imageBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2; - imageBarrier.pNext = nullptr; - - imageBarrier.srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; - imageBarrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; - imageBarrier.dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT; - imageBarrier.dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT; - - - imageBarrier.oldLayout = currentLayout; - imageBarrier.newLayout = newLayout; - VkImageAspectFlags aspectMask = (newLayout == VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL) ? VK_IMAGE_ASPECT_DEPTH_BIT : VK_IMAGE_ASPECT_COLOR_BIT; - imageBarrier.subresourceRange = vkinit::image_subresource_range(aspectMask); - imageBarrier.image = image; - VkDependencyInfo depInfo{}; - depInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; - depInfo.pNext = nullptr; - depInfo.imageMemoryBarrierCount = 1; - depInfo.pImageMemoryBarriers = &imageBarrier; + VkImageMemoryBarrier2 imageBarrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2, + .srcStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, + .dstStageMask = VK_PIPELINE_STAGE_2_ALL_COMMANDS_BIT, + .dstAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT | VK_ACCESS_2_MEMORY_READ_BIT, + .oldLayout = currentLayout, + .newLayout = newLayout, + .image = image, + .subresourceRange = vkinit::image_subresource_range(aspectMask), + }; + + VkDependencyInfo depInfo{ + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .imageMemoryBarrierCount = 1, + .pImageMemoryBarriers = &imageBarrier, + }; vkCmdPipelineBarrier2(cmd, &depInfo); } @@ -33,39 +30,45 @@ void vkutil::copy_image_to_image(VkCommandBuffer cmd, VkImage source, VkImage de { VkImageBlit2 blitRegion{ .sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, - .pNext = nullptr }; - blitRegion.srcOffsets[1].x = srcSize.width; - blitRegion.srcOffsets[1].y = srcSize.height; - blitRegion.srcOffsets[1].z = 1; + blitRegion.srcSubresource = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }; - blitRegion.dstOffsets[1].x = dstSize.width; - blitRegion.dstOffsets[1].y = dstSize.height; - blitRegion.dstOffsets[1].z = 1; + blitRegion.dstSubresource = { + .aspectMask=VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel=0, + .baseArrayLayer=0, + .layerCount=1, + }; - blitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; - blitRegion.srcSubresource.baseArrayLayer = 0; - blitRegion.srcSubresource.layerCount = 1; - blitRegion.srcSubresource.mipLevel = 0; + blitRegion.srcOffsets[1] = { + .x = int32_t(srcSize.width), + .y = int32_t(srcSize.height), + .z = 1, + }; - blitRegion.dstSubresource.aspectMask=VK_IMAGE_ASPECT_COLOR_BIT; - blitRegion.dstSubresource.baseArrayLayer=0; - blitRegion.dstSubresource.layerCount=1; - blitRegion.dstSubresource.mipLevel=0; + blitRegion.dstOffsets[1] = { + .x = int32_t(dstSize.width), + .y = int32_t(dstSize.height), + .z = 1, + }; VkBlitImageInfo2 blitInfo{ - .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, .pNext = nullptr + .sType = VK_STRUCTURE_TYPE_BLIT_IMAGE_INFO_2, + .srcImage = source, + .srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .dstImage = destination, + .dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + .regionCount = 1, + .pRegions = &blitRegion, + .filter = VK_FILTER_LINEAR, }; - blitInfo.dstImage = destination; - blitInfo.dstImageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; - blitInfo.srcImage = source; - blitInfo.srcImageLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; - blitInfo.filter = VK_FILTER_LINEAR; - blitInfo.regionCount = 1; - blitInfo.pRegions = &blitRegion; - vkCmdBlitImage2(cmd, &blitInfo); } diff --git a/vk_initializers.cpp b/vk_initializers.cpp index 3c30974..f54839b 100644 --- a/vk_initializers.cpp +++ b/vk_initializers.cpp @@ -3,163 +3,137 @@ VkCommandPoolCreateInfo vkinit::command_pool_create_info(uint32_t queueFamilyIndex, VkCommandPoolCreateFlags flags /*= 0*/) { - VkCommandPoolCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - info.pNext = nullptr; - info.queueFamilyIndex = queueFamilyIndex; - info.flags = flags; - return info; + return { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .flags = flags, + .queueFamilyIndex = queueFamilyIndex, + }; } VkCommandBufferAllocateInfo vkinit::command_buffer_allocate_info( VkCommandPool pool, uint32_t count /*= 1*/) { - VkCommandBufferAllocateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; - info.pNext = nullptr; - - info.commandPool = pool; - info.commandBufferCount = count; - info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; - return info; + return { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .commandPool = pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = count, + }; } VkFenceCreateInfo vkinit::fence_create_info(VkFenceCreateFlags flags /*=0*/) { - VkFenceCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; - info.pNext = nullptr; - info.flags = flags; - return info; + return { + .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, + .flags = flags + }; } VkSemaphoreCreateInfo vkinit::semaphore_create_info(VkSemaphoreCreateFlags flags/*=0*/) { - VkSemaphoreCreateInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - info.pNext = nullptr; - info.flags = flags; - return info; + return { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .flags = flags, + }; } VkCommandBufferBeginInfo vkinit::command_buffer_begin_info(VkCommandBufferUsageFlags flags /*= 0*/) { - VkCommandBufferBeginInfo info = {}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - info.pNext = nullptr; - - info.pInheritanceInfo = nullptr; - info.flags = flags; - return info; + return { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .flags = flags, + .pInheritanceInfo = nullptr, + }; } VkImageSubresourceRange vkinit::image_subresource_range(VkImageAspectFlags aspectMask) { - VkImageSubresourceRange subImage {}; - subImage.aspectMask = aspectMask; - subImage.baseMipLevel = 0; - subImage.levelCount = VK_REMAINING_MIP_LEVELS; - subImage.baseArrayLayer = 0; - subImage.layerCount = VK_REMAINING_ARRAY_LAYERS; - - return subImage; + return { + .aspectMask = aspectMask, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }; } VkSemaphoreSubmitInfo vkinit::semaphore_submit_info(VkPipelineStageFlags2 stageMask, VkSemaphore semaphore) { - VkSemaphoreSubmitInfo submitInfo{}; - submitInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO; - submitInfo.pNext = nullptr; - submitInfo.semaphore = semaphore; - submitInfo.stageMask = stageMask; - submitInfo.deviceIndex = 0; - submitInfo.value = 1; - - return submitInfo; + return { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .semaphore = semaphore, + .value = 1, + .stageMask = stageMask, + .deviceIndex = 0, + }; } VkCommandBufferSubmitInfo vkinit::command_buffer_submit_info(VkCommandBuffer cmd) { - VkCommandBufferSubmitInfo info{}; - info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO; - info.pNext = nullptr; - info.commandBuffer = cmd; - info.deviceMask = 0; - - return info; + return { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO, + .commandBuffer = cmd, + .deviceMask = 0, + }; } VkSubmitInfo2 vkinit::submit_info(VkCommandBufferSubmitInfo* cmd, VkSemaphoreSubmitInfo* signalSemaphoreInfo, VkSemaphoreSubmitInfo* waitSemaphoreInfo) { - VkSubmitInfo2 info = {}; - info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2; - info.pNext = nullptr; - - info.waitSemaphoreInfoCount = waitSemaphoreInfo == nullptr ? 0 : 1; - info.pWaitSemaphoreInfos = waitSemaphoreInfo; - - info.signalSemaphoreInfoCount = signalSemaphoreInfo == nullptr ? 0 : 1; - info.pSignalSemaphoreInfos = signalSemaphoreInfo; - - info.commandBufferInfoCount = 1; - info.pCommandBufferInfos = cmd; - - return info; + return { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .waitSemaphoreInfoCount = (waitSemaphoreInfo == nullptr ? 0u : 1u), + .pWaitSemaphoreInfos = waitSemaphoreInfo, + .commandBufferInfoCount = 1, + .pCommandBufferInfos = cmd, + .signalSemaphoreInfoCount = (signalSemaphoreInfo == nullptr ? 0u : 1u), + .pSignalSemaphoreInfos = signalSemaphoreInfo, + }; } VkImageCreateInfo vkinit::image_create_info(VkFormat format, VkImageUsageFlags usageFlags, VkExtent3D extent) { - VkImageCreateInfo info{}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; - info.pNext = nullptr; - - info.imageType = VK_IMAGE_TYPE_2D; - - info.format = format; - info.extent = extent; - - info.mipLevels = 1; - info.arrayLayers = 1; - - info.samples = VK_SAMPLE_COUNT_1_BIT; - - info.tiling = VK_IMAGE_TILING_OPTIMAL; - info.usage = usageFlags; - - return info; + return { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .imageType = VK_IMAGE_TYPE_2D, + .format = format, + .extent = extent, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = usageFlags, + }; } VkImageViewCreateInfo vkinit::imageview_create_info(VkFormat format, VkImage image, VkImageAspectFlags aspectFlags) { - VkImageViewCreateInfo info{}; - info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; - info.pNext = nullptr; - - info.viewType = VK_IMAGE_VIEW_TYPE_2D; - info.image = image; - info.format = format; - info.subresourceRange.baseMipLevel = 0; - info.subresourceRange.levelCount = 1; - info.subresourceRange.baseArrayLayer = 0; - info.subresourceRange.layerCount = 1; - info.subresourceRange.aspectMask = aspectFlags; - - return info; + return { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .image = image, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = format, + .subresourceRange = { + .aspectMask = aspectFlags, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + } + }; } VkRenderingAttachmentInfo vkinit::attachment_info( VkImageView view, VkClearValue* clear ,VkImageLayout layout /*= VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL*/) { - VkRenderingAttachmentInfo colorAttachment {}; - colorAttachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - colorAttachment.pNext = nullptr; - - colorAttachment.imageView = view; - colorAttachment.imageLayout = layout; - colorAttachment.loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD; - colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + VkRenderingAttachmentInfo colorAttachment{ + .sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO, + .imageView = view, + .imageLayout = layout, + .loadOp = clear ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_LOAD, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + }; if (clear) { colorAttachment.clearValue = *clear; @@ -172,16 +146,13 @@ VkRenderingAttachmentInfo vkinit::attachment_info( VkRenderingInfo vkinit::rendering_info(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment, VkRenderingAttachmentInfo* depthAttachment) { - VkRenderingInfo renderInfo {}; - renderInfo.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; - renderInfo.pNext = nullptr; - - renderInfo.renderArea = VkRect2D { VkOffset2D { 0, 0 }, renderExtent }; - renderInfo.layerCount = 1; - renderInfo.colorAttachmentCount = 1; - renderInfo.pColorAttachments = colorAttachment; - renderInfo.pDepthAttachment = depthAttachment; - renderInfo.pStencilAttachment = nullptr; - - return renderInfo; + return { + .sType = VK_STRUCTURE_TYPE_RENDERING_INFO, + .renderArea = VkRect2D { VkOffset2D { 0, 0 }, renderExtent }, + .layerCount = 1, + .colorAttachmentCount = 1, + .pColorAttachments = colorAttachment, + .pDepthAttachment = depthAttachment, + .pStencilAttachment = nullptr, + }; } diff --git a/vk_pipelines.cpp b/vk_pipelines.cpp index 4067f1d..acecc80 100644 --- a/vk_pipelines.cpp +++ b/vk_pipelines.cpp @@ -23,13 +23,13 @@ bool vkutil::load_shader_module(const char* filePath, file.close(); - VkShaderModuleCreateInfo createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - createInfo.pNext = nullptr; - - // codeSize has to be in bytes - createInfo.codeSize = buffer.size() * sizeof(uint32_t); - createInfo.pCode = buffer.data(); + VkShaderModuleCreateInfo createInfo = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .pNext = nullptr, + // codeSize has to be in bytes + .codeSize = buffer.size() * sizeof(uint32_t), + .pCode = buffer.data(), + }; VkShaderModule shaderModule; if(vkCreateShaderModule(device, &createInfo, nullptr, &shaderModule) != VK_SUCCESS) { From 4f7ab6db68d1bf839ecb7a9fb3d58afc77b00a0d Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 7 Dec 2025 23:56:47 -0500 Subject: [PATCH 02/13] Cleaned up the struct inits in the engine too. --- vk_engine.cpp | 249 ++++++++++++++++++++++++++------------------------ 1 file changed, 130 insertions(+), 119 deletions(-) diff --git a/vk_engine.cpp b/vk_engine.cpp index da914ab..6e9dbc8 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -150,16 +150,15 @@ void VulkanEngine::draw() // this will put the image we just rendered to into the visible window. // we want to wait on the _renderSemaphore for that, // as its necessary that drawing commands have finished before the image is displayed to the user - VkPresentInfoKHR presentInfo{}; - presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; - presentInfo.pNext = nullptr; - presentInfo.pSwapchains = &_swapchain; - presentInfo.swapchainCount = 1; - - presentInfo.pWaitSemaphores = &get_current_frame()._renderSemaphore; - presentInfo.waitSemaphoreCount = 1; - - presentInfo.pImageIndices = &swapchainImageIndex; + VkPresentInfoKHR presentInfo{ + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = nullptr, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &get_current_frame()._renderSemaphore, + .swapchainCount = 1, + .pSwapchains = &_swapchain, + .pImageIndices = &swapchainImageIndex, + }; VK_CHECK(vkQueuePresentKHR(_graphicsQueue, &presentInfo)); @@ -251,18 +250,19 @@ void VulkanEngine::init_vulkan() { SDL_Vulkan_CreateSurface(_window, _instance, &_surface); //vulkan 1.3 features - VkPhysicalDeviceVulkan13Features features13{}; - - features13.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES; - features13.dynamicRendering = true; - features13.synchronization2 = true; - + VkPhysicalDeviceVulkan13Features features13{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES, + .synchronization2 = true, + .dynamicRendering = true, + }; + //vulkan 1.2 features - VkPhysicalDeviceVulkan12Features features12{}; - features12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; - features12.bufferDeviceAddress = true; - features12.descriptorIndexing = true; - + VkPhysicalDeviceVulkan12Features features12{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .descriptorIndexing = true, + .bufferDeviceAddress = true, + }; + // use vkbootstrap to select a gpu // We want a gpu that can write to the SDL surface vkb::PhysicalDeviceSelector selector{ vkb_inst }; @@ -284,11 +284,13 @@ void VulkanEngine::init_vulkan() { _graphicsQueueFamily = vkbDevice.get_queue_index(vkb::QueueType::graphics).value(); // initialize the memory allocator - VmaAllocatorCreateInfo allocatorInfo{}; - allocatorInfo.physicalDevice = _chosenGPU; - allocatorInfo.device = _device; - allocatorInfo.instance = _instance; - allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT; + VmaAllocatorCreateInfo allocatorInfo{ + .flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT, + .physicalDevice = _chosenGPU, + .device = _device, + .instance = _instance, + }; + vmaCreateAllocator(&allocatorInfo, &_allocator); _mainDeletionQueue.push_function([&]() { @@ -339,18 +341,19 @@ void VulkanEngine::init_swapchain() { _drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT; _drawImage.imageExtent = drawImageExtent; - VkImageUsageFlags drawImageUsages{}; - drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; - drawImageUsages |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; - drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT; - drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + VkImageUsageFlags drawImageUsages = + VK_IMAGE_USAGE_TRANSFER_SRC_BIT + | VK_IMAGE_USAGE_TRANSFER_DST_BIT + | VK_IMAGE_USAGE_STORAGE_BIT + | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; VkImageCreateInfo rimg_info = vkinit::image_create_info(_drawImage.imageFormat, drawImageUsages, drawImageExtent); - VmaAllocationCreateInfo rimg_allocinfo{}; - rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - rimg_allocinfo.requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - + VmaAllocationCreateInfo rimg_allocinfo{ + .usage = VMA_MEMORY_USAGE_GPU_ONLY, + .requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), + }; + //allocate and create the image vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &_drawImage.image, &_drawImage.allocation, nullptr); @@ -443,19 +446,19 @@ void VulkanEngine::init_descriptors() { //allocate a descriptor set for our draw image _drawImageDescriptors = globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout); - VkDescriptorImageInfo imgInfo{}; - imgInfo.imageLayout = VK_IMAGE_LAYOUT_GENERAL; - imgInfo.imageView = _drawImage.imageView; - - VkWriteDescriptorSet drawImageWrite = {}; - drawImageWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; - drawImageWrite.pNext = nullptr; - - drawImageWrite.dstBinding = 0; - drawImageWrite.dstSet = _drawImageDescriptors; - drawImageWrite.descriptorCount = 1; - drawImageWrite.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; - drawImageWrite.pImageInfo = &imgInfo; + VkDescriptorImageInfo imgInfo{ + .imageView = _drawImage.imageView, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + VkWriteDescriptorSet drawImageWrite = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = _drawImageDescriptors, + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, + .pImageInfo = &imgInfo, + }; vkUpdateDescriptorSets(_device, 1, &drawImageWrite, 0, nullptr), @@ -474,20 +477,20 @@ void VulkanEngine::init_pipelines() void VulkanEngine::init_background_pipelines() { - VkPipelineLayoutCreateInfo computeLayout{}; - computeLayout.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - computeLayout.pNext = nullptr; - computeLayout.pSetLayouts = &_drawImageDescriptorLayout; - computeLayout.setLayoutCount = 1; - - VkPushConstantRange pushConstant{}; - pushConstant.offset = 0; - pushConstant.size = sizeof(ComputePushConstants) ; - pushConstant.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT; - - computeLayout.pPushConstantRanges = &pushConstant; - computeLayout.pushConstantRangeCount = 1; - + VkPushConstantRange pushConstant{ + .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, + .offset = 0, + .size = sizeof(ComputePushConstants) , + }; + + VkPipelineLayoutCreateInfo computeLayout{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .setLayoutCount = 1, + .pSetLayouts = &_drawImageDescriptorLayout, + .pushConstantRangeCount = 1, + .pPushConstantRanges = &pushConstant, + }; + VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout)); VkShaderModule gradientShader; @@ -497,40 +500,42 @@ void VulkanEngine::init_background_pipelines() VkShaderModule skyShader; good = vkutil::load_shader_module("sky.comp.spv", _device, &skyShader); - VkPipelineShaderStageCreateInfo stageinfo{}; - stageinfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - stageinfo.pNext = nullptr; - stageinfo.stage = VK_SHADER_STAGE_COMPUTE_BIT; - stageinfo.module = gradientShader; - stageinfo.pName = "main"; - - VkComputePipelineCreateInfo computePipelineCreateInfo{}; - computePipelineCreateInfo.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO; - computePipelineCreateInfo.pNext = nullptr; - computePipelineCreateInfo.layout = _gradientPipelineLayout; - computePipelineCreateInfo.stage = stageinfo; - - ComputeEffect gradient; - gradient.layout = _gradientPipelineLayout; - gradient.name = "gradient"; - gradient.data = {}; - gradient.data.data1 = glm::vec4(1, 0, 0, 1); - gradient.data.data2 = glm::vec4(0, 0, 1, 1); + VkPipelineShaderStageCreateInfo stageinfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = gradientShader, + .pName = "main", + }; + + VkComputePipelineCreateInfo computePipelineCreateInfo{ + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .stage = stageinfo, + .layout = _gradientPipelineLayout, + }; + + ComputeEffect gradient{ + .name = "gradient", + .layout = _gradientPipelineLayout, + .data = { + .data1 = glm::vec4(1, 0, 0, 1), + .data2 = glm::vec4(0, 0, 1, 1), + }, + }; + VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &gradient.pipeline)); // change the shader module only to create the sky computePipelineCreateInfo.stage.module = skyShader; - ComputeEffect sky; - sky.layout = _gradientPipelineLayout; - sky.name = "sky"; - sky.data = {}; - // default sky - sky.data.data1 = glm::vec4(0.1, 0.2, 0.4, 0.97); + ComputeEffect sky{ + .name = "sky", + .layout = _gradientPipelineLayout, + .data{.data1 = glm::vec4(0.1, 0.2, 0.4, 0.97)}, + }; + VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &sky.pipeline)); - backgroundEffects.push_back(gradient); backgroundEffects.push_back(sky); @@ -574,25 +579,28 @@ void VulkanEngine::init_imgui() // 1: create descriptor pool for IMGUI // the size of the pool is very oversize, but it's copied from imgui demo // itself. - VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } }; + VkDescriptorPoolSize pool_sizes[] = { + { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } + }; - VkDescriptorPoolCreateInfo pool_info{}; - pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; - pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; - pool_info.maxSets = 1000; - pool_info.poolSizeCount = (uint32_t)std::size(pool_sizes); - pool_info.pPoolSizes = pool_sizes; - + VkDescriptorPoolCreateInfo pool_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = 1000, + .poolSizeCount = (uint32_t)std::size(pool_sizes), + .pPoolSizes = pool_sizes, + }; + VkDescriptorPool imguiPool; VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &imguiPool)); @@ -600,21 +608,24 @@ void VulkanEngine::init_imgui() ImGui::CreateContext(); ImGui_ImplSDL2_InitForVulkan(_window); - ImGui_ImplVulkan_InitInfo init_info{}; - init_info.Instance = _instance; - init_info.PhysicalDevice = _chosenGPU; - init_info.Device = _device; - init_info.Queue = _graphicsQueue; - init_info.DescriptorPool = imguiPool; - init_info.MinImageCount = 3; - init_info.ImageCount = 3; - init_info.UseDynamicRendering = true; - init_info.PipelineRenderingCreateInfo = {.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO}; - init_info.PipelineRenderingCreateInfo.colorAttachmentCount = 1; - init_info.PipelineRenderingCreateInfo.pColorAttachmentFormats = &_swapchainImageFormat; + ImGui_ImplVulkan_InitInfo init_info{ + .Instance = _instance, + .PhysicalDevice = _chosenGPU, + .Device = _device, + .Queue = _graphicsQueue, + .DescriptorPool = imguiPool, + .MinImageCount = 3, + .ImageCount = 3, + .MSAASamples = VK_SAMPLE_COUNT_1_BIT, + .UseDynamicRendering = true, + .PipelineRenderingCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &_swapchainImageFormat, + }, + }; - init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; ImGui_ImplVulkan_Init(&init_info); ImGui_ImplVulkan_CreateFontsTexture(); From 35198bce6bdb4b389c1c0742d82430e168463f3f Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Mon, 8 Dec 2025 00:58:09 -0500 Subject: [PATCH 03/13] Pulled the gui out into VkGUI to separate it out. --- meson.build | 1 + vk_engine.cpp | 155 +++----------------------------------------------- vk_engine.h | 22 +------ vk_gui.cpp | 151 ++++++++++++++++++++++++++++++++++++++++++++++++ vk_gui.h | 18 ++++++ vk_types.h | 17 ++++++ 6 files changed, 198 insertions(+), 166 deletions(-) create mode 100644 vk_gui.cpp create mode 100644 vk_gui.h diff --git a/meson.build b/meson.build index deacd3f..64df191 100644 --- a/meson.build +++ b/meson.build @@ -121,6 +121,7 @@ sources = [ 'vk_images.cpp', 'vk_descriptors.cpp', 'vk_pipelines.cpp', + 'vk_gui.cpp', 'main.cpp', ] diff --git a/vk_engine.cpp b/vk_engine.cpp index 6e9dbc8..cf70b02 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -3,18 +3,13 @@ #include "vk_pipelines.h" #include -#include -#include - #include #include #include "VkBootstrap.h" #include #include -#include "imgui.h" -#include "imgui_impl_sdl2.h" -#include "imgui_impl_vulkan.h" +#include #define VMA_IMPLEMENTATION #include "vk_mem_alloc.h" @@ -32,18 +27,7 @@ void VulkanEngine::init() assert(loadedEngine == nullptr); loadedEngine = this; - // We initialize SDL and create a window with it. - SDL_Init(SDL_INIT_VIDEO); - - SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN); - - _window = SDL_CreateWindow( - "Vulkan Engine", - SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, - int(_windowExtent.width), - int(_windowExtent.height), - window_flags); + _window = _gui.init_window(_windowExtent); init_vulkan(); init_swapchain(); @@ -51,7 +35,7 @@ void VulkanEngine::init() init_sync_structures(); init_descriptors(); init_pipelines(); - init_imgui(); + _gui.init_imgui(VulkanEngine::Get()); //everything went fine _isInitialized = true; @@ -81,7 +65,7 @@ void VulkanEngine::cleanup() vkDestroyDevice(_device, nullptr); vkb::destroy_debug_utils_messenger(_instance, _debug_messenger); vkDestroyInstance(_instance, nullptr); - SDL_DestroyWindow(_window); + _gui.destroy(_window); } loadedEngine = nullptr; @@ -125,7 +109,7 @@ void VulkanEngine::draw() vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - draw_imgui(cmd, _swapchainImageViews[swapchainImageIndex]); + _gui.draw_imgui(_swapchainExtent, cmd, _swapchainImageViews[swapchainImageIndex]); vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); @@ -168,68 +152,21 @@ void VulkanEngine::draw() void VulkanEngine::run() { - SDL_Event e; - bool bQuit = false; - bool stop_rendering = false; - //main loop - while(!bQuit) + while(!_gui.should_quit) { - //Handle events on queue - while(SDL_PollEvent(&e) != 0) - { - //close the window when user alt-f4s or clicks the X button - if(e.type == SDL_QUIT) { - bQuit = true; - } + _gui.poll_event(); - if(e.type == SDL_WINDOWEVENT) { - if(e.window.event == SDL_WINDOWEVENT_MINIMIZED) { - stop_rendering = true; - } - - if(e.window.event == SDL_WINDOWEVENT_RESTORED) { - stop_rendering = false; - } - } - - ImGui_ImplSDL2_ProcessEvent(&e); - } - - if(stop_rendering) { + if(_gui.stop_rendering) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); continue; } - render_imgui(); + _gui.render_imgui(backgroundEffects, ¤tBackgroundEffect); draw(); } } -void VulkanEngine::render_imgui() { - ImGui_ImplVulkan_NewFrame(); - ImGui_ImplSDL2_NewFrame(); - - ImGui::NewFrame(); - - if (ImGui::Begin("background")) { - - ComputeEffect& selected = backgroundEffects[currentBackgroundEffect]; - - ImGui::Text("Selected effect: %s", selected.name); - - ImGui::SliderInt("Effect Index", ¤tBackgroundEffect,0, backgroundEffects.size() - 1); - - ImGui::InputFloat4("data1",(float*)& selected.data.data1); - ImGui::InputFloat4("data2",(float*)& selected.data.data2); - ImGui::InputFloat4("data3",(float*)& selected.data.data3); - ImGui::InputFloat4("data4",(float*)& selected.data.data4); - } - ImGui::End(); - - ImGui::Render(); -} - void VulkanEngine::init_vulkan() { vkb::InstanceBuilder builder; @@ -570,81 +507,7 @@ void VulkanEngine::immediate_submit(std::function&& f VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, _immFence)); VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true,9999999999)); - } -void VulkanEngine::init_imgui() -{ -// 1: create descriptor pool for IMGUI - // the size of the pool is very oversize, but it's copied from imgui demo - // itself. - VkDescriptorPoolSize pool_sizes[] = { - { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, - { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, - { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, - { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } - }; - - VkDescriptorPoolCreateInfo pool_info{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, - .maxSets = 1000, - .poolSizeCount = (uint32_t)std::size(pool_sizes), - .pPoolSizes = pool_sizes, - }; - - VkDescriptorPool imguiPool; - VK_CHECK(vkCreateDescriptorPool(_device, &pool_info, nullptr, &imguiPool)); - // 2: initialize the imgui library - ImGui::CreateContext(); - - ImGui_ImplSDL2_InitForVulkan(_window); - - ImGui_ImplVulkan_InitInfo init_info{ - .Instance = _instance, - .PhysicalDevice = _chosenGPU, - .Device = _device, - .Queue = _graphicsQueue, - .DescriptorPool = imguiPool, - .MinImageCount = 3, - .ImageCount = 3, - .MSAASamples = VK_SAMPLE_COUNT_1_BIT, - .UseDynamicRendering = true, - .PipelineRenderingCreateInfo = { - .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, - .colorAttachmentCount = 1, - .pColorAttachmentFormats = &_swapchainImageFormat, - }, - }; - - ImGui_ImplVulkan_Init(&init_info); - ImGui_ImplVulkan_CreateFontsTexture(); - - _mainDeletionQueue.push_function([=,this]() { - ImGui_ImplVulkan_Shutdown(); - vkDestroyDescriptorPool(_device, imguiPool, nullptr); - }); -} - - -void VulkanEngine::draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView) -{ - VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - VkRenderingInfo renderInfo = vkinit::rendering_info(_swapchainExtent, &colorAttachment, nullptr); - - vkCmdBeginRendering(cmd, &renderInfo); - - ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); - - vkCmdEndRendering(cmd); -} diff --git a/vk_engine.h b/vk_engine.h index 5a7903b..b5c85a5 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -5,22 +5,7 @@ #include #include - -struct ComputePushConstants { - glm::vec4 data1; - glm::vec4 data2; - glm::vec4 data3; - glm::vec4 data4; -}; - -struct ComputeEffect { - const char *name; - VkPipeline pipeline; - VkPipelineLayout layout; - - ComputePushConstants data; -}; - +#include "vk_gui.h" struct DeletionQueue { std::deque> deletors; @@ -48,7 +33,6 @@ struct FrameData { DeletionQueue _deletionQueue; }; -constexpr unsigned int FRAME_OVERLAP=2; class VulkanEngine { public: @@ -104,6 +88,7 @@ public: // imgui shader stuff std::vector backgroundEffects; int currentBackgroundEffect{0}; + VkGUI _gui; static VulkanEngine& Get(); @@ -129,11 +114,8 @@ private: void init_descriptors(); void init_pipelines(); void init_background_pipelines(); - void init_imgui(); void create_swapchain(uint32_t width, uint32_t height); void destroy_swapchain(); void draw_background(VkCommandBuffer cmd); - void draw_imgui(VkCommandBuffer cmd, VkImageView targetImageView); - void render_imgui(); }; diff --git a/vk_gui.cpp b/vk_gui.cpp new file mode 100644 index 0000000..86e4bd3 --- /dev/null +++ b/vk_gui.cpp @@ -0,0 +1,151 @@ +#include "vk_gui.h" +#include "vk_initializers.h" +#include +#include +#include +#include +#include +#include "vk_engine.h" + +struct SDL_Window* VkGUI::init_window(VkExtent2D windowExtent) +{ + SDL_Init(SDL_INIT_VIDEO); + + SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_VULKAN); + + return SDL_CreateWindow( + "Vulkan Engine", + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + int(windowExtent.width), + int(windowExtent.height), + window_flags); +} + +void VkGUI::init_imgui(VulkanEngine& engine) +{ + // 1. create descriptor pool for IMGUI + // the size of the pool is very oversize, but it's copied from imgui demo + // itself. + VkDescriptorPoolSize pool_sizes[] = { + { VK_DESCRIPTOR_TYPE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1000 }, + { VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1000 }, + { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 1000 }, + { VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 1000 } + }; + + VkDescriptorPoolCreateInfo pool_info{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, + .maxSets = 1000, + .poolSizeCount = (uint32_t)std::size(pool_sizes), + .pPoolSizes = pool_sizes, + }; + + VkDescriptorPool imguiPool; + VK_CHECK(vkCreateDescriptorPool(engine._device, &pool_info, nullptr, &imguiPool)); + + // 2: initialize the imgui library + ImGui::CreateContext(); + + ImGui_ImplSDL2_InitForVulkan(engine._window); + + ImGui_ImplVulkan_InitInfo init_info{ + .Instance = engine._instance, + .PhysicalDevice = engine._chosenGPU, + .Device = engine._device, + .Queue = engine._graphicsQueue, + .DescriptorPool = imguiPool, + .MinImageCount = 3, + .ImageCount = 3, + .MSAASamples = VK_SAMPLE_COUNT_1_BIT, + .UseDynamicRendering = true, + .PipelineRenderingCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, + .colorAttachmentCount = 1, + .pColorAttachmentFormats = &engine._swapchainImageFormat, + }, + }; + + ImGui_ImplVulkan_Init(&init_info); + ImGui_ImplVulkan_CreateFontsTexture(); + + engine._mainDeletionQueue.push_function([=,this]() { + ImGui_ImplVulkan_Shutdown(); + vkDestroyDescriptorPool(engine._device, imguiPool, nullptr); + }); +} + +void VkGUI::draw_imgui(VkExtent2D& swapchainExtent, VkCommandBuffer cmd, VkImageView targetImageView) +{ + VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(targetImageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + VkRenderingInfo renderInfo = vkinit::rendering_info(swapchainExtent, &colorAttachment, nullptr); + + vkCmdBeginRendering(cmd, &renderInfo); + + ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), cmd); + + vkCmdEndRendering(cmd); +} + +void VkGUI::render_imgui(std::vector& backgroundEffects, int* currentBackgroundEffect) +{ + ImGui_ImplVulkan_NewFrame(); + ImGui_ImplSDL2_NewFrame(); + + ImGui::NewFrame(); + + if (ImGui::Begin("background")) { + + ComputeEffect& selected = backgroundEffects[*currentBackgroundEffect]; + + ImGui::Text("Selected effect: %s", selected.name); + + ImGui::SliderInt("Effect Index", currentBackgroundEffect,0, backgroundEffects.size() - 1); + + ImGui::InputFloat4("data1",(float*)& selected.data.data1); + ImGui::InputFloat4("data2",(float*)& selected.data.data2); + ImGui::InputFloat4("data3",(float*)& selected.data.data3); + ImGui::InputFloat4("data4",(float*)& selected.data.data4); + } + ImGui::End(); + + ImGui::Render(); +} + +void VkGUI::destroy(struct SDL_Window* _window) +{ + SDL_DestroyWindow(_window); +} + + +void VkGUI::poll_event() { + SDL_Event event; + + while(SDL_PollEvent(&event) != 0) { + //close the window when user alt-f4s or clicks the X button + if(event.type == SDL_QUIT) { + should_quit = true; + } + + if(event.type == SDL_WINDOWEVENT) { + if(event.window.event == SDL_WINDOWEVENT_MINIMIZED) { + stop_rendering = true; + } + + if(event.window.event == SDL_WINDOWEVENT_RESTORED) { + stop_rendering = false; + } + } + + ImGui_ImplSDL2_ProcessEvent(&event); + } +} diff --git a/vk_gui.h b/vk_gui.h new file mode 100644 index 0000000..8890504 --- /dev/null +++ b/vk_gui.h @@ -0,0 +1,18 @@ +#pragma once +#include "vk_types.h" + +class VulkanEngine; + +class VkGUI { + public: + bool should_quit = false; + bool stop_rendering = false; + + struct SDL_Window* init_window(VkExtent2D windowExtent); + void init_imgui(VulkanEngine& engine); + void draw_imgui(VkExtent2D& swapchainExtent, VkCommandBuffer cmd, VkImageView targetImageView); + void render_imgui(std::vector& backgroundEffects, int* currentBackgroundEffect); + void destroy(struct SDL_Window* _window); + + void poll_event(); +}; diff --git a/vk_types.h b/vk_types.h index 9b880a6..ed4264a 100644 --- a/vk_types.h +++ b/vk_types.h @@ -37,3 +37,20 @@ struct AllocatedImage { VkExtent3D imageExtent; VkFormat imageFormat; }; + +struct ComputePushConstants { + glm::vec4 data1; + glm::vec4 data2; + glm::vec4 data3; + glm::vec4 data4; +}; + +struct ComputeEffect { + const char *name; + VkPipeline pipeline; + VkPipelineLayout layout; + + ComputePushConstants data; +}; + +constexpr unsigned int FRAME_OVERLAP=2; From 61931083c67cde2756f0353bf67c31a322100524 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 9 Dec 2025 13:22:44 -0500 Subject: [PATCH 04/13] Refactored the shader loading. --- vk_engine.cpp | 106 +++++++++++++++++++++++++------------------------- vk_engine.h | 4 ++ 2 files changed, 58 insertions(+), 52 deletions(-) diff --git a/vk_engine.cpp b/vk_engine.cpp index cf70b02..642ceb2 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -410,14 +410,47 @@ void VulkanEngine::init_descriptors() { void VulkanEngine::init_pipelines() { init_background_pipelines(); + init_shaders(); } +void VulkanEngine::load_shader(const char *file_name, const char *entry_point, ComputeEffect sky) { + VkShaderModule skyShader; + bool good = vkutil::load_shader_module(file_name, _device, &skyShader); + assert(good && "failed to load shader"); + + VkPipelineShaderStageCreateInfo stageinfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = VK_SHADER_STAGE_COMPUTE_BIT, + .module = skyShader, + .pName = entry_point, + }; + + VkComputePipelineCreateInfo computePipelineCreateInfo{ + .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, + .stage = stageinfo, + .layout = _gradientPipelineLayout, + }; + + sky.layout = _gradientPipelineLayout; + + VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &sky.pipeline)); + + backgroundEffects.push_back(sky); + + vkDestroyShaderModule(_device, skyShader, nullptr); + + _mainDeletionQueue.push_function([=,this]() { + vkDestroyPipeline(_device, sky.pipeline, nullptr); + }); +} + + void VulkanEngine::init_background_pipelines() { VkPushConstantRange pushConstant{ .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, .offset = 0, - .size = sizeof(ComputePushConstants) , + .size = sizeof(ComputePushConstants), }; VkPipelineLayoutCreateInfo computeLayout{ @@ -430,59 +463,28 @@ void VulkanEngine::init_background_pipelines() VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout)); - VkShaderModule gradientShader; - bool good = vkutil::load_shader_module("gradient_color.comp.spv", _device, &gradientShader); - assert(good && "failed to load gradient_color.comp.spv"); - - VkShaderModule skyShader; - good = vkutil::load_shader_module("sky.comp.spv", _device, &skyShader); - - VkPipelineShaderStageCreateInfo stageinfo{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .stage = VK_SHADER_STAGE_COMPUTE_BIT, - .module = gradientShader, - .pName = "main", - }; - - VkComputePipelineCreateInfo computePipelineCreateInfo{ - .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, - .stage = stageinfo, - .layout = _gradientPipelineLayout, - }; - - ComputeEffect gradient{ - .name = "gradient", - .layout = _gradientPipelineLayout, - .data = { - .data1 = glm::vec4(1, 0, 0, 1), - .data2 = glm::vec4(0, 0, 1, 1), - }, - }; - - - VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &gradient.pipeline)); - - // change the shader module only to create the sky - computePipelineCreateInfo.stage.module = skyShader; - - ComputeEffect sky{ - .name = "sky", - .layout = _gradientPipelineLayout, - .data{.data1 = glm::vec4(0.1, 0.2, 0.4, 0.97)}, - }; - - VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &sky.pipeline)); - - backgroundEffects.push_back(gradient); - backgroundEffects.push_back(sky); - - vkDestroyShaderModule(_device, gradientShader, nullptr); - vkDestroyShaderModule(_device, skyShader, nullptr); - + // final cleanup _mainDeletionQueue.push_function([=,this]() { vkDestroyPipelineLayout(_device, _gradientPipelineLayout, nullptr); - vkDestroyPipeline(_device, sky.pipeline, nullptr); - vkDestroyPipeline(_device, sky.pipeline, nullptr); + }); +} + + +void VulkanEngine::init_shaders() { + /// gradient shader + load_shader("gradient_color.comp.spv", "main", { + .name="gradient", + .data={ + .data1 = glm::vec4(1, 0, 0, 1), + .data2 = glm::vec4(0, 0, 1, 1), + } + }); + + load_shader("sky.comp.spv", "main", { + .name="sky", + .data = { + .data1 = glm::vec4(0.1, 0.2, 0.4, 0.97) + } }); } diff --git a/vk_engine.h b/vk_engine.h index b5c85a5..c890046 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -88,6 +88,8 @@ public: // imgui shader stuff std::vector backgroundEffects; int currentBackgroundEffect{0}; + + // ZED's REFACTOR VkGUI _gui; static VulkanEngine& Get(); @@ -114,6 +116,8 @@ private: void init_descriptors(); void init_pipelines(); void init_background_pipelines(); + void init_shaders(); + void load_shader(const char *file_name, const char *entry_point, ComputeEffect data); void create_swapchain(uint32_t width, uint32_t height); void destroy_swapchain(); From b5c6774412cb8622ec7736fc1d82b361e22ec37e Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Tue, 9 Dec 2025 13:47:39 -0500 Subject: [PATCH 05/13] Clean out references to 'sky' in the load_shader. --- vk_engine.cpp | 49 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/vk_engine.cpp b/vk_engine.cpp index 642ceb2..de223bb 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -73,6 +73,7 @@ void VulkanEngine::cleanup() void VulkanEngine::draw() { + // ZED: begin command transaction // wait until the gpu has finished rendering the last frame. Timeout of 1 second VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000)); get_current_frame()._deletionQueue.flush(); @@ -87,38 +88,60 @@ void VulkanEngine::draw() VkCommandBufferBeginInfo cmdBeginInfo = vkinit::command_buffer_begin_info(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT); + VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); + + + + + + + // ZED: draw the things _drawExtent.width = _drawImage.imageExtent.width; _drawExtent.height = _drawImage.imageExtent.height; - VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); - // transition our main draw image into general layout so we can write into it // we will overwrite it all so we dont care about what was the older layout - vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); draw_background(cmd); + // ZED: ? vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + + + // ZED: ? vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); vkutil::copy_image_to_image(cmd, _drawImage.image, _swapchainImages[swapchainImageIndex], _drawExtent, _swapchainExtent); + + + // ZED: ? vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); _gui.draw_imgui(_swapchainExtent, cmd, _swapchainImageViews[swapchainImageIndex]); + // ZED: end drawing the things + + + + + + // ZED: finalize image and commit command buffer vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); VK_CHECK(vkEndCommandBuffer(cmd)); + + + // ZED: submit command to queue //prepare the submission to the queue. //we want to wait on the _presentSemaphore, as that semaphore is signaled when the swapchain is ready //we will signal the _renderSemaphore, to signal that rendering has finished - VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd); VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR,get_current_frame()._swapchainSemaphore); @@ -413,15 +436,15 @@ void VulkanEngine::init_pipelines() init_shaders(); } -void VulkanEngine::load_shader(const char *file_name, const char *entry_point, ComputeEffect sky) { - VkShaderModule skyShader; - bool good = vkutil::load_shader_module(file_name, _device, &skyShader); +void VulkanEngine::load_shader(const char *file_name, const char *entry_point, ComputeEffect effect) { + VkShaderModule shader; + bool good = vkutil::load_shader_module(file_name, _device, &shader); assert(good && "failed to load shader"); VkPipelineShaderStageCreateInfo stageinfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .stage = VK_SHADER_STAGE_COMPUTE_BIT, - .module = skyShader, + .module = shader, .pName = entry_point, }; @@ -431,16 +454,16 @@ void VulkanEngine::load_shader(const char *file_name, const char *entry_point, C .layout = _gradientPipelineLayout, }; - sky.layout = _gradientPipelineLayout; + effect.layout = _gradientPipelineLayout; - VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &sky.pipeline)); + VK_CHECK(vkCreateComputePipelines(_device, VK_NULL_HANDLE, 1, &computePipelineCreateInfo, nullptr, &effect.pipeline)); - backgroundEffects.push_back(sky); + backgroundEffects.push_back(effect); - vkDestroyShaderModule(_device, skyShader, nullptr); + vkDestroyShaderModule(_device, shader, nullptr); _mainDeletionQueue.push_function([=,this]() { - vkDestroyPipeline(_device, sky.pipeline, nullptr); + vkDestroyPipeline(_device, effect.pipeline, nullptr); }); } From 3d36517b4f41f7dc0338f4d9971a098ea96ad42e Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Wed, 10 Dec 2025 12:58:55 -0500 Subject: [PATCH 06/13] A bit more refactoring to expose the begin/end transactional nature. --- vk_descriptors.cpp | 1 - vk_engine.cpp | 105 +++++++++++++++++++++------------------------ vk_engine.h | 10 ++++- 3 files changed, 59 insertions(+), 57 deletions(-) diff --git a/vk_descriptors.cpp b/vk_descriptors.cpp index a8aca8b..d8d4f62 100644 --- a/vk_descriptors.cpp +++ b/vk_descriptors.cpp @@ -2,7 +2,6 @@ void DescriptorLayoutBuilder::add_binding(uint32_t binding, VkDescriptorType type) { - VkDescriptorSetLayoutBinding newbind{ .binding = binding, .descriptorType = type, diff --git a/vk_engine.cpp b/vk_engine.cpp index de223bb..448cffb 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -71,8 +71,7 @@ void VulkanEngine::cleanup() loadedEngine = nullptr; } -void VulkanEngine::draw() -{ +Transaction VulkanEngine::begin_transaction() { // ZED: begin command transaction // wait until the gpu has finished rendering the last frame. Timeout of 1 second VK_CHECK(vkWaitForFences(_device, 1, &get_current_frame()._renderFence, true, 1000000000)); @@ -90,64 +89,22 @@ void VulkanEngine::draw() VK_CHECK(vkBeginCommandBuffer(cmd, &cmdBeginInfo)); + return {cmd, swapchainImageIndex}; +} - - - - - // ZED: draw the things - _drawExtent.width = _drawImage.imageExtent.width; - _drawExtent.height = _drawImage.imageExtent.height; - - // transition our main draw image into general layout so we can write into it - // we will overwrite it all so we dont care about what was the older layout - vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); - - draw_background(cmd); - - // ZED: ? - vkutil::transition_image(cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); - - - - - // ZED: ? - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); - - vkutil::copy_image_to_image(cmd, _drawImage.image, - _swapchainImages[swapchainImageIndex], - _drawExtent, _swapchainExtent); - - - - // ZED: ? - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - - _gui.draw_imgui(_swapchainExtent, cmd, _swapchainImageViews[swapchainImageIndex]); - // ZED: end drawing the things - - - - - - - // ZED: finalize image and commit command buffer - vkutil::transition_image(cmd, _swapchainImages[swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); - - VK_CHECK(vkEndCommandBuffer(cmd)); - - +void VulkanEngine::commit_transaction(Transaction& t) { + VK_CHECK(vkEndCommandBuffer(t.cmd)); // ZED: submit command to queue //prepare the submission to the queue. //we want to wait on the _presentSemaphore, as that semaphore is signaled when the swapchain is ready //we will signal the _renderSemaphore, to signal that rendering has finished - VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(cmd); + VkCommandBufferSubmitInfo cmdinfo = vkinit::command_buffer_submit_info(t.cmd); VkSemaphoreSubmitInfo waitInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_COLOR_ATTACHMENT_OUTPUT_BIT_KHR,get_current_frame()._swapchainSemaphore); VkSemaphoreSubmitInfo signalInfo = vkinit::semaphore_submit_info(VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, get_current_frame()._renderSemaphore); - VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo,&signalInfo,&waitInfo); + VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, &signalInfo, &waitInfo); //submit command buffer to the queue and execute it. // _renderFence will now block until the graphic commands finish execution @@ -164,7 +121,7 @@ void VulkanEngine::draw() .pWaitSemaphores = &get_current_frame()._renderSemaphore, .swapchainCount = 1, .pSwapchains = &_swapchain, - .pImageIndices = &swapchainImageIndex, + .pImageIndices = &t.swapchainImageIndex, }; VK_CHECK(vkQueuePresentKHR(_graphicsQueue, &presentInfo)); @@ -173,6 +130,44 @@ void VulkanEngine::draw() _frameNumber++; } +void VulkanEngine::draw() +{ + auto t = begin_transaction(); + + // ZED: draw the things + _drawExtent.width = _drawImage.imageExtent.width; + _drawExtent.height = _drawImage.imageExtent.height; + + // transition our main draw image into general layout so we can write into it + // we will overwrite it all so we dont care about what was the older layout + vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL); + + draw_background(t.cmd); + + // ZED: ? + vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + // ZED: ? + vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + vkutil::copy_image_to_image(t.cmd, _drawImage.image, + _swapchainImages[t.swapchainImageIndex], + _drawExtent, _swapchainExtent); + + // ZED: ? + vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + _gui.draw_imgui(_swapchainExtent, t.cmd, _swapchainImageViews[t.swapchainImageIndex]); + // ZED: end drawing the things + + // ZED: finalize image and commit command buffer + vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + commit_transaction(t); +} + void VulkanEngine::run() { //main loop @@ -393,7 +388,7 @@ void VulkanEngine::init_descriptors() { { VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1 } }; - globalDescriptorAllocator.init_pool(_device, 10, sizes); + _globalDescriptorAllocator.init_pool(_device, 10, sizes); // make the descriptor set layout for our compute draw { @@ -404,14 +399,14 @@ void VulkanEngine::init_descriptors() { // other code //allocate a descriptor set for our draw image - _drawImageDescriptors = globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout); + _drawImageDescriptors = _globalDescriptorAllocator.allocate(_device,_drawImageDescriptorLayout); VkDescriptorImageInfo imgInfo{ .imageView = _drawImage.imageView, .imageLayout = VK_IMAGE_LAYOUT_GENERAL, }; - VkWriteDescriptorSet drawImageWrite = { + VkWriteDescriptorSet drawImageWrite{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = _drawImageDescriptors, .dstBinding = 0, @@ -424,7 +419,7 @@ void VulkanEngine::init_descriptors() { //make sure both the descriptor allocator and the new layout get cleaned up properly _mainDeletionQueue.push_function([&]() { - globalDescriptorAllocator.destroy_pool(_device); + _globalDescriptorAllocator.destroy_pool(_device); vkDestroyDescriptorSetLayout(_device, _drawImageDescriptorLayout, nullptr); }); diff --git a/vk_engine.h b/vk_engine.h index c890046..0f684f4 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -7,6 +7,11 @@ #include #include "vk_gui.h" +struct Transaction { + VkCommandBuffer cmd; + uint32_t swapchainImageIndex = 0; +}; + struct DeletionQueue { std::deque> deletors; @@ -42,7 +47,7 @@ public: VkPipeline _gradientPipeline; VkPipelineLayout _gradientPipelineLayout; - DescriptorAllocator globalDescriptorAllocator; + DescriptorAllocator _globalDescriptorAllocator; VkDescriptorSet _drawImageDescriptors; VkDescriptorSetLayout _drawImageDescriptorLayout; @@ -108,6 +113,9 @@ public: void immediate_submit(std::function&& function); + Transaction begin_transaction(); + void commit_transaction(Transaction& cmd); + private: void init_vulkan(); void init_swapchain(); From 10fc100e2b280f61343e4ef6c88d9759798d2fff Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 14 Dec 2025 10:19:24 -0500 Subject: [PATCH 07/13] Got the triangle working! It only took 10 hours. --- Makefile | 2 + colored_triangle.frag | 13 +++ colored_triangle.frag.spv | Bin 0 -> 500 bytes colored_triangle.vert | 24 ++++++ colored_triangle.vert.spv | Bin 0 -> 1388 bytes vk_engine.cpp | 88 ++++++++++++++++++++- vk_engine.h | 7 +- vk_initializers.cpp | 25 ++++++ vk_initializers.h | 4 + vk_pipelines.cpp | 162 ++++++++++++++++++++++++++++++++++++++ vk_pipelines.h | 31 ++++++++ 11 files changed, 352 insertions(+), 4 deletions(-) create mode 100644 colored_triangle.frag create mode 100644 colored_triangle.frag.spv create mode 100644 colored_triangle.vert create mode 100644 colored_triangle.vert.spv diff --git a/Makefile b/Makefile index 73487f8..afadde2 100644 --- a/Makefile +++ b/Makefile @@ -13,6 +13,8 @@ shaders: glslangValidator -V gradient.comp -o gradient.comp.spv glslangValidator -V sky.comp -o sky.comp.spv glslangValidator -V gradient_color.comp -o gradient_color.comp.spv + glslangValidator -V colored_triangle.frag -o colored_triangle.frag.spv + glslangValidator -V colored_triangle.vert -o colored_triangle.vert.spv %.cpp : %.rl ragel -I $(ROOT_DIR) -G1 -o $@ $< diff --git a/colored_triangle.frag b/colored_triangle.frag new file mode 100644 index 0000000..2f05038 --- /dev/null +++ b/colored_triangle.frag @@ -0,0 +1,13 @@ +#version 450 + +//shader input +layout (location = 0) in vec3 inColor; + +//output write +layout (location = 0) out vec4 outFragColor; + +void main() +{ + //return red + outFragColor = vec4(inColor,1.0f); +} diff --git a/colored_triangle.frag.spv b/colored_triangle.frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..ad4159133580a498844f3a27c1ca0813d2f65262 GIT binary patch literal 500 zcmYk1PfJ2k5XC27Pc8F5)GmycfwZUyqFMyHaZ&UMgdl|Sf!W^A=BH{S^!r_((S>v8 z&Y3fF=4O@iTEt#7q8|O&o^DiN0 za0EK&JiBbL+djkAog( z$t}|Rn0FO!08Y@mi@E*-?mE%m$lsN@9&+jriR#VOH;8;8@#@XpLGFTFZ3{%K9&h-8 z`{?Cy#t%$RyoYsm6WIR(Yj3;c2!C9sdfJt!EW$x}D?svw;QRnfKT@`bw zSqjz6GtY5~_}a?`*5`?<#9gAvuR;EexP@v`*JAxydSCL72mR59{;24G2f|jowY}ju zE3)ALcBa(3Z^>ta;xPNsR~J3^qPLf4-`-^7qBr>5Kk4b(-f^Cv^ji4SuR}ljMpK!2 zH#pK!i{CnYChUF6-B8Z+4UdbxVLlvDmfQpKq^`^Qo>52t{w+!|*+k|7g#%HMYwFW** zcD~#?m3zk;_KY^VbL8;)yvE+Mocqj`x@ukb5ziA2eJzmPM}7EMtg${WP^*!%uXc$@ z>F2A!7fYPd4qdg&gc`$M=vJti_bYU(Ai2;5e+kZc_RRA7xJpMH?jKg-JuTOp2@oj X-@DXuYQpzJYI)c4eZ+rp_>A}m)1^~) literal 0 HcmV?d00001 diff --git a/vk_engine.cpp b/vk_engine.cpp index 448cffb..1106b6d 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -144,8 +144,12 @@ void VulkanEngine::draw() draw_background(t.cmd); + vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + draw_geometry(t.cmd); + // ZED: ? - vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_GENERAL, + vkutil::transition_image(t.cmd, _drawImage.image, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); // ZED: ? @@ -159,15 +163,47 @@ void VulkanEngine::draw() // ZED: ? vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - _gui.draw_imgui(_swapchainExtent, t.cmd, _swapchainImageViews[t.swapchainImageIndex]); // ZED: end drawing the things + _gui.draw_imgui(_swapchainExtent, t.cmd, _swapchainImageViews[t.swapchainImageIndex]); // ZED: finalize image and commit command buffer - vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + vkutil::transition_image(t.cmd, _swapchainImages[t.swapchainImageIndex], VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); commit_transaction(t); } +void VulkanEngine::draw_geometry(VkCommandBuffer cmd) +{ + VkRenderingAttachmentInfo colorAttachment = vkinit::attachment_info(_drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + + VkRenderingInfo renderInfo = vkinit::rendering_info(_drawExtent, &colorAttachment, nullptr); + vkCmdBeginRendering(cmd, &renderInfo); + + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _trianglePipeline); + + VkViewport viewport{ + .x = 0, + .y = 0, + .width = (float)_drawExtent.width, + .height = (float)_drawExtent.height, + .minDepth = 0.0f, + .maxDepth = 1.0f, + }; + + vkCmdSetViewport(cmd, 0, 1, &viewport); + + VkRect2D scissor{ + .offset = { .x = 0, .y = 0, }, + .extent = _drawExtent + }; + + vkCmdSetScissor(cmd, 0, 1, &scissor); + + vkCmdDraw(cmd, 3, 1, 0, 0); + + vkCmdEndRendering(cmd); +} + void VulkanEngine::run() { //main loop @@ -428,6 +464,7 @@ void VulkanEngine::init_descriptors() { void VulkanEngine::init_pipelines() { init_background_pipelines(); + init_triangle_pipelines(); init_shaders(); } @@ -530,4 +567,49 @@ void VulkanEngine::immediate_submit(std::function&& f } +void VulkanEngine::init_triangle_pipelines() +{ + VkShaderModule triangleFragShader; + if(!vkutil::load_shader_module("colored_triangle.frag.spv",_device, &triangleFragShader)) { + std::print("Error when building the triangle fragment shaders module"); + } else { + std::print("Triangle fragment shader successfully loaded"); + } + + VkShaderModule triangleVertexShader; + if(!vkutil::load_shader_module("colored_triangle.vert.spv", _device, &triangleVertexShader)) { + std::println("Error when building the triangle, vertex shader module"); + } else { + std::println("Triangle vertex shader successfully loaded"); + } + + VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info(); + + VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_trianglePipelineLayout)); + + PipelineBuilder pipelineBuilder; + + pipelineBuilder._pipelineLayout = _trianglePipelineLayout; + pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader); + pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL); + pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); + pipelineBuilder.set_multisampling_none(); + pipelineBuilder.disable_blending(); + pipelineBuilder.disable_depthtest(); + + // connect the image format we will drw into, from draw image + pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat); + pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED); + + _trianglePipeline = pipelineBuilder.build_pipeline(_device); + + vkDestroyShaderModule(_device, triangleFragShader, nullptr); + vkDestroyShaderModule(_device, triangleVertexShader, nullptr); + + _mainDeletionQueue.push_function([&]() { + vkDestroyPipelineLayout(_device, _trianglePipelineLayout, nullptr); + vkDestroyPipeline(_device, _trianglePipeline, nullptr); + }); +} diff --git a/vk_engine.h b/vk_engine.h index 0f684f4..465088c 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -66,6 +66,7 @@ public: std::vector _swapchainImageViews; VkExtent2D _swapchainExtent; + // VMA stuff VmaAllocator _allocator; @@ -93,7 +94,9 @@ public: // imgui shader stuff std::vector backgroundEffects; int currentBackgroundEffect{0}; - + VkPipelineLayout _trianglePipelineLayout; + VkPipeline _trianglePipeline; + // ZED's REFACTOR VkGUI _gui; @@ -126,8 +129,10 @@ private: void init_background_pipelines(); void init_shaders(); void load_shader(const char *file_name, const char *entry_point, ComputeEffect data); + void init_triangle_pipelines(); void create_swapchain(uint32_t width, uint32_t height); void destroy_swapchain(); void draw_background(VkCommandBuffer cmd); + void draw_geometry(VkCommandBuffer cmd); }; diff --git a/vk_initializers.cpp b/vk_initializers.cpp index f54839b..b54c78c 100644 --- a/vk_initializers.cpp +++ b/vk_initializers.cpp @@ -156,3 +156,28 @@ VkRenderingInfo vkinit::rendering_info(VkExtent2D renderExtent, VkRenderingAttac .pStencilAttachment = nullptr, }; } + + +VkPipelineShaderStageCreateInfo vkinit::pipeline_shader_stage_create_info(VkShaderStageFlagBits stage, VkShaderModule shader, const char* entry) { + return { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .stage = stage, + .module = shader, + .pName = entry, + }; +} + + +VkPipelineLayoutCreateInfo vkinit::pipeline_layout_create_info() +{ + return { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 0, + .pSetLayouts = nullptr, + .pushConstantRangeCount = 0, + .pPushConstantRanges = nullptr, + }; +} + diff --git a/vk_initializers.h b/vk_initializers.h index 000b378..3cec09a 100644 --- a/vk_initializers.h +++ b/vk_initializers.h @@ -35,5 +35,9 @@ namespace vkinit { VkImageView view, VkClearValue* clear ,VkImageLayout layout=VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); VkRenderingInfo rendering_info(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment, VkRenderingAttachmentInfo* depthAttachment); + + VkPipelineShaderStageCreateInfo pipeline_shader_stage_create_info(VkShaderStageFlagBits stage, VkShaderModule shader, const char* entry="main"); + + VkPipelineLayoutCreateInfo pipeline_layout_create_info(); } diff --git a/vk_pipelines.cpp b/vk_pipelines.cpp index acecc80..b7b3c8d 100644 --- a/vk_pipelines.cpp +++ b/vk_pipelines.cpp @@ -40,3 +40,165 @@ bool vkutil::load_shader_module(const char* filePath, *outShaderModule = shaderModule; return true; } + + +void PipelineBuilder::clear() { + // clear all of the structs we need back to 0 with their correct stype + + _inputAssembly = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; + + _rasterizer = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; + + _colorBlendAttachment = {}; + + _multisampling = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; + + _pipelineLayout = {}; + + _depthStencil = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; + + _renderInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO }; + + _shaderStages.clear(); +} + +VkPipeline PipelineBuilder::build_pipeline(VkDevice device) +{ + VkPipelineViewportStateCreateInfo viewportState{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, + .viewportCount = 1, + .scissorCount = 1, + }; + + VkPipelineColorBlendStateCreateInfo colorBlending{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, + .logicOpEnable = VK_FALSE, + .logicOp = VK_LOGIC_OP_COPY, + .attachmentCount = 1, + .pAttachments = &_colorBlendAttachment, + }; + + VkPipelineVertexInputStateCreateInfo _vertexInputInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO + }; + + VkDynamicState state[] = { + VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR + }; + + VkPipelineDynamicStateCreateInfo dynamicInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, + .dynamicStateCount = 2, + // ZED: we don't need to do this? state is also &state[0] + .pDynamicStates = &state[0], + }; + + // build the actual pipeline + VkGraphicsPipelineCreateInfo pipelineInfo = { + .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, + .pNext = &_renderInfo, + .stageCount = (uint32_t)_shaderStages.size(), + .pStages = _shaderStages.data(), + .pVertexInputState = &_vertexInputInfo, + .pInputAssemblyState = &_inputAssembly, + .pViewportState = &viewportState, + .pRasterizationState = &_rasterizer, + .pMultisampleState = &_multisampling, + .pDepthStencilState = &_depthStencil, + .pColorBlendState = &colorBlending, + .pDynamicState = &dynamicInfo, + .layout = _pipelineLayout, + }; + + // its easy to error out on create graphics pipeline, so we handle it a bit + // better than the common VK_CHECK case + + VkPipeline newPipeline; + auto works = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &newPipeline); + + if(works != VK_SUCCESS) { + std::println("failed to create pipeline"); + return VK_NULL_HANDLE; + } else { + return newPipeline; + } +} + +void PipelineBuilder::set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader) +{ + _shaderStages.clear(); + + _shaderStages.push_back( + vkinit::pipeline_shader_stage_create_info( + VK_SHADER_STAGE_VERTEX_BIT, + vertexShader)); + + _shaderStages.push_back( + vkinit::pipeline_shader_stage_create_info( + VK_SHADER_STAGE_FRAGMENT_BIT, + fragmentShader)); +} + +void PipelineBuilder::set_input_topology(VkPrimitiveTopology topology) +{ + _inputAssembly.topology = topology; + _inputAssembly.primitiveRestartEnable = VK_FALSE; +} + +void PipelineBuilder::set_polygon_mode(VkPolygonMode mode) +{ + _rasterizer.polygonMode = mode; + _rasterizer.lineWidth = 1.0f; +} + +void PipelineBuilder::set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace) +{ + _rasterizer.cullMode = cullMode; + _rasterizer.frontFace = frontFace; +} + +void PipelineBuilder::set_multisampling_none() +{ + _multisampling.sampleShadingEnable = VK_FALSE; + _multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + _multisampling.minSampleShading = 1.0f; + _multisampling.pSampleMask = nullptr; + _multisampling.alphaToCoverageEnable = VK_FALSE; + _multisampling.alphaToOneEnable = VK_FALSE; +} + +void PipelineBuilder::disable_blending() +{ + _colorBlendAttachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT + | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT + | VK_COLOR_COMPONENT_A_BIT; + + _colorBlendAttachment.blendEnable = VK_FALSE; +} + +void PipelineBuilder::set_color_attachment_format(VkFormat format) +{ + _colorAttachmentFormat = format; + _renderInfo.colorAttachmentCount = 1; + _renderInfo.pColorAttachmentFormats = &_colorAttachmentFormat; +} + +void PipelineBuilder::set_depth_format(VkFormat format) +{ + _renderInfo.depthAttachmentFormat = format; +} + +void PipelineBuilder::disable_depthtest() +{ + _depthStencil.depthTestEnable = VK_FALSE; + _depthStencil.depthWriteEnable = VK_FALSE; + _depthStencil.depthCompareOp = VK_COMPARE_OP_NEVER; + _depthStencil.depthBoundsTestEnable = VK_FALSE; + _depthStencil.stencilTestEnable = VK_FALSE; + _depthStencil.front = {}; + _depthStencil.back = {}; + _depthStencil.minDepthBounds = 0.0f; + _depthStencil.maxDepthBounds = 1.0f; +} diff --git a/vk_pipelines.h b/vk_pipelines.h index df9c74d..16b4067 100644 --- a/vk_pipelines.h +++ b/vk_pipelines.h @@ -2,6 +2,37 @@ #include "vk_types.h" +class PipelineBuilder { + public: + std::vector _shaderStages; + + VkPipelineInputAssemblyStateCreateInfo _inputAssembly; + VkPipelineRasterizationStateCreateInfo _rasterizer; + VkPipelineColorBlendAttachmentState _colorBlendAttachment; + VkPipelineMultisampleStateCreateInfo _multisampling; + VkPipelineLayout _pipelineLayout; + VkPipelineDepthStencilStateCreateInfo _depthStencil; + VkPipelineRenderingCreateInfo _renderInfo; + VkFormat _colorAttachmentFormat; + + PipelineBuilder() { + clear(); + } + + void clear(); + + VkPipeline build_pipeline(VkDevice device); + void set_shaders(VkShaderModule vertexShader, VkShaderModule fragmentShader); + void set_input_topology(VkPrimitiveTopology topology); + void set_polygon_mode(VkPolygonMode mode); + void set_cull_mode(VkCullModeFlags cullMode, VkFrontFace frontFace); + void set_multisampling_none(); + void disable_blending(); + void set_color_attachment_format(VkFormat format); + void set_depth_format(VkFormat format); + void disable_depthtest(); +}; + namespace vkutil { bool load_shader_module(const char* filePath, VkDevice device, From 5435880d343a44c9aa9092a7b72e01184a30267d Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 14 Dec 2025 10:39:30 -0500 Subject: [PATCH 08/13] Bring in SCC to keep track of how much code this is. --- Makefile | 2 +- stb_image.h => vendor/stb_image.h | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename stb_image.h => vendor/stb_image.h (100%) diff --git a/Makefile b/Makefile index afadde2..e6a7c36 100644 --- a/Makefile +++ b/Makefile @@ -57,4 +57,4 @@ debug_test: build gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests -e "[pathing]" money: - scc --exclude-dir subprojects + scc --exclude-dir subprojects,vendor diff --git a/stb_image.h b/vendor/stb_image.h similarity index 100% rename from stb_image.h rename to vendor/stb_image.h From c905ef9ca75197593cc7772a7e06e53a60d5f999 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 14 Dec 2025 13:28:49 -0500 Subject: [PATCH 09/13] [BREAKING] This crashes on Linux, and looks like it's crashing wayland? Testing on Windows. --- Makefile | 1 + colored_triangle_mesh.vert | 37 +++++++ colored_triangle_mesh.vert.spv | Bin 0 -> 2760 bytes vk_engine.cpp | 189 +++++++++++++++++++++++++++++++++ vk_engine.h | 14 ++- vk_types.h | 28 ++++- 6 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 colored_triangle_mesh.vert create mode 100644 colored_triangle_mesh.vert.spv diff --git a/Makefile b/Makefile index e6a7c36..99a8005 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ shaders: glslangValidator -V gradient_color.comp -o gradient_color.comp.spv glslangValidator -V colored_triangle.frag -o colored_triangle.frag.spv glslangValidator -V colored_triangle.vert -o colored_triangle.vert.spv + glslangValidator -V colored_triangle_mesh.vert -o colored_triangle_mesh.vert.spv %.cpp : %.rl ragel -I $(ROOT_DIR) -G1 -o $@ $< diff --git a/colored_triangle_mesh.vert b/colored_triangle_mesh.vert new file mode 100644 index 0000000..fe9bec5 --- /dev/null +++ b/colored_triangle_mesh.vert @@ -0,0 +1,37 @@ +#version 450 +#extension GL_EXT_buffer_reference : require + +layout (location = 0) out vec3 outColor; +layout (location = 1) out vec2 outUV; + +struct Vertex { + + vec3 position; + float uv_x; + vec3 normal; + float uv_y; + vec4 color; +}; + +layout(buffer_reference, std430) readonly buffer VertexBuffer{ + Vertex vertices[]; +}; + +//push constants block +layout( push_constant ) uniform constants +{ + mat4 render_matrix; + VertexBuffer vertexBuffer; +} PushConstants; + +void main() +{ + //load vertex data from device adress + Vertex v = PushConstants.vertexBuffer.vertices[gl_VertexIndex]; + + //output data + gl_Position = PushConstants.render_matrix *vec4(v.position, 1.0f); + outColor = v.color.xyz; + outUV.x = v.uv_x; + outUV.y = v.uv_y; +} diff --git a/colored_triangle_mesh.vert.spv b/colored_triangle_mesh.vert.spv new file mode 100644 index 0000000000000000000000000000000000000000..7461a400c8a0a0bf9cd7093701b0dd74b9984d28 GIT binary patch literal 2760 zcmZ9M>vB_76vua)CR9*SD;E`FYSpS}y#bMigVy7$`SbYEqijHYC6B4^?(MD(M-SI7U-`srFQ!uZiyY=V zO&-qzbkOSbl;41^K-J6>{0{;a(uq{xZQlOrhoVYzkWnk^wX;;lI-N(|?EBeZ zl>L(ErczW(>99ZSj5__iUWG{g>}Pz`#8cl1X5C(YaM0>L!%7psr_+AcZTGwV0lZAV zr{G7qTxMQvz2T_U8x4^w%pm5W^M~1>dC(dSIu;I_y&UCz-h!CB1@eP*7WWvl#7@tHSts=9$9VVX z8~*?@-hE8s%ejo2+vVnneHr5%ff*xKn!=RD?AcoGq6&G`{ZCLg&k|M5u}lWzl+PkL zTMasgzdKOZuD)34=C5M)+Yg`M`qc0}S=jVBkM7-TGj^4XFTnrizpM)4F13r9*Ih)u z;Kp7g&imJ1MfQHxtrz*k)L(&H+uYX+eW}3Cpi?@Za}Iu~(9a;N8-EsVym`a^8nQ?G z>u`O|V}5ma8}S#BozGtNUxqu2r2cjxd%ux?71=$6?i=y$L;ncb`!jwGZZ7Y|d3=vG z$fLjdr_da90sS+$wZy7OzAIzJv`OZ@47a9s=Ti6k7Snze?!CA#>ll9>O6j+o+BcE( zHMja!q1&tZ)VB-FH?@Oo4(HbYPGJ+*U)^_W&Y0^vWRL#e!?l^?D&LEFyd#hJKOp|M zn9Dcm_hB!apzin_-e~Dzh7~6_v`LZ9o#*?32Czj-{pO3 zSgQtU^H_Ho{uX2}=D0zp>NUt1XIX)anS+e+y}Sb%W3R@j>lbz2MK;FzQRh8mV^$$! z)b)!x?;{)IT}7Qs$j12oj8WImm__)9kTKq~F}}r*A!B@_#;EJ3-8b_EjHxvW9tRxU4`F`0`o2!Y{9;7LdNT_-FN$YG1CUJ{pe#q zoA6tZZ{L2LNqqx~nQtS<%+6$uZy@n|X!c((FmaDP+LQVG4z!!k{oaMl7jLnGyj$S< s_}1@1eh+>F=1|usax{?5;qL6o9QPsd$ngN#9R3%WLtUSL>FyfzAE&d)g8%>k literal 0 HcmV?d00001 diff --git a/vk_engine.cpp b/vk_engine.cpp index 1106b6d..51695b6 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -201,6 +201,19 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) vkCmdDraw(cmd, 3, 1, 0, 0); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipeline); + + GPUDrawPushConstants push_constants{ + .worldMatrix = glm::mat4{ 1.0f }, + .vertexBuffer = rectangle.vertexBufferAddress, + }; + + vkCmdPushConstants(cmd, _meshPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), +&push_constants); + vkCmdBindIndexBuffer(cmd, rectangle.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0); + vkCmdEndRendering(cmd); } @@ -465,6 +478,7 @@ void VulkanEngine::init_pipelines() { init_background_pipelines(); init_triangle_pipelines(); + init_mesh_pipeline(); init_shaders(); } @@ -566,6 +580,60 @@ void VulkanEngine::immediate_submit(std::function&& f VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true,9999999999)); } +void VulkanEngine::init_mesh_pipeline() +{ + VkShaderModule triangleFragShader; + if (!vkutil::load_shader_module("colored_triangle.frag.spv", _device, &triangleFragShader)) { + std::print("Error when building the triangle fragment shader module"); + } + else { + std::print("Triangle fragment shader succesfully loaded"); + } + + VkShaderModule triangleVertexShader; + if (!vkutil::load_shader_module("colored_triangle_mesh.vert.spv", _device, &triangleVertexShader)) { + std::print("Error when building the triangle vertex shader module"); + } + else { + std::print("Triangle vertex shader succesfully loaded"); + } + + VkPushConstantRange bufferRange{ + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .offset = 0, + .size = sizeof(GPUDrawPushConstants), + }; + + VkPipelineLayoutCreateInfo pipeline_layout_info = vkinit::pipeline_layout_create_info(); + pipeline_layout_info.pPushConstantRanges = &bufferRange; + pipeline_layout_info.pushConstantRangeCount = 1; + + VK_CHECK(vkCreatePipelineLayout(_device, &pipeline_layout_info, nullptr, &_meshPipelineLayout)); + + + PipelineBuilder pipelineBuilder; + + pipelineBuilder._pipelineLayout = _meshPipelineLayout; + pipelineBuilder.set_shaders(triangleVertexShader, triangleFragShader); + pipelineBuilder.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + pipelineBuilder.set_polygon_mode(VK_POLYGON_MODE_FILL); + pipelineBuilder.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); + pipelineBuilder.set_multisampling_none(); + pipelineBuilder.disable_blending(); + pipelineBuilder.disable_depthtest(); + pipelineBuilder.set_color_attachment_format(_drawImage.imageFormat); + pipelineBuilder.set_depth_format(VK_FORMAT_UNDEFINED); + + _meshPipeline = pipelineBuilder.build_pipeline(_device); + vkDestroyShaderModule(_device, triangleFragShader, nullptr); + vkDestroyShaderModule(_device, triangleVertexShader, nullptr); + + _mainDeletionQueue.push_function([&]() { + vkDestroyPipelineLayout(_device, _meshPipelineLayout, nullptr); + vkDestroyPipeline(_device, _meshPipeline, nullptr); + }); +} + void VulkanEngine::init_triangle_pipelines() { @@ -613,3 +681,124 @@ void VulkanEngine::init_triangle_pipelines() vkDestroyPipeline(_device, _trianglePipeline, nullptr); }); } + + +AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage) +{ + VkBufferCreateInfo bufferInfo{ + .sType=VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = allocSize, + .usage = usage, + }; + + VmaAllocationCreateInfo vmaallocInfo{ + .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT, + .usage = memoryUsage, + }; + + AllocatedBuffer newBuffer; + + VK_CHECK(vmaCreateBuffer(_allocator, &bufferInfo, + &vmaallocInfo, &newBuffer.buffer, + &newBuffer.allocation, &newBuffer.info)); + + return newBuffer; +} + +void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) +{ + vmaDestroyBuffer(_allocator, buffer.buffer, buffer.allocation); +} + + +GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, std::span vertices) +{ + const size_t vertexBufferSize = vertices.size() * sizeof(Vertex); + const size_t indexBufferSize = indices.size() * sizeof(uint32_t); + + GPUMeshBuffers newSurface; + + newSurface.vertexBuffer = create_buffer( + vertexBufferSize, + VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, + VMA_MEMORY_USAGE_GPU_ONLY); + + VkBufferDeviceAddressInfo deviceAddressInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, + .buffer = newSurface.vertexBuffer.buffer + }; + + newSurface.vertexBufferAddress = vkGetBufferDeviceAddress(_device, &deviceAddressInfo); + + newSurface.indexBuffer = create_buffer(indexBufferSize, + VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + VMA_MEMORY_USAGE_GPU_ONLY); + + AllocatedBuffer staging = create_buffer(vertexBufferSize + indexBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY); + + void* data = staging.allocation->GetMappedData(); + + memcpy(data, vertices.data(), vertexBufferSize); + memcpy((char *)data + vertexBufferSize, indices.data(), indexBufferSize); + + immediate_submit([&](VkCommandBuffer cmd) { + VkBufferCopy vertexCopy{ + .srcOffset = 0, + .dstOffset = 0, + .size = vertexBufferSize, + }; + + vkCmdCopyBuffer(cmd, staging.buffer, + newSurface.vertexBuffer.buffer, 1, + &vertexCopy); + + VkBufferCopy indexCopy{ + .srcOffset = vertexBufferSize, + .dstOffset = 0, + .size = indexBufferSize, + }; + + vkCmdCopyBuffer(cmd, staging.buffer, + newSurface.indexBuffer.buffer, 1, + &indexCopy); + }); + + destroy_buffer(staging); + + return newSurface; +} + +void VulkanEngine::init_default_data() { + std::array rect_vertices; + + rect_vertices[0].position = {0.5,-0.5, 0}; + rect_vertices[1].position = {0.5,0.5, 0}; + rect_vertices[2].position = {-0.5,-0.5, 0}; + rect_vertices[3].position = {-0.5,0.5, 0}; + + rect_vertices[0].color = {0,0, 0,1}; + rect_vertices[1].color = { 0.5,0.5,0.5 ,1}; + rect_vertices[2].color = { 1,0, 0,1 }; + rect_vertices[3].color = { 0,1, 0,1 }; + + std::array rect_indices; + + rect_indices[0] = 0; + rect_indices[1] = 1; + rect_indices[2] = 2; + + rect_indices[3] = 2; + rect_indices[4] = 1; + rect_indices[5] = 3; + + rectangle = uploadMesh(rect_indices, rect_vertices); + + _mainDeletionQueue.push_function([&](){ + destroy_buffer(rectangle.indexBuffer); + destroy_buffer(rectangle.vertexBuffer); + }); +} + + diff --git a/vk_engine.h b/vk_engine.h index 465088c..dcececa 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -96,6 +96,10 @@ public: int currentBackgroundEffect{0}; VkPipelineLayout _trianglePipelineLayout; VkPipeline _trianglePipeline; + VkPipelineLayout _meshPipelineLayout; + VkPipeline _meshPipeline; + + GPUMeshBuffers rectangle; // ZED's REFACTOR VkGUI _gui; @@ -128,11 +132,19 @@ private: void init_pipelines(); void init_background_pipelines(); void init_shaders(); - void load_shader(const char *file_name, const char *entry_point, ComputeEffect data); void init_triangle_pipelines(); + void init_mesh_pipeline(); + void init_default_data(); + + void load_shader(const char *file_name, const char *entry_point, ComputeEffect data); void create_swapchain(uint32_t width, uint32_t height); + AllocatedBuffer create_buffer(size_t allocSize, VkBufferUsageFlags usage, VmaMemoryUsage memoryUsage); + void destroy_swapchain(); + void destroy_buffer(const AllocatedBuffer& buffer); void draw_background(VkCommandBuffer cmd); void draw_geometry(VkCommandBuffer cmd); + + GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); }; diff --git a/vk_types.h b/vk_types.h index ed4264a..4f93e18 100644 --- a/vk_types.h +++ b/vk_types.h @@ -30,6 +30,14 @@ } while (0) +constexpr unsigned int FRAME_OVERLAP=2; + +struct AllocatedBuffer { + VkBuffer buffer; + VmaAllocation allocation; + VmaAllocationInfo info; +}; + struct AllocatedImage { VkImage image; VkImageView imageView; @@ -53,4 +61,22 @@ struct ComputeEffect { ComputePushConstants data; }; -constexpr unsigned int FRAME_OVERLAP=2; +struct Vertex { + glm::vec3 position; + float uv_x; + glm::vec3 normal; + float uv_y; + glm::vec4 color; +}; + +struct GPUMeshBuffers { + AllocatedBuffer indexBuffer; + AllocatedBuffer vertexBuffer; + VkDeviceAddress vertexBufferAddress; +}; + +struct GPUDrawPushConstants { + glm::mat4 worldMatrix; + VkDeviceAddress vertexBuffer; +}; + From 6d29fb718bd05b989cdf54e16856e70e19f565e6 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Sun, 14 Dec 2025 23:37:00 -0500 Subject: [PATCH 10/13] Fixed by initializing the data. --- vk_engine.cpp | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/vk_engine.cpp b/vk_engine.cpp index 51695b6..8725787 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -36,6 +36,7 @@ void VulkanEngine::init() init_descriptors(); init_pipelines(); _gui.init_imgui(VulkanEngine::Get()); + init_default_data(); //everything went fine _isInitialized = true; @@ -183,7 +184,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) VkViewport viewport{ .x = 0, - .y = 0, + .y = 0, .width = (float)_drawExtent.width, .height = (float)_drawExtent.height, .minDepth = 0.0f, @@ -208,7 +209,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) .vertexBuffer = rectangle.vertexBufferAddress, }; - vkCmdPushConstants(cmd, _meshPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), + vkCmdPushConstants(cmd, _meshPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(GPUDrawPushConstants), &push_constants); vkCmdBindIndexBuffer(cmd, rectangle.indexBuffer.buffer, 0, VK_INDEX_TYPE_UINT32); @@ -259,14 +260,14 @@ void VulkanEngine::init_vulkan() { .synchronization2 = true, .dynamicRendering = true, }; - + //vulkan 1.2 features VkPhysicalDeviceVulkan12Features features12{ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, .descriptorIndexing = true, .bufferDeviceAddress = true, }; - + // use vkbootstrap to select a gpu // We want a gpu that can write to the SDL surface vkb::PhysicalDeviceSelector selector{ vkb_inst }; @@ -294,7 +295,7 @@ void VulkanEngine::init_vulkan() { .device = _device, .instance = _instance, }; - + vmaCreateAllocator(&allocatorInfo, &_allocator); _mainDeletionQueue.push_function([&]() { @@ -345,7 +346,7 @@ void VulkanEngine::init_swapchain() { _drawImage.imageFormat = VK_FORMAT_R16G16B16A16_SFLOAT; _drawImage.imageExtent = drawImageExtent; - VkImageUsageFlags drawImageUsages = + VkImageUsageFlags drawImageUsages = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT @@ -357,7 +358,7 @@ void VulkanEngine::init_swapchain() { .usage = VMA_MEMORY_USAGE_GPU_ONLY, .requiredFlags = VkMemoryPropertyFlags(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT), }; - + //allocate and create the image vmaCreateImage(_allocator, &rimg_info, &rimg_allocinfo, &_drawImage.image, &_drawImage.allocation, nullptr); @@ -454,7 +455,7 @@ void VulkanEngine::init_descriptors() { .imageView = _drawImage.imageView, .imageLayout = VK_IMAGE_LAYOUT_GENERAL, }; - + VkWriteDescriptorSet drawImageWrite{ .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = _drawImageDescriptors, @@ -521,7 +522,7 @@ void VulkanEngine::init_background_pipelines() .offset = 0, .size = sizeof(ComputePushConstants), }; - + VkPipelineLayoutCreateInfo computeLayout{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = 1, @@ -529,7 +530,7 @@ void VulkanEngine::init_background_pipelines() .pushConstantRangeCount = 1, .pPushConstantRanges = &pushConstant, }; - + VK_CHECK(vkCreatePipelineLayout(_device, &computeLayout, nullptr, &_gradientPipelineLayout)); // final cleanup @@ -577,7 +578,7 @@ void VulkanEngine::immediate_submit(std::function&& f VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, nullptr, nullptr); VK_CHECK(vkQueueSubmit2(_graphicsQueue, 1, &submit, _immFence)); - VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true,9999999999)); + VK_CHECK(vkWaitForFences(_device, 1, &_immFence, true,9999999999)); } void VulkanEngine::init_mesh_pipeline() @@ -705,7 +706,7 @@ AllocatedBuffer VulkanEngine::create_buffer(size_t allocSize, VkBufferUsageFlags return newBuffer; } -void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) +void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) { vmaDestroyBuffer(_allocator, buffer.buffer, buffer.allocation); } @@ -734,7 +735,7 @@ GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, std::span indices, std::span Date: Mon, 15 Dec 2025 11:04:17 -0500 Subject: [PATCH 11/13] Working fastgltf build, but had to manually include the headers. --- .gitignore | 2 ++ Makefile | 5 ++++- basicmesh.glb.gz | Bin 0 -> 33597 bytes meson.build | 8 ++++++-- vk_loader.cpp | 19 +++++++++++++++++++ vk_loader.h | 22 ++++++++++++++++++++++ vk_types.h | 1 + wraps/fastgltf.wrap | 9 +++++++++ 8 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 basicmesh.glb.gz create mode 100644 vk_loader.cpp create mode 100644 vk_loader.h create mode 100644 wraps/fastgltf.wrap diff --git a/.gitignore b/.gitignore index ca37570..8e5e5f8 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ backup coverage coverage/* .venv +*.glb +imgui.ini diff --git a/Makefile b/Makefile index 99a8005..0e6f65d 100644 --- a/Makefile +++ b/Makefile @@ -26,7 +26,10 @@ shaders: %.png: %.dot dot -Tpng $< -o $@ -build: +%.glb: %.glb.gz + gunzip $< + +build: basicmesh.glb meson compile -j 10 -C $(ROOT_DIR)/builddir release_build: diff --git a/basicmesh.glb.gz b/basicmesh.glb.gz new file mode 100644 index 0000000000000000000000000000000000000000..5898895bc5261fb1090f4f76ff06dd3dca60f82a GIT binary patch literal 33597 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틳?a#P?DHoT1POjV)#JJ> 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 literal 0 HcmV?d00001 diff --git a/meson.build b/meson.build index 64df191..0346d16 100644 --- a/meson.build +++ b/meson.build @@ -69,7 +69,6 @@ elif build_machine.system() == 'darwin' endif vma = subproject('vulkan-memory-allocator').get_variable('vma_allocator_dep') -# vulkan_headers = subproject('vulkan-headers').get_variable('vulkan_headers_dep') imgui = subproject('imgui', default_options: { @@ -79,7 +78,7 @@ imgui = subproject('imgui', ).get_variable('imgui_dep') sdl2 = subproject('sdl2').get_variable('sdl2_dep') - +fastgltf = subproject('fastgltf').get_variable('fastgltf_dep') glm_opts = cmake.subproject_options() glm_opts.add_cmake_defines({ @@ -121,16 +120,21 @@ sources = [ 'vk_images.cpp', 'vk_descriptors.cpp', 'vk_pipelines.cpp', + 'vk_loader.cpp', 'vk_gui.cpp', 'main.cpp', ] +# have to force this here for...cmake reasons +include_dirs = include_directories('subprojects/fastgltf/include') + tests = [ ] executable('hellovulk', sources, cpp_args: cpp_args, link_args: link_args, + include_directories: include_dirs, win_subsystem: 'windows', override_options: exe_defaults, dependencies: dependencies) diff --git a/vk_loader.cpp b/vk_loader.cpp new file mode 100644 index 0000000..a5249f1 --- /dev/null +++ b/vk_loader.cpp @@ -0,0 +1,19 @@ +#include "vk_loader.h" +#include "vendor/stb_image.h" +#include + +#include "vk_engine.h" +#include "vk_initializers.h" +#include "vk_types.h" +#include + +#include +#include +#include + + +std::optional>> loadGltfMeshes(VulkanEngine* engine, std::filesystem::path filePath) +{ + + return std::nullopt; +} diff --git a/vk_loader.h b/vk_loader.h new file mode 100644 index 0000000..7e553f1 --- /dev/null +++ b/vk_loader.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +struct GeoSurface { + uint32_t startIndex; + uint32_t count; +}; + +struct MeshAsset { + std::string name; + + std::vector surfaces; + GPUMeshBuffers meshBuffers; +}; + +class VulkanEngine; + +std::optional>> loadGltfMeshes(VulkanEngine* engine, std::filesystem::path filePath); + diff --git a/vk_types.h b/vk_types.h index 4f93e18..d491e6f 100644 --- a/vk_types.h +++ b/vk_types.h @@ -18,6 +18,7 @@ #include +#define GLM_ENABLE_EXPERIMENTAL 1 #include #include diff --git a/wraps/fastgltf.wrap b/wraps/fastgltf.wrap new file mode 100644 index 0000000..abac338 --- /dev/null +++ b/wraps/fastgltf.wrap @@ -0,0 +1,9 @@ +[wrap-git] +directory=fastgltf +url=https://github.com/spnda/fastgltf.git +revision=v0.9.x +depth=1 +method=cmake + +[provide] +fastgltf = fastgltf_dep From 85d792e257c6f073193c9bedad97fdc050ae1809 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Mon, 15 Dec 2025 11:08:46 -0500 Subject: [PATCH 12/13] Better handling of the glb.gz files. --- Makefile | 3 ++- basicmesh.glb.gz | Bin 33597 -> 33597 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 0e6f65d..e3bb9fb 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ shaders: dot -Tpng $< -o $@ %.glb: %.glb.gz - gunzip $< + gunzip -k $< build: basicmesh.glb meson compile -j 10 -C $(ROOT_DIR)/builddir @@ -56,6 +56,7 @@ debug_run: build clean: meson compile --clean -C builddir + rm -f basicmesh.glb debug_test: build gdb --nx -x .gdbinit --ex run --ex bt --ex q --args builddir/runtests -e "[pathing]" diff --git a/basicmesh.glb.gz b/basicmesh.glb.gz index 5898895bc5261fb1090f4f76ff06dd3dca60f82a..50829341c32723916f10743d9e087e674d73c7d2 100644 GIT binary patch delta 18 Zcmdnn# Date: Sat, 20 Dec 2025 00:36:55 -0500 Subject: [PATCH 13/13] [BROKEN] This compiles if we use fastgltf 0.9.0, but it says Error::UnsupportedVersion for the file. Earlier versions can't compile because either simdjson has code errors, or the .a/.so is missing important functions. --- Makefile | 5 +- meson.build | 10 +++- vk_engine.cpp | 33 +++++++++++- vk_engine.h | 5 +- vk_loader.cpp | 124 +++++++++++++++++++++++++++++++++++++++++++- wraps/fastgltf.wrap | 2 +- wraps/simdjson.wrap | 13 +++++ 7 files changed, 185 insertions(+), 7 deletions(-) create mode 100644 wraps/simdjson.wrap diff --git a/Makefile b/Makefile index e3bb9fb..7536b9a 100644 --- a/Makefile +++ b/Makefile @@ -29,9 +29,12 @@ shaders: %.glb: %.glb.gz gunzip -k $< -build: basicmesh.glb +build: meson compile -j 10 -C $(ROOT_DIR)/builddir +meshes: basicmesh.glb + @echo "Meshes made" + release_build: meson --wipe builddir -Db_ndebug=true --buildtype release meson compile -j 10 -C builddir diff --git a/meson.build b/meson.build index 0346d16..054c8ef 100644 --- a/meson.build +++ b/meson.build @@ -78,7 +78,14 @@ imgui = subproject('imgui', ).get_variable('imgui_dep') sdl2 = subproject('sdl2').get_variable('sdl2_dep') -fastgltf = subproject('fastgltf').get_variable('fastgltf_dep') + +fastgltf_opts = cmake.subproject_options() +fastgltf_opts.add_cmake_defines({ + 'FASTGLTF_DOWNLOAD_SIMDJSON': false, +}) + +fastgltf_proj = cmake.subproject('fastgltf', options: fastgltf_opts) +fastgltf = fastgltf_proj.get_variable('fastgltf_dep') glm_opts = cmake.subproject_options() glm_opts.add_cmake_defines({ @@ -112,6 +119,7 @@ dependencies += [ glm, imgui, sdl2, + fastgltf, ] sources = [ diff --git a/vk_engine.cpp b/vk_engine.cpp index 8725787..cfcc16f 100644 --- a/vk_engine.cpp +++ b/vk_engine.cpp @@ -14,7 +14,7 @@ #define VMA_IMPLEMENTATION #include "vk_mem_alloc.h" -constexpr bool bUseValidationLayers = true; +constexpr bool bUseValidationLayers = false; VulkanEngine* loadedEngine = nullptr; @@ -202,6 +202,7 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) vkCmdDraw(cmd, 3, 1, 0, 0); + // draw rectangle vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _meshPipeline); GPUDrawPushConstants push_constants{ @@ -215,6 +216,30 @@ void VulkanEngine::draw_geometry(VkCommandBuffer cmd) vkCmdDrawIndexed(cmd, 6, 1, 0, 0, 0); + + // draw monkey + size_t model_idx = 2; // 0 cube; 1 sphere; 2 monkey + + push_constants.vertexBuffer = testMeshes[model_idx]->meshBuffers.vertexBufferAddress; + + vkCmdPushConstants(cmd, + _meshPipelineLayout, + VK_SHADER_STAGE_VERTEX_BIT, + 0, + sizeof(GPUDrawPushConstants), + &push_constants); + + vkCmdBindIndexBuffer(cmd, + testMeshes[model_idx]->meshBuffers.indexBuffer.buffer, + 0, + VK_INDEX_TYPE_UINT32); + + vkCmdDrawIndexed(cmd, + testMeshes[model_idx]->surfaces[0].count, + 1, + testMeshes[model_idx]->surfaces[0].startIndex, + 0, 0); + vkCmdEndRendering(cmd); } @@ -711,7 +736,6 @@ void VulkanEngine::destroy_buffer(const AllocatedBuffer& buffer) vmaDestroyBuffer(_allocator, buffer.buffer, buffer.allocation); } - GPUMeshBuffers VulkanEngine::uploadMesh(std::span indices, std::span vertices) { const size_t vertexBufferSize = vertices.size() * sizeof(Vertex); @@ -796,6 +820,11 @@ void VulkanEngine::init_default_data() { rectangle = uploadMesh(rect_indices, rect_vertices); + auto basicmesh = loadGltfMeshes(this, "basicmesh.glb"); + assert(basicmesh != std::nullopt && "Failed to load basicmesh.glb"); + + testMeshes = *basicmesh; + _mainDeletionQueue.push_function([&](){ destroy_buffer(rectangle.indexBuffer); destroy_buffer(rectangle.vertexBuffer); diff --git a/vk_engine.h b/vk_engine.h index dcececa..1875146 100644 --- a/vk_engine.h +++ b/vk_engine.h @@ -6,6 +6,7 @@ #include #include #include "vk_gui.h" +#include "vk_loader.h" struct Transaction { VkCommandBuffer cmd; @@ -100,6 +101,7 @@ public: VkPipeline _meshPipeline; GPUMeshBuffers rectangle; + std::vector> testMeshes; // ZED's REFACTOR VkGUI _gui; @@ -123,6 +125,8 @@ public: Transaction begin_transaction(); void commit_transaction(Transaction& cmd); + GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); + private: void init_vulkan(); void init_swapchain(); @@ -146,5 +150,4 @@ private: void draw_background(VkCommandBuffer cmd); void draw_geometry(VkCommandBuffer cmd); - GPUMeshBuffers uploadMesh(std::span indices, std::span vertices); }; diff --git a/vk_loader.cpp b/vk_loader.cpp index a5249f1..c978f60 100644 --- a/vk_loader.cpp +++ b/vk_loader.cpp @@ -11,9 +11,131 @@ #include #include +constexpr bool OverrideColors = true; std::optional>> loadGltfMeshes(VulkanEngine* engine, std::filesystem::path filePath) { + std::println("\nLoading GLTF: {}", filePath.string()); + auto data = fastgltf::GltfDataBuffer::FromPath(filePath); - return std::nullopt; + if(data.error() != fastgltf::Error::None) { + std::println("Failed to load glTF: {}={}\n", + typeid(data.error()).name(), + fastgltf::to_underlying(data.error())); + return std::nullopt; + } + + constexpr auto gltfOptions = fastgltf::Options::LoadExternalBuffers; + + fastgltf::Asset gltf{}; + fastgltf::Parser parser{}; + + auto load = parser.loadGltfBinary(data.get(), filePath.parent_path(), gltfOptions); + + switch(load.error()) { + case fastgltf::Error::None: + gltf = std::move(load.get()); + break; + case fastgltf::Error::InvalidGLB: + std::println("fastgltf says Error::InvalidGLB {}", filePath.string()); + return std::nullopt; + break; + case fastgltf::Error::UnsupportedVersion: + std::println("fastgltf says Error::UnsupportedVersion {}", filePath.string()); + return std::nullopt; + break; + case fastgltf::Error::InvalidPath: + std::println("fastgltf says Error::UnsupportedVersion {}", + filePath.string()); + return std::nullopt; + break; + default: + std::println("Unknown fastgltf error loading {}", filePath.string()); + break; + } + + std::vector> meshes; + // use the same vectors for all meshes + std::vector indices; + std::vector vertices; + + for(auto& mesh : gltf.meshes) { + MeshAsset newmesh; + newmesh.name = mesh.name; + + indices.clear(); + indices.clear(); + + for(auto&& p : mesh.primitives) { + auto& indexAccessor = gltf.accessors[p.indicesAccessor.value()]; + + GeoSurface newSurface { + .startIndex = (uint32_t)indices.size(), + .count = (uint32_t)indexAccessor.count, + }; + + size_t initial_vtx = vertices.size(); + + // load indices + { + indices.reserve(indices.size() + indexAccessor.count); + fastgltf::iterateAccessor( + gltf, indexAccessor, [&](std::uint32_t idx) { + indices.push_back(idx + initial_vtx); + }); + } + + // load vertex positions + { + fastgltf::Accessor& posAccessor = gltf.accessors[ + p.findAttribute("POSITIONS")->accessorIndex]; + vertices.resize(vertices.size() + posAccessor.count); + + fastgltf::iterateAccessorWithIndex( + gltf, posAccessor, [&](glm::vec3 v, size_t index) + { + vertices[initial_vtx + index] = { + .position = v, + .uv_x = 0, + .normal = {1, 0, 0}, + .uv_y = 0, + .color = glm::vec4{1.0f}, + }; + }); + } + + // load vertex normals + auto normals = p.findAttribute("NORMA:"); + if(normals != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[(*normals).accessorIndex], + [&](glm::vec3 v, size_t index) + { + vertices[initial_vtx + index].normal = v; + }); + } + + auto uv = p.findAttribute("TEXCOORD_0"); + if(uv != p.attributes.end()) { + fastgltf::iterateAccessorWithIndex( + gltf, gltf.accessors[(*uv).accessorIndex], + [&](glm::vec2 v, size_t index) { + vertices[initial_vtx + index].uv_x = v.x; + vertices[initial_vtx + index].uv_y = v.y; + }); + } + + if(OverrideColors) { + for(Vertex& vtx : vertices) { + vtx.color = glm::vec4(vtx.normal, 1.0f); + } + } + + newmesh.meshBuffers = engine->uploadMesh(indices, vertices); + meshes.emplace_back(std::make_shared(std::move(newmesh))); + + } + } + + return meshes; } diff --git a/wraps/fastgltf.wrap b/wraps/fastgltf.wrap index abac338..db7da81 100644 --- a/wraps/fastgltf.wrap +++ b/wraps/fastgltf.wrap @@ -1,7 +1,7 @@ [wrap-git] directory=fastgltf url=https://github.com/spnda/fastgltf.git -revision=v0.9.x +revision=v0.9.0 depth=1 method=cmake diff --git a/wraps/simdjson.wrap b/wraps/simdjson.wrap new file mode 100644 index 0000000..a3e383c --- /dev/null +++ b/wraps/simdjson.wrap @@ -0,0 +1,13 @@ +[wrap-file] +directory = simdjson-3.3.0 +source_url = https://github.com/simdjson/simdjson/archive/refs/tags/v3.3.0.tar.gz +source_filename = simdjson-3.3.0.tar.gz +source_hash = a8c9feff2f19c3ff281d42f0b6b4b18f02236513b99229756fa9a1b14787a58a +patch_filename = simdjson_3.3.0-2_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/simdjson_3.3.0-2/get_patch +patch_hash = 3c39f8a5abac17732b9599a416e6edb09f5f987bcf4e8b46808dbe88040eb40f +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/simdjson_3.3.0-2/simdjson-3.3.0.tar.gz +wrapdb_version = 3.3.0-2 + +[provide] +simdjson = simdjson_dep