diff --git a/shaders/depthPrepass.frag b/shaders/depthPrepass.frag new file mode 100644 index 0000000..86e99d4 --- /dev/null +++ b/shaders/depthPrepass.frag @@ -0,0 +1,6 @@ +#version 450 + + +void main() { + +} \ No newline at end of file diff --git a/shaders/depthPrepass.vert b/shaders/depthPrepass.vert new file mode 100644 index 0000000..50957b6 --- /dev/null +++ b/shaders/depthPrepass.vert @@ -0,0 +1,49 @@ +#version 450 +#extension GL_EXT_buffer_reference : require + +struct Model +{ + mat4 modelMatrix; +}; + +struct Material +{ + vec4 colorFactor; + vec4 metalRoughFactors; + ivec4 textureImageIndices; + ivec4 textureSamplerIndices; + vec4 alphaCutoff; +}; + +layout (buffer_reference, std430) readonly buffer ModelData +{ + Model models[]; +}; + +layout (buffer_reference, std430) readonly buffer MaterialData +{ + Material materials[]; +}; + +layout (set = 0, binding = 0) uniform addresses +{ + MaterialData materialBufferDeviceAddress; + ModelData modelBufferDeviceAddress; +} bufferAddresses; + + +layout (location = 0) in vec3 position; + + +layout (push_constant) uniform PushConstants { + mat4 viewProj; // (64) + // (16) + // (16) + // (16) + // (16) +} pushConstants; + +void main() { + mat4 model = bufferAddresses.modelBufferDeviceAddress.models[gl_InstanceIndex].modelMatrix; + gl_Position = pushConstants.viewProj * model * vec4(position, 1.0f); +} \ No newline at end of file diff --git a/shaders/fragment.frag b/shaders/fragment.frag deleted file mode 100644 index a6a30fa..0000000 --- a/shaders/fragment.frag +++ /dev/null @@ -1,17 +0,0 @@ -#version 450 - -layout(location = 0) in vec2 TexCoord; -layout(location = 1) in vec3 Color; -layout(location = 0) out vec4 FragColor; - -layout(set = 0, binding = 0) uniform sampler2D sourceImage; - -vec3 linearTosRGB(vec3 color) { - return pow(color, vec3(1.0 / 2.2)); -} - -void main() { - FragColor = texture(sourceImage, TexCoord); - //FragColor = vec4(linearTosRGB(Color), 1.0); - //FragColor = vec4(1); -} \ No newline at end of file diff --git a/shaders/cube.frag b/shaders/pbr.frag similarity index 100% rename from shaders/cube.frag rename to shaders/pbr.frag diff --git a/shaders/cube.vert b/shaders/pbr.vert similarity index 100% rename from shaders/cube.vert rename to shaders/pbr.vert diff --git a/shaders/vertex.vert b/shaders/vertex.vert deleted file mode 100644 index 7b49c86..0000000 --- a/shaders/vertex.vert +++ /dev/null @@ -1,33 +0,0 @@ -#version 450 - -layout(location = 0) out vec2 TexCoord; -layout(location = 1) out vec3 Color; - -layout(push_constant) uniform PushConstants { - float time; -} pushConstants; - -void main() { - vec2 positions[3] = vec2[]( - vec2(-0.75, 0.75), - vec2( 0.0 , -0.75), - vec2(0.75, 0.75) - ); - - vec2 pos = positions[gl_VertexIndex]; - - TexCoord = pos * 0.5 + 0.5; - - vec3 colors[3] = vec3[]( - vec3(1.0, 0.0, 0.0), // Red - vec3(0.0, 1.0, 0.0), // Green - vec3(0.0, 0.0, 1.0) // Blue - ); - // Smooth color transition - float t = fract(pushConstants.time); - int currentColorIndex = (gl_VertexIndex + int(pushConstants.time)) % 3; - int nextColorIndex = (currentColorIndex + 1) % 3; - Color = mix(colors[currentColorIndex], colors[nextColorIndex], t); - - gl_Position = vec4(pos, 1.0, 1.0); -} \ No newline at end of file diff --git a/src/core/engine.cpp b/src/core/engine.cpp index 1a2b00f..679d6fa 100644 --- a/src/core/engine.cpp +++ b/src/core/engine.cpp @@ -213,17 +213,22 @@ void Engine::run() ImGui::Text("View Direction: (%.2f, %.2f, %.2f)", viewDir.x, viewDir.y, viewDir.z); ImGui::Text("Rotation (%.2f, %.2f, %.2f)", cameraRotation.x, cameraRotation.y, cameraRotation.z); ImGui::Text("Position: (%.2f, %.2f, %.2f)", cameraPosition.x, cameraPosition.y, cameraPosition.z); + } + ImGui::End(); - if (ImGui::TreeNode("Rotation Matrix")) { - glm::mat4 rotationMatrix = camera.getRotationMatrixWS(); - for (int i = 0; i < 4; i++) - ImGui::Text("%.2f %.2f %.2f %.2f", rotationMatrix[i][0], rotationMatrix[i][1], rotationMatrix[i][2], rotationMatrix[i][3]); - - ImGui::TreePop(); + if (ImGui::Begin("Save Depth Prepass")) { + if (ImGui::Button("Save Depth Prepass Now!")) { + std::filesystem::path path = std::filesystem::current_path() / "test.png"; + auto depthNormalize = [](float depth) { + return log(1.0f + depth * 10.0f) / log(11.0f); + }; + vk_helpers::saveGrayscaleImage(this, depthPrepassImage, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL, VK_IMAGE_ASPECT_DEPTH_BIT, + sizeof(float), path.string().c_str(), depthNormalize); } } ImGui::End(); + scene.imguiSceneGraph(); ImGui::Render(); @@ -274,8 +279,17 @@ void Engine::draw() // draw geometry into _drawImage vk_helpers::transitionImage(cmd, drawImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); vk_helpers::transitionImage(cmd, depthImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + vk_helpers::transitionImage(cmd, depthPrepassImage.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + + VkClearValue depthClearValue = {0.0f, 0.0f}; + VkRenderingAttachmentInfo depthPrepassAttachment = vk_helpers::attachmentInfo(depthPrepassImage.imageView, &depthClearValue, + VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); + VkRenderingInfo depthPrepassRenderInfo = vk_helpers::renderingInfo(drawExtent, nullptr, &depthPrepassAttachment); + vkCmdBeginRendering(cmd, &depthPrepassRenderInfo); + drawDepthPrepass(cmd); + vkCmdEndRendering(cmd); + - VkClearValue depthClearValue = {0.0f, 0}; VkRenderingAttachmentInfo colorAttachment = vk_helpers::attachmentInfo(drawImage.imageView, nullptr, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); VkRenderingAttachmentInfo depthAttachment = vk_helpers::attachmentInfo(depthImage.imageView, &depthClearValue, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL); @@ -409,6 +423,51 @@ void Engine::cullRender(VkCommandBuffer cmd) const vkCmdDispatch(cmd, static_cast(std::ceil(static_cast(testRenderObject->getInstanceBufferSize()) / 64.0f)), 1, 1); } +void Engine::drawDepthPrepass(VkCommandBuffer cmd) const +{ + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, depthPrepassPipeline); + // Dynamic States + { + // Viewport + VkViewport viewport = {}; + viewport.x = 0; + viewport.y = 0; + viewport.width = drawExtent.width; + viewport.height = drawExtent.height; + viewport.minDepth = 0.f; + viewport.maxDepth = 1.f; + vkCmdSetViewport(cmd, 0, 1, &viewport); + // Scissor + VkRect2D scissor = {}; + scissor.offset.x = 0; + scissor.offset.y = 0; + scissor.extent.width = drawExtent.width; + scissor.extent.height = drawExtent.height; + vkCmdSetScissor(cmd, 0, 1, &scissor); + } + + if (!testRenderObject->canDraw()) { + return; + } + + const glm::mat4 viewProj = camera.getViewProjMatrix(); + vkCmdPushConstants(cmd, depthPrepassPipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(glm::mat4), &viewProj); + + VkDescriptorBufferBindingInfoEXT descriptorBufferBindingInfo[1]; + descriptorBufferBindingInfo[0] = testRenderObject->getAddressesDescriptorBuffer().getDescriptorBufferBindingInfo(); + vkCmdBindDescriptorBuffersEXT(cmd, 1, descriptorBufferBindingInfo); + + constexpr VkDeviceSize zeroOffset{0}; + constexpr uint32_t addressIndex{0}; + vkCmdSetDescriptorBufferOffsetsEXT(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, depthPrepassPipelineLayout, 0, 1, &addressIndex, &zeroOffset); + + VkDeviceSize offsets{0}; + vkCmdBindVertexBuffers(cmd, 0, 1, &testRenderObject->getVertexBuffer().buffer, &offsets); + vkCmdBindIndexBuffer(cmd, testRenderObject->getIndexBuffer().buffer, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexedIndirect(cmd, testRenderObject->getIndirectBuffer().buffer, 0, testRenderObject->getInstanceBufferSize(), + sizeof(VkDrawIndexedIndirectCommand)); +} + void Engine::drawRender(VkCommandBuffer cmd) const { vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, renderPipeline); @@ -518,6 +577,9 @@ void Engine::cleanup() vmaDestroyImage(allocator, drawImage.image, drawImage.allocation); vkDestroyImageView(device, depthImage.imageView, nullptr); vmaDestroyImage(allocator, depthImage.image, depthImage.allocation); + vkDestroyImageView(device, depthPrepassImage.imageView, nullptr); + vmaDestroyImage(allocator, depthPrepassImage.image, depthPrepassImage.allocation); + // Swapchain vkDestroySwapchainKHR(device, swapchain, nullptr); for (int i = 0; i < swapchainImageViews.size(); i++) { @@ -774,6 +836,7 @@ void Engine::initDescriptors() void Engine::initPipelines() { initEnvironmentPipeline(); + initDepthPrepassPipeline(); initRenderPipeline(); initFrustumCullingPipeline(); } @@ -826,9 +889,69 @@ void Engine::initFrustumCullingPipeline() }); } +void Engine::initDepthPrepassPipeline() +{ + VkPipelineLayoutCreateInfo layoutInfo = vk_helpers::pipelineLayoutCreateInfo(); + VkDescriptorSetLayout descriptorLayout[1]; + descriptorLayout[0] = RenderObject::addressesDescriptorSetLayout; + layoutInfo.pSetLayouts = descriptorLayout; + layoutInfo.setLayoutCount = 1; + VkPushConstantRange pushConstants{}; + pushConstants.offset = 0; + pushConstants.size = sizeof(glm::mat4); + pushConstants.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + layoutInfo.pPushConstantRanges = &pushConstants; + layoutInfo.pushConstantRangeCount = 1; + + VK_CHECK(vkCreatePipelineLayout(device, &layoutInfo, nullptr, &depthPrepassPipelineLayout)); + + VkShaderModule vertShader; + if (!vk_helpers::loadShaderModule("shaders/depthPrepass.vert.spv", device, &vertShader)) { + throw std::runtime_error("Error when building the cube vertex shader module(depthPrepass.vert.spv)\n"); + } + VkShaderModule fragShader; + if (!vk_helpers::loadShaderModule("shaders/depthPrepass.frag.spv", device, &fragShader)) { + throw std::runtime_error("Error when building the cube vertex shader module(depthPrepass.frag.spv)\n"); + } + + PipelineBuilder depthPrepassPipelineBuilder; { + VkVertexInputBindingDescription mainBinding{}; + mainBinding.binding = 0; + mainBinding.stride = sizeof(Vertex); + mainBinding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription vertexAttributes[1]; + vertexAttributes[0].binding = 0; + vertexAttributes[0].location = 0; + vertexAttributes[0].format = VK_FORMAT_R32G32B32_SFLOAT; + vertexAttributes[0].offset = offsetof(Vertex, position); + + depthPrepassPipelineBuilder.setupVertexInput(&mainBinding, 1, vertexAttributes, 1); + } + + depthPrepassPipelineBuilder.setShaders(vertShader, fragShader); + depthPrepassPipelineBuilder.setupInputAssembly(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST); + depthPrepassPipelineBuilder.setupRasterization(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE); // VK_CULL_MODE_BACK_BIT + depthPrepassPipelineBuilder.disableMultisampling(); + depthPrepassPipelineBuilder.setupBlending(PipelineBuilder::BlendMode::NO_BLEND); + depthPrepassPipelineBuilder.enableDepthtest(true, VK_COMPARE_OP_GREATER_OR_EQUAL); + depthPrepassPipelineBuilder.setupRenderer(VK_FORMAT_UNDEFINED, depthPrepassImage.imageFormat); + depthPrepassPipelineBuilder.setupPipelineLayout(depthPrepassPipelineLayout); + + depthPrepassPipeline = depthPrepassPipelineBuilder.buildPipeline(device, VK_PIPELINE_CREATE_DESCRIPTOR_BUFFER_BIT_EXT); + + vkDestroyShaderModule(device, vertShader, nullptr); + vkDestroyShaderModule(device, fragShader, nullptr); + + mainDeletionQueue.pushFunction([&]() { + vkDestroyPipelineLayout(device, depthPrepassPipelineLayout, nullptr); + vkDestroyPipeline(device, depthPrepassPipeline, nullptr); + }); +} + void Engine::initRenderPipeline() { - VkPipelineLayoutCreateInfo layout_info = vk_helpers::pipelineLayoutCreateInfo(); + VkPipelineLayoutCreateInfo layoutInfo = vk_helpers::pipelineLayoutCreateInfo(); VkDescriptorSetLayout descriptorLayout[4]; assert(RenderObject::addressesDescriptorSetLayout != VK_NULL_HANDLE); @@ -838,28 +961,28 @@ void Engine::initRenderPipeline() descriptorLayout[0] = RenderObject::addressesDescriptorSetLayout; descriptorLayout[1] = RenderObject::textureDescriptorSetLayout; - descriptorLayout[2] = sceneUniformDescriptorSetLayout; + descriptorLayout[2] = sceneUniformDescriptorSetLayout; // currently not used. descriptorLayout[3] = Environment::environmentMapDescriptorSetLayout; - layout_info.pSetLayouts = descriptorLayout; - layout_info.setLayoutCount = 4; + layoutInfo.pSetLayouts = descriptorLayout; + layoutInfo.setLayoutCount = 4; VkPushConstantRange pushConstants{}; pushConstants.offset = 0; pushConstants.size = sizeof(SceneData); pushConstants.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; - layout_info.pPushConstantRanges = &pushConstants; - layout_info.pushConstantRangeCount = 1; + layoutInfo.pPushConstantRanges = &pushConstants; + layoutInfo.pushConstantRangeCount = 1; - VK_CHECK(vkCreatePipelineLayout(device, &layout_info, nullptr, &renderPipelineLayout)); + VK_CHECK(vkCreatePipelineLayout(device, &layoutInfo, nullptr, &renderPipelineLayout)); VkShaderModule vertShader; - if (!vk_helpers::loadShaderModule("shaders/cube.vert.spv", device, &vertShader)) { - throw std::runtime_error("Error when building the cube vertex shader module(cube.vert.spv)\n"); + if (!vk_helpers::loadShaderModule("shaders/pbr.vert.spv", device, &vertShader)) { + throw std::runtime_error("Error when building the cube vertex shader module(pbr.vert.spv)\n"); } VkShaderModule fragShader; - if (!vk_helpers::loadShaderModule("shaders/cube.frag.spv", device, &fragShader)) { - fmt::print("Error when building the cube fragment shader module(cube.frag.spv)\n"); + if (!vk_helpers::loadShaderModule("shaders/pbr.frag.spv", device, &fragShader)) { + fmt::print("Error when building the cube fragment shader module(pbr.frag.spv)\n"); } PipelineBuilder renderPipelineBuilder; { @@ -1050,6 +1173,11 @@ AllocatedBuffer Engine::createStagingBuffer(size_t allocSize) const return createBuffer(allocSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU); } +AllocatedBuffer Engine::createReceivingBuffer(size_t allocSize) const +{ + return createBuffer(allocSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU); +} + void Engine::copyBuffer(const AllocatedBuffer& src, const AllocatedBuffer& dst, const VkDeviceSize size) const { immediateSubmit([&](VkCommandBuffer cmd) { @@ -1257,12 +1385,12 @@ void Engine::createDrawImages(uint32_t width, uint32_t height) drawImageUsages |= VK_IMAGE_USAGE_STORAGE_BIT; drawImageUsages |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; drawImageUsages |= VK_IMAGE_USAGE_SAMPLED_BIT; - VkImageCreateInfo rimg_info = vk_helpers::imageCreateInfo(drawImage.imageFormat, drawImageUsages, drawImageExtent); + VkImageCreateInfo renderImageInfo = vk_helpers::imageCreateInfo(drawImage.imageFormat, drawImageUsages, drawImageExtent); - VmaAllocationCreateInfo rimg_allocinfo = {}; - rimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - rimg_allocinfo.requiredFlags = static_cast(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - vmaCreateImage(allocator, &rimg_info, &rimg_allocinfo, &drawImage.image, &drawImage.allocation, nullptr); + VmaAllocationCreateInfo renderImageAllocationInfo = {}; + renderImageAllocationInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + renderImageAllocationInfo.requiredFlags = static_cast(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vmaCreateImage(allocator, &renderImageInfo, &renderImageAllocationInfo, &drawImage.image, &drawImage.allocation, nullptr); VkImageViewCreateInfo rview_info = vk_helpers::imageviewCreateInfo(drawImage.imageFormat, drawImage.image, VK_IMAGE_ASPECT_COLOR_BIT); VK_CHECK(vkCreateImageView(device, &rview_info, nullptr, &drawImage.imageView)); @@ -1274,15 +1402,24 @@ void Engine::createDrawImages(uint32_t width, uint32_t height) depthImage.imageExtent = depthImageExtent; VkImageUsageFlags depthImageUsages{}; depthImageUsages |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; - VkImageCreateInfo dimg_info = vk_helpers::imageCreateInfo(depthImage.imageFormat, depthImageUsages, depthImageExtent); - - VmaAllocationCreateInfo dimg_allocinfo = {}; - dimg_allocinfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; - dimg_allocinfo.requiredFlags = static_cast(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - vmaCreateImage(allocator, &dimg_info, &dimg_allocinfo, &depthImage.image, &depthImage.allocation, nullptr); - - VkImageViewCreateInfo dview_info = vk_helpers::imageviewCreateInfo(depthImage.imageFormat, depthImage.image, VK_IMAGE_ASPECT_DEPTH_BIT); - VK_CHECK(vkCreateImageView(device, &dview_info, nullptr, &depthImage.imageView)); + VkImageCreateInfo depthImageInfo = vk_helpers::imageCreateInfo(depthImage.imageFormat, depthImageUsages, depthImageExtent); + + VmaAllocationCreateInfo depthImageAllocationInfo = {}; + depthImageAllocationInfo.usage = VMA_MEMORY_USAGE_GPU_ONLY; + depthImageAllocationInfo.requiredFlags = static_cast(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + vmaCreateImage(allocator, &depthImageInfo, &depthImageAllocationInfo, &depthImage.image, &depthImage.allocation, nullptr); + + VkImageViewCreateInfo depthViewInfo = vk_helpers::imageviewCreateInfo(depthImage.imageFormat, depthImage.image, VK_IMAGE_ASPECT_DEPTH_BIT); + VK_CHECK(vkCreateImageView(device, &depthViewInfo, nullptr, &depthImage.imageView)); + + // Depth Prepass + depthPrepassImage.imageFormat = VK_FORMAT_D32_SFLOAT; + depthPrepassImage.imageExtent = depthImageExtent; + depthImageUsages |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT; + depthImageInfo = vk_helpers::imageCreateInfo(depthImage.imageFormat, depthImageUsages, depthImageExtent); + vmaCreateImage(allocator, &depthImageInfo, &depthImageAllocationInfo, &depthPrepassImage.image, &depthPrepassImage.allocation, nullptr); + VkImageViewCreateInfo prepassDepthViewInfo = vk_helpers::imageviewCreateInfo(depthPrepassImage.imageFormat, depthPrepassImage.image, VK_IMAGE_ASPECT_DEPTH_BIT); + VK_CHECK(vkCreateImageView(device, &prepassDepthViewInfo, nullptr, &depthPrepassImage.imageView)); drawExtent = {width, height}; } diff --git a/src/core/engine.h b/src/core/engine.h index bbf7a7a..a56e283 100644 --- a/src/core/engine.h +++ b/src/core/engine.h @@ -83,6 +83,8 @@ class Engine void cullRender(VkCommandBuffer cmd) const; + void drawDepthPrepass(VkCommandBuffer cmd) const; + void drawRender(VkCommandBuffer cmd) const; void drawImgui(VkCommandBuffer cmd, VkImageView targetImageView); @@ -119,6 +121,8 @@ class Engine void initFrustumCullingPipeline(); + void initDepthPrepassPipeline(); + void initRenderPipeline(); void initEnvironmentPipeline(); @@ -189,20 +193,22 @@ class Engine DescriptorBufferSampler sceneSamplerDescriptorBuffer; private: // Pipelines - // Render - VkPipelineLayout renderPipelineLayout{VK_NULL_HANDLE}; - VkPipeline renderPipeline{VK_NULL_HANDLE}; - // Frustum Culling VkPipelineLayout frustumCullingPipelineLayout{VK_NULL_HANDLE}; VkPipeline frustumCullingPipeline{VK_NULL_HANDLE}; + // Depth Prepass + VkPipelineLayout depthPrepassPipelineLayout{VK_NULL_HANDLE}; + VkPipeline depthPrepassPipeline{VK_NULL_HANDLE}; + + // Render + VkPipelineLayout renderPipelineLayout{VK_NULL_HANDLE}; + VkPipeline renderPipeline{VK_NULL_HANDLE}; // Environment VkPipelineLayout environmentPipelineLayout{VK_NULL_HANDLE}; VkPipeline environmentPipeline{VK_NULL_HANDLE}; - private: // Swapchain VkSwapchainKHR swapchain{}; VkFormat swapchainImageFormat{}; @@ -226,6 +232,8 @@ class Engine void createDrawImages(uint32_t width, uint32_t height); + AllocatedImage depthPrepassImage{}; + public: // Default Data static AllocatedImage whiteImage; static AllocatedImage errorCheckerboardImage; @@ -240,6 +248,8 @@ class Engine AllocatedBuffer createStagingBuffer(size_t allocSize) const; + AllocatedBuffer createReceivingBuffer(size_t allocSize) const; + /** * * @param src diff --git a/src/core/environment.cpp b/src/core/environment.cpp index 9fe171d..25ff589 100644 --- a/src/core/environment.cpp +++ b/src/core/environment.cpp @@ -18,6 +18,9 @@ VkDescriptorSetLayout Environment::equiImageDescriptorSetLayout = VK_NULL_HANDLE VkDescriptorSetLayout Environment::cubemapStorageDescriptorSetLayout = VK_NULL_HANDLE; VkDescriptorSetLayout Environment::cubemapDescriptorSetLayout = VK_NULL_HANDLE; VkDescriptorSetLayout Environment::lutDescriptorSetLayout = VK_NULL_HANDLE; +/** + * Diff/Spec Irradiance Cubemap (LOD 1-4 spec, LOD 5 diff), and 2D-LUT + */ VkDescriptorSetLayout Environment::environmentMapDescriptorSetLayout = VK_NULL_HANDLE; VkPipelineLayout Environment::equiToCubemapPipelineLayout = VK_NULL_HANDLE; diff --git a/src/core/render_object.cpp b/src/core/render_object.cpp index f434730..834815a 100644 --- a/src/core/render_object.cpp +++ b/src/core/render_object.cpp @@ -1,6 +1,5 @@ // // Created by William on 2024-08-24. -// #include "render_object.h" @@ -14,8 +13,17 @@ #include "glm/detail/type_quat.hpp" #include "glm/gtx/matrix_decompose.hpp" +/** + * Material and Instance Buffer Addresses + */ VkDescriptorSetLayout RenderObject::addressesDescriptorSetLayout{VK_NULL_HANDLE}; +/** + * Sampler and Image Arrays + */ VkDescriptorSetLayout RenderObject::textureDescriptorSetLayout{VK_NULL_HANDLE}; +/** + * Frustum Culling Data Buffer Addresses + */ VkDescriptorSetLayout RenderObject::frustumCullingDescriptorSetLayout{VK_NULL_HANDLE}; int RenderObject::renderObjectCount{0}; diff --git a/src/core/scene.cpp b/src/core/scene.cpp index e29c8fe..7ae244c 100644 --- a/src/core/scene.cpp +++ b/src/core/scene.cpp @@ -187,11 +187,11 @@ void Scene::imguiSceneGraph() ImGui::End(); - if (ImGui::Begin("Test")) { + /*if (ImGui::Begin("Gameobject Selection")) { // can select a gameobject // text fields to modify the model matrix of said gameobject } - ImGui::End(); + ImGui::End();*/ } int Scene::getIndexInVector(GameObject* obj, std::vector vector) diff --git a/src/renderer/vk_helpers.cpp b/src/renderer/vk_helpers.cpp index 864afea..48627a0 100644 --- a/src/renderer/vk_helpers.cpp +++ b/src/renderer/vk_helpers.cpp @@ -6,6 +6,7 @@ #include #include +#include #include "../core/engine.h" @@ -213,7 +214,7 @@ VkRenderingInfo vk_helpers::renderingInfo(VkExtent2D renderExtent, VkRenderingAt renderInfo.renderArea = VkRect2D{VkOffset2D{0, 0}, renderExtent}; renderInfo.layerCount = 1; - renderInfo.colorAttachmentCount = 1; + renderInfo.colorAttachmentCount = colorAttachment == nullptr ? 0 : 1; renderInfo.pColorAttachments = colorAttachment; renderInfo.pDepthAttachment = depthAttachment; renderInfo.pStencilAttachment = nullptr; @@ -286,6 +287,33 @@ void vk_helpers::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayo vkCmdPipelineBarrier2(cmd, &depInfo); } +void vk_helpers::transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageAspectFlags aspectMask, + VkImageLayout targetLayout) +{ + VkImageMemoryBarrier2 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 = targetLayout; + + imageBarrier.subresourceRange = imageSubresourceRange(aspectMask); + imageBarrier.image = image; + + VkDependencyInfo depInfo{}; + depInfo.sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO; + depInfo.pNext = nullptr; + + depInfo.imageMemoryBarrierCount = 1; + depInfo.pImageMemoryBarriers = &imageBarrier; + + vkCmdPipelineBarrier2(cmd, &depInfo); +} + void vk_helpers::copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize) { VkImageBlit2 blitRegion{.sType = VK_STRUCTURE_TYPE_IMAGE_BLIT_2, .pNext = nullptr}; @@ -569,7 +597,8 @@ std::optional vk_helpers::loadImage(const Engine* engine, const } }, [&](const fastgltf::sources::Array& vector) { - unsigned char* data = stbi_load_from_memory(reinterpret_cast(vector.bytes.data()), static_cast(vector.bytes.size()), &width, &height, &nrChannels, 4); + unsigned char* data = stbi_load_from_memory(reinterpret_cast(vector.bytes.data()), + static_cast(vector.bytes.size()), &width, &height, &nrChannels, 4); if (data) { VkExtent3D imagesize; imagesize.width = width; @@ -591,7 +620,9 @@ std::optional vk_helpers::loadImage(const Engine* engine, const std::visit(fastgltf::visitor{ [](auto& arg) {}, [&](const fastgltf::sources::Array& vector) { - unsigned char* data = stbi_load_from_memory(reinterpret_cast(vector.bytes.data() + bufferView.byteOffset), static_cast(bufferView.byteLength), &width, &height, &nrChannels, 4); + unsigned char* data = stbi_load_from_memory( + reinterpret_cast(vector.bytes.data() + bufferView.byteOffset), + static_cast(bufferView.byteLength), &width, &height, &nrChannels, 4); if (data) { VkExtent3D imagesize; imagesize.width = width; @@ -633,3 +664,98 @@ void vk_helpers::loadTexture(const fastgltf::Optional& te samplerIndex = gltf.textures[textureIndex].samplerIndex.value() + samplerOffset; } } + +void vk_helpers::saveImage(const Engine* engine, const AllocatedImage& image, VkImageLayout imageLayout, VkImageAspectFlags aspectFlag, + size_t formatSize, uint32_t channelCount, const char* savePath) +{ + const size_t dataSize = image.imageExtent.width * image.imageExtent.height * channelCount * formatSize; + AllocatedBuffer receivingBuffer = engine->createReceivingBuffer(dataSize); + + engine->immediateSubmit([&](VkCommandBuffer cmd) { + VkBufferImageCopy bufferCopyRegion{}; + bufferCopyRegion.imageSubresource.aspectMask = aspectFlag; + bufferCopyRegion.imageSubresource.mipLevel = 0; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent = image.imageExtent; + bufferCopyRegion.bufferOffset = 0; + bufferCopyRegion.bufferRowLength = 0; + bufferCopyRegion.bufferImageHeight = 0; + + vk_helpers::transitionImage(cmd, image.image, imageLayout, aspectFlag, VK_IMAGE_LAYOUT_GENERAL); + + vkCmdCopyImageToBuffer(cmd, image.image, VK_IMAGE_LAYOUT_GENERAL, receivingBuffer.buffer, 1, &bufferCopyRegion); + + vk_helpers::transitionImage(cmd, image.image, VK_IMAGE_LAYOUT_GENERAL, imageLayout); + }); + + void* data = receivingBuffer.info.pMappedData; + const auto imageData = static_cast(data); + + const auto byteImageData = new uint8_t[image.imageExtent.width * image.imageExtent.height * 4]; + constexpr auto powEight = static_cast(pow(2, 8) - 1); + for (size_t i = 0; i < image.imageExtent.width * image.imageExtent.height; ++i) { + for (int j = 0; j < channelCount; j++) { + const auto value = static_cast(imageData[i * channelCount] * powEight); + byteImageData[i * 4 + j] = value; + } + // 4 channels target + const int remaining = 4 - channelCount; + for (int j = remaining; j < channelCount; j++) { + const auto value = static_cast(imageData[i * channelCount] * powEight); + byteImageData[i * 4 + j] = value; + } + + byteImageData[i * 4 + 3] = 255; + } + + + stbi_write_png(savePath, image.imageExtent.width, image.imageExtent.height, 4, byteImageData, image.imageExtent.width * 4); + + delete[] byteImageData; + engine->destroyBuffer(receivingBuffer); +} + +void vk_helpers::saveGrayscaleImage(const Engine* engine, const AllocatedImage& image, VkImageLayout imageLayout, VkImageAspectFlags aspectFlag, + size_t formatSize, const char* savePath, const std::function& valueTransform) +{ + const size_t dataSize = image.imageExtent.width * image.imageExtent.height * 1 * formatSize; + AllocatedBuffer receivingBuffer = engine->createReceivingBuffer(dataSize); + + engine->immediateSubmit([&](VkCommandBuffer cmd) { + VkBufferImageCopy bufferCopyRegion{}; + bufferCopyRegion.imageSubresource.aspectMask = aspectFlag; + bufferCopyRegion.imageSubresource.mipLevel = 0; + bufferCopyRegion.imageSubresource.baseArrayLayer = 0; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent = image.imageExtent; + bufferCopyRegion.bufferOffset = 0; + bufferCopyRegion.bufferRowLength = 0; + bufferCopyRegion.bufferImageHeight = 0; + + vk_helpers::transitionImage(cmd, image.image, imageLayout, aspectFlag, VK_IMAGE_LAYOUT_GENERAL); + + vkCmdCopyImageToBuffer(cmd, image.image, VK_IMAGE_LAYOUT_GENERAL, receivingBuffer.buffer, 1, &bufferCopyRegion); + + vk_helpers::transitionImage(cmd, image.image, VK_IMAGE_LAYOUT_GENERAL, imageLayout); + }); + + void* data = receivingBuffer.info.pMappedData; + const auto imageData = static_cast(data); + + const auto byteImageData = new uint8_t[image.imageExtent.width * image.imageExtent.height * 4]; + constexpr auto powEight = static_cast(pow(2, 8) - 1); + for (size_t i = 0; i < image.imageExtent.width * image.imageExtent.height; ++i) { + const float floatValue = valueTransform(imageData[i]); + const auto value = static_cast(floatValue * powEight); + byteImageData[i * 4 + 0] = value; + byteImageData[i * 4 + 1] = value; + byteImageData[i * 4 + 2] = value; + byteImageData[i * 4 + 3] = 255; + } + + stbi_write_png(savePath, image.imageExtent.width, image.imageExtent.height, 4, byteImageData, image.imageExtent.width * 4); + + delete[] byteImageData; + engine->destroyBuffer(receivingBuffer); +} diff --git a/src/renderer/vk_helpers.h b/src/renderer/vk_helpers.h index 49f1d7a..7b9d0a8 100644 --- a/src/renderer/vk_helpers.h +++ b/src/renderer/vk_helpers.h @@ -51,12 +51,12 @@ namespace vk_helpers VkSemaphoreSubmitInfo semaphoreSubmitInfo(VkPipelineStageFlags2 stageMask, VkSemaphore semaphore); - VkRenderingAttachmentInfo attachmentInfo(VkImageView view, VkClearValue *clear, + VkRenderingAttachmentInfo attachmentInfo(VkImageView view, VkClearValue* clear, VkImageLayout layout /*= VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL*/); - VkRenderingInfo renderingInfo(VkExtent2D renderExtent, VkRenderingAttachmentInfo *colorAttachment, VkRenderingAttachmentInfo *depthAttachment); + VkRenderingInfo renderingInfo(VkExtent2D renderExtent, VkRenderingAttachmentInfo* colorAttachment, VkRenderingAttachmentInfo* depthAttachment); - VkSubmitInfo2 submitInfo(VkCommandBufferSubmitInfo *cmd, VkSemaphoreSubmitInfo *signalSemaphoreInfo, VkSemaphoreSubmitInfo *waitSemaphoreInfo); + VkSubmitInfo2 submitInfo(VkCommandBufferSubmitInfo* cmd, VkSemaphoreSubmitInfo* signalSemaphoreInfo, VkSemaphoreSubmitInfo* waitSemaphoreInfo); VkPresentInfoKHR presentInfo(); @@ -80,6 +80,8 @@ namespace vk_helpers void transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageLayout targetLayout); + void transitionImage(VkCommandBuffer cmd, VkImage image, VkImageLayout currentLayout, VkImageAspectFlags aspectMask, VkImageLayout targetLayout); + void copyImageToImage(VkCommandBuffer cmd, VkImage source, VkImage destination, VkExtent2D srcSize, VkExtent2D dstSize); void generateMipmaps(VkCommandBuffer cmd, VkImage image, VkExtent2D imageSize); @@ -88,7 +90,8 @@ namespace vk_helpers VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo(); VkPipelineShaderStageCreateInfo pipelineShaderStageCreateInfo(VkShaderStageFlagBits stage, VkShaderModule shaderModule, - const char *entry = "main"); + const char* entry = "main"); + /** * Loads a shader module from the file paths specified * @param filePath @@ -96,14 +99,15 @@ namespace vk_helpers * @param outShaderModule * @return */ - bool loadShaderModule(const char *filePath, VkDevice device, VkShaderModule *outShaderModule); + bool loadShaderModule(const char* filePath, VkDevice device, VkShaderModule* outShaderModule); VkFilter extractFilter(fastgltf::Filter filter); VkSamplerMipmapMode extractMipmapMode(fastgltf::Filter filter); - std::optional loadImage(const Engine* engine, const fastgltf::Asset& asset, const fastgltf::Image& image, const std::filesystem::path& parentFolder); + std::optional loadImage(const Engine* engine, const fastgltf::Asset& asset, const fastgltf::Image& image, + const std::filesystem::path& parentFolder); /** * Loads a fastgltf texture. @@ -114,8 +118,35 @@ namespace vk_helpers * @param imageOffset * @param samplerOffset */ - void loadTexture(const fastgltf::Optional& texture, const fastgltf::Asset& gltf, float& imageIndex, float& samplerIndex, uint32_t + void loadTexture(const fastgltf::Optional& texture, const fastgltf::Asset& gltf, float& imageIndex, float& samplerIndex, + uint32_t imageOffset = 0, uint32_t samplerOffset = 0); + + /** + * Saves the AllocatedImage to the specified save path. + * @param engine + * @param image + * @param imageLayout the image layout at the time of copying, usually VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL for depth + * @param aspectFlag usually VK_IMAGE_ASPECT_COLOR_BIT, VK_IMAGE_ASPECT_DEPTH_BIT for depth + * @param formatSize usually \code sizeof(float)\endcode, depends on aspectFlag + * @param channelCount usually 4, 1 for depth + * @param savePath + */ + void saveImage(const Engine* engine, const AllocatedImage& image, VkImageLayout imageLayout, VkImageAspectFlags aspectFlag, size_t formatSize, + uint32_t channelCount, const char* savePath); + + /** + * Save the Allocated image as a grayscaled image. The image must be a format with only 1 channel (e.g. R32 or D32) + * @param engine + * @param image + * @param imageLayout + * @param aspectFlag + * @param formatSize + * @param savePath + * @param valueTransform function applied to the color value before it is converted into 8 bits + */ + void saveGrayscaleImage(const Engine* engine, const AllocatedImage& image, VkImageLayout imageLayout, VkImageAspectFlags aspectFlag, + size_t formatSize, const char* savePath, const std::function& valueTransform); } diff --git a/src/renderer/vk_pipelines.cpp b/src/renderer/vk_pipelines.cpp index fc3a3c1..0810cb6 100644 --- a/src/renderer/vk_pipelines.cpp +++ b/src/renderer/vk_pipelines.cpp @@ -80,6 +80,15 @@ VkPipeline PipelineBuilder::buildPipeline(VkDevice device, VkPipelineCreateFlagB } } +void PipelineBuilder::setShaders(VkShaderModule vertexShader) +{ + shaderStages.clear(); + + shaderStages.push_back( + vk_helpers::pipelineShaderStageCreateInfo(VK_SHADER_STAGE_VERTEX_BIT, vertexShader) + ); +} + void PipelineBuilder::setShaders(VkShaderModule vertexShader, VkShaderModule fragmentShader) { shaderStages.clear(); @@ -109,7 +118,7 @@ void PipelineBuilder::setupInputAssembly(VkPrimitiveTopology topology) inputAssembly.primitiveRestartEnable = VK_FALSE; } -void PipelineBuilder::setupRasterization(VkPolygonMode polygonMode, VkCullModeFlags cullMode, VkFrontFace frontFace) +void PipelineBuilder::setupRasterization(const VkPolygonMode polygonMode, const VkCullModeFlags cullMode, const VkFrontFace frontFace, bool rasterizerDiscardEnable) { // Draw Mode rasterizer.polygonMode = polygonMode; @@ -118,6 +127,8 @@ void PipelineBuilder::setupRasterization(VkPolygonMode polygonMode, VkCullModeFl // Culling rasterizer.cullMode = cullMode; rasterizer.frontFace = frontFace; + + rasterizer.rasterizerDiscardEnable = rasterizerDiscardEnable; } void PipelineBuilder::setupMultisampling(VkBool32 sampleShadingEnable, VkSampleCountFlagBits rasterizationSamples, float minSampleShading, @@ -136,12 +147,16 @@ void PipelineBuilder::setupMultisampling(VkBool32 sampleShadingEnable, VkSampleC void PipelineBuilder::setupRenderer(VkFormat colorattachmentFormat, VkFormat depthAttachmentFormat) { // Color Format - colorAttachmentFormat = colorattachmentFormat; - renderInfo.colorAttachmentCount = 1; - renderInfo.pColorAttachmentFormats = &colorAttachmentFormat; + if (colorattachmentFormat != VK_FORMAT_UNDEFINED) { + colorAttachmentFormat = colorattachmentFormat; + renderInfo.colorAttachmentCount = 1; + renderInfo.pColorAttachmentFormats = &colorAttachmentFormat; + } // Depth Format - renderInfo.depthAttachmentFormat = depthAttachmentFormat; + if (depthAttachmentFormat != VK_FORMAT_UNDEFINED) { + renderInfo.depthAttachmentFormat = depthAttachmentFormat; + } } void PipelineBuilder::setupDepthStencil(VkBool32 depthTestEnable, VkBool32 depthWriteEnable, VkCompareOp compareOp, VkBool32 depthBoundsTestEnable, diff --git a/src/renderer/vk_pipelines.h b/src/renderer/vk_pipelines.h index a9860f0..f43faaf 100644 --- a/src/renderer/vk_pipelines.h +++ b/src/renderer/vk_pipelines.h @@ -28,6 +28,8 @@ class PipelineBuilder VkPipeline buildPipeline(VkDevice device, VkPipelineCreateFlagBits flags); + void setShaders(VkShaderModule vertexShader); + void setShaders(VkShaderModule vertexShader, VkShaderModule fragmentShader); /** @@ -42,12 +44,17 @@ class PipelineBuilder void setupInputAssembly(VkPrimitiveTopology topology); - void setupRasterization(VkPolygonMode polygonMode, VkCullModeFlags cullMode, VkFrontFace frontFace); + void setupRasterization(VkPolygonMode polygonMode, VkCullModeFlags cullMode, VkFrontFace frontFace, bool rasterizerDiscardEnable = false); void setupMultisampling(VkBool32 sampleShadingEnable, VkSampleCountFlagBits rasterizationSamples , float minSampleShading, const VkSampleMask* pSampleMask , VkBool32 alphaToCoverageEnable, VkBool32 alphaToOneEnable); + /** + * Defines the renderer attachment format. If either is set to \code VK_FORMAT_UNDEFINED\endcode then it won't be set up. + * @param colorattachmentFormat + * @param depthAttachmentFormat + */ void setupRenderer(VkFormat colorattachmentFormat, VkFormat depthAttachmentFormat); /**