From a084d8fe4589bf921ac96a14cbc4059e4fef36ad Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Wed, 17 Oct 2018 10:06:38 -0400 Subject: [PATCH] Add post processing pass --- CMakeLists.txt | 5 +- include/postpass.h | 29 ++++++++ include/renderer.h | 11 +++ shaders/post.frag | 11 +++ shaders/post.vert | 9 +++ src/postpass.cpp | 167 +++++++++++++++++++++++++++++++++++++++++++++ src/renderer.cpp | 20 ++++++ src/worldpass.cpp | 2 +- 8 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 include/postpass.h create mode 100644 shaders/post.frag create mode 100644 shaders/post.vert create mode 100644 src/postpass.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 89f847a..4fc96ce 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,8 @@ endif() add_executable(Graph src/main.cpp src/renderer.cpp - src/worldpass.cpp) + src/worldpass.cpp + src/postpass.cpp) target_link_libraries(Graph PUBLIC SDL2::SDL2 SDL2::SDL2main Vulkan::Vulkan assimp::assimp) target_include_directories(Graph PUBLIC include) @@ -32,6 +33,8 @@ endmacro() compile_shader(triangle.vert) compile_shader(triangle.frag) +compile_shader(post.vert) +compile_shader(post.frag) add_custom_target(BuildShaders DEPENDS ${SPV_FILES}) add_dependencies(Graph BuildShaders) diff --git a/include/postpass.h b/include/postpass.h new file mode 100644 index 0000000..3654617 --- /dev/null +++ b/include/postpass.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +class Renderer; +struct RenderTarget; + +class PostPass { +public: + PostPass(Renderer& renderer); + + void render(VkCommandBuffer commandBuffer, RenderTarget* target); + + void createDescriptorSet(RenderTarget* target); + +private: + void createDescriptorSetLayout(); + void createPipeline(); + void createSampler(); + + Renderer& renderer_; + + VkDescriptorSetLayout setLayout_ = nullptr; + + VkPipelineLayout pipelineLayout_ = nullptr; + VkPipeline pipeline_ = nullptr; + + VkSampler offscreenSampler_ = nullptr; +}; diff --git a/include/renderer.h b/include/renderer.h index 1ef2ffb..fedbec4 100644 --- a/include/renderer.h +++ b/include/renderer.h @@ -3,6 +3,7 @@ #include #include "worldpass.h" +#include "postpass.h" struct RenderTarget { VkSurfaceKHR surface = nullptr; @@ -25,6 +26,8 @@ struct RenderTarget { VkSemaphore imageAvailableSemaphore = nullptr; VkSemaphore renderFinishedSemaphore = nullptr; VkFence* fences = nullptr; + + VkDescriptorSet* postSets = nullptr; }; class World; @@ -59,6 +62,10 @@ public: return presentationRenderPass_; } + VkDescriptorPool getDescriptorPool() const { + return descriptorPool_; + } + private: void createInstance(); #ifdef DEBUG @@ -67,6 +74,7 @@ private: void createLogicalDevice(); void createCommandPool(); void createPresentationRenderPass(); + void createDescriptorPool(); VkInstance instance_ = nullptr; @@ -94,5 +102,8 @@ private: VkRenderPass presentationRenderPass_ = nullptr; + VkDescriptorPool descriptorPool_ = nullptr; + WorldPass* worldPass_ = nullptr; + PostPass* postPass_ = nullptr; }; diff --git a/shaders/post.frag b/shaders/post.frag new file mode 100644 index 0000000..d800253 --- /dev/null +++ b/shaders/post.frag @@ -0,0 +1,11 @@ +#version 460 core + +layout(location = 0) in vec2 inUV; + +layout(location = 0) out vec4 outColor; + +layout(binding = 0) uniform sampler2D offscreenSampler; + +void main() { + outColor = texture(offscreenSampler, inUV); +} diff --git a/shaders/post.vert b/shaders/post.vert new file mode 100644 index 0000000..de04ddb --- /dev/null +++ b/shaders/post.vert @@ -0,0 +1,9 @@ +#version 460 core + +layout(location = 0) out vec2 outUV; + +void main() { + outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); + gl_Position = vec4(outUV * 2.0f + -1.0f, 0.0f, 1.0f); +} + diff --git a/src/postpass.cpp b/src/postpass.cpp new file mode 100644 index 0000000..36e013b --- /dev/null +++ b/src/postpass.cpp @@ -0,0 +1,167 @@ +#include "postpass.h" + +#include + +#include "renderer.h" + +PostPass::PostPass(Renderer& renderer) : renderer_(renderer) { + createDescriptorSetLayout(); + createPipeline(); + createSampler(); +} + +void PostPass::render(VkCommandBuffer commandBuffer, RenderTarget* target) { + vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_); + vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout_, 0, 1, &target->postSets[target->currentImage], 0, nullptr); + vkCmdDraw(commandBuffer, 3, 1, 0, 0); +} + +void PostPass::createDescriptorSet(RenderTarget* target) { + VkDescriptorSetAllocateInfo allocInfo = {}; + allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + allocInfo.descriptorPool = renderer_.getDescriptorPool(); + allocInfo.descriptorSetCount = target->numImages; + + // FIXME: lol what + VkDescriptorSetLayout* layouts = new VkDescriptorSetLayout[target->numImages]; + for(uint32_t i = 0; i < target->numImages; i++) + layouts[i] = setLayout_; + + allocInfo.pSetLayouts = layouts; + + target->postSets = new VkDescriptorSet[target->numImages]; + vkAllocateDescriptorSets(renderer_.getDevice(), &allocInfo, target->postSets); + + delete[] layouts; + + for(uint32_t i = 0; i < target->numImages; i++) { + VkDescriptorImageInfo imageInfo = {}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + imageInfo.imageView = target->offscreenImageViews[i]; + imageInfo.sampler = offscreenSampler_; + + VkWriteDescriptorSet descriptorWrite = {}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.descriptorCount = 1; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite.dstSet = target->postSets[i]; + descriptorWrite.pImageInfo = &imageInfo; + + vkUpdateDescriptorSets(renderer_.getDevice(), 1, &descriptorWrite, 0, nullptr); + } +} + +void PostPass::createDescriptorSetLayout() { + VkDescriptorSetLayoutBinding offscreenSamplerBinding = {}; + offscreenSamplerBinding.descriptorCount = 1; + offscreenSamplerBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + offscreenSamplerBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + createInfo.bindingCount = 1; + createInfo.pBindings = &offscreenSamplerBinding; + + vkCreateDescriptorSetLayout(renderer_.getDevice(), &createInfo, nullptr, &setLayout_); +} + +void PostPass::createPipeline() { + VkShaderModule vertShaderModule = renderer_.createShader("post.vert.spv"); + VkShaderModule fragShaderModule = renderer_.createShader("post.frag.spv"); + + VkPipelineShaderStageCreateInfo vertShaderStageInfo = {}; + vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT; + vertShaderStageInfo.module = vertShaderModule; + vertShaderStageInfo.pName = "main"; + + VkPipelineShaderStageCreateInfo fragShaderStageInfo = {}; + fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + fragShaderStageInfo.module = fragShaderModule; + fragShaderStageInfo.pName = "main"; + + const std::array shaderStages = {vertShaderStageInfo, fragShaderStageInfo}; + + VkPipelineVertexInputStateCreateInfo vertexInputInfo = {}; + vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + + VkPipelineInputAssemblyStateCreateInfo inputAssembly = {}; + inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineViewportStateCreateInfo viewportState = {}; + viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewportState.viewportCount = 1; + viewportState.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_FRONT_BIT; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + + VkPipelineMultisampleStateCreateInfo multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState colorBlendAttachment = {}; + 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; + + VkPipelineColorBlendStateCreateInfo colorBlending = {}; + colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + colorBlending.attachmentCount = 1; + colorBlending.pAttachments = &colorBlendAttachment; + + const std::array dynamicStates = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR + }; + + VkPipelineDynamicStateCreateInfo dynamicState = {}; + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.dynamicStateCount = dynamicStates.size(); + dynamicState.pDynamicStates = dynamicStates.data(); + + VkPipelineLayoutCreateInfo pipelineLayoutInfo = {}; + pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + pipelineLayoutInfo.setLayoutCount = 1; + pipelineLayoutInfo.pSetLayouts = &setLayout_; + + vkCreatePipelineLayout(renderer_.getDevice(), &pipelineLayoutInfo, nullptr, &pipelineLayout_); + + VkGraphicsPipelineCreateInfo pipelineInfo = {}; + pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipelineInfo.stageCount = shaderStages.size(); + pipelineInfo.pStages = shaderStages.data(); + pipelineInfo.pVertexInputState = &vertexInputInfo; + pipelineInfo.pInputAssemblyState = &inputAssembly; + pipelineInfo.pViewportState = &viewportState; + pipelineInfo.pRasterizationState = &rasterizer; + pipelineInfo.pMultisampleState = &multisampling; + pipelineInfo.pColorBlendState = &colorBlending; + pipelineInfo.pDynamicState = &dynamicState; + pipelineInfo.layout = pipelineLayout_; + pipelineInfo.renderPass = renderer_.getRenderPass(); + + vkCreateGraphicsPipelines(renderer_.getDevice(), nullptr, 1, &pipelineInfo, nullptr, &pipeline_); + + vkDestroyShaderModule(renderer_.getDevice(), fragShaderModule, nullptr); + vkDestroyShaderModule(renderer_.getDevice(), vertShaderModule, nullptr); +} + +void PostPass::createSampler() { + VkSamplerCreateInfo samplerInfo = {}; + samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + + vkCreateSampler(renderer_.getDevice(), &samplerInfo, nullptr, &offscreenSampler_); +} diff --git a/src/renderer.cpp b/src/renderer.cpp index 039b962..96c0ed2 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -19,8 +19,10 @@ Renderer::Renderer() { createLogicalDevice(); createCommandPool(); createPresentationRenderPass(); + createDescriptorPool(); worldPass_ = new WorldPass(*this); + postPass_ = new PostPass(*this); } Renderer::~Renderer() { @@ -80,6 +82,8 @@ void Renderer::render(World& world, RenderTarget* target) { vkCmdBeginRenderPass(commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); + postPass_->render(commandBuffer, target); + vkCmdEndRenderPass(commandBuffer); vkEndCommandBuffer(commandBuffer); @@ -263,6 +267,8 @@ RenderTarget* Renderer::createSurfaceRenderTarget(VkSurfaceKHR surface, RenderTa } } + postPass_->createDescriptorSet(target); + VkCommandBufferAllocateInfo allocateInfo = {}; allocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; @@ -581,3 +587,17 @@ void Renderer::createPresentationRenderPass() { vkCreateRenderPass(device_, &renderPassInfo, nullptr, &presentationRenderPass_); } + +void Renderer::createDescriptorPool() { + VkDescriptorPoolSize poolSize = {}; + poolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSize.descriptorCount = 15; + + VkDescriptorPoolCreateInfo poolInfo = {}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = 15; + + vkCreateDescriptorPool(device_, &poolInfo, nullptr, &descriptorPool_); +} diff --git a/src/worldpass.cpp b/src/worldpass.cpp index 8ec4d01..23ced49 100644 --- a/src/worldpass.cpp +++ b/src/worldpass.cpp @@ -58,7 +58,7 @@ void WorldPass::createRenderPass() { colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkAttachmentReference colorAttachmentRef = {}; colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;