diff --git a/data/shaders/radialblur/colorpass.vert b/data/shaders/radialblur/colorpass.vert index 05c9cd4e..abd9082e 100644 --- a/data/shaders/radialblur/colorpass.vert +++ b/data/shaders/radialblur/colorpass.vert @@ -10,7 +10,6 @@ layout (binding = 0) uniform UBO { mat4 projection; mat4 model; - vec4 glowColor; } ubo; layout (location = 0) out vec3 outColor; @@ -22,8 +21,6 @@ out gl_PerVertex void main() { - { - outColor = inColor; - } + outColor = inColor; gl_Position = ubo.projection * ubo.model * inPos; } diff --git a/data/shaders/radialblur/colorpass.vert.spv b/data/shaders/radialblur/colorpass.vert.spv index 3778d638..2cce8d4a 100644 Binary files a/data/shaders/radialblur/colorpass.vert.spv and b/data/shaders/radialblur/colorpass.vert.spv differ diff --git a/data/shaders/radialblur/radialblur.frag b/data/shaders/radialblur/radialblur.frag index 57f4cf47..27ca176a 100644 --- a/data/shaders/radialblur/radialblur.frag +++ b/data/shaders/radialblur/radialblur.frag @@ -7,8 +7,6 @@ layout (binding = 1) uniform sampler2D samplerColor; layout (binding = 2) uniform UBO { - int texWidth; - int texHeight; float radialBlurScale; float radialBlurStrength; vec2 radialOrigin; @@ -20,7 +18,8 @@ layout (location = 0) out vec4 outFragColor; void main() { - vec2 radialSize = vec2(1.0 / ubo.texWidth, 1.0 / ubo.texHeight); + ivec2 texDim = textureSize(samplerColor, 0); + vec2 radialSize = vec2(1.0 / texDim.s, 1.0 / texDim.t); vec2 UV = inUV; diff --git a/data/shaders/radialblur/radialblur.frag.spv b/data/shaders/radialblur/radialblur.frag.spv index 27a0b95e..b348e565 100644 Binary files a/data/shaders/radialblur/radialblur.frag.spv and b/data/shaders/radialblur/radialblur.frag.spv differ diff --git a/offscreen/offscreen.cpp b/offscreen/offscreen.cpp index 1efaf3e6..943a1ce2 100644 --- a/offscreen/offscreen.cpp +++ b/offscreen/offscreen.cpp @@ -356,10 +356,12 @@ public: { offscreenPass.commandBuffer = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false); } - - // Create a semaphore used to synchronize offscreen rendering and usage - VkSemaphoreCreateInfo semaphoreCreateInfo = vkTools::initializers::semaphoreCreateInfo(); - VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &offscreenPass.semaphore)); + if (offscreenPass.semaphore == VK_NULL_HANDLE) + { + // Create a semaphore used to synchronize offscreen rendering and usage + VkSemaphoreCreateInfo semaphoreCreateInfo = vkTools::initializers::semaphoreCreateInfo(); + VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &offscreenPass.semaphore)); + } VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); @@ -805,7 +807,6 @@ public: VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.shaded)); // Offscreen // Flip culling - rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; pipelineCreateInfo.renderPass = offscreenPass.renderPass; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.shadedOffscreen)); diff --git a/radialblur/radialblur.cpp b/radialblur/radialblur.cpp index 4fb8ad83..47876746 100644 --- a/radialblur/radialblur.cpp +++ b/radialblur/radialblur.cpp @@ -23,13 +23,8 @@ #define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION false -// Texture properties -#define TEX_DIM 128 -#define TEX_FORMAT VK_FORMAT_R8G8B8A8_UNORM -#define TEX_FILTER VK_FILTER_LINEAR; - // Offscreen frame buffer properties -#define FB_DIM TEX_DIM +#define FB_DIM 512 #define FB_COLOR_FORMAT VK_FORMAT_R8G8B8A8_UNORM // Vertex layout for this example @@ -75,8 +70,6 @@ public: } uboQuadVS; struct { - int32_t texWidth = TEX_DIM; - int32_t texHeight = TEX_DIM; float radialBlurScale = 0.25f; float radialBlurStrength = 0.75f; glm::vec2 radialOrigin = glm::vec2(0.5f, 0.5f); @@ -109,21 +102,21 @@ public: VkDeviceMemory mem; VkImageView view; }; - struct FrameBuffer { + struct OffscreenPass { int32_t width, height; - VkFramebuffer frameBuffer; + VkFramebuffer frameBuffer; FrameBufferAttachment color, depth; - VkSampler colorSampler; - } offScreenFrameBuf; - - VkCommandBuffer offScreenCmdBuffer = VK_NULL_HANDLE; - - // Semaphore used to synchronize between offscreen and final scene rendering - VkSemaphore offscreenSemaphore = VK_NULL_HANDLE; + VkRenderPass renderPass; + VkSampler sampler; + VkDescriptorImageInfo descriptor; + VkCommandBuffer commandBuffer = VK_NULL_HANDLE; + // Semaphore used to synchronize between offscreen and final scene render pass + VkSemaphore semaphore = VK_NULL_HANDLE; + } offscreenPass; VulkanExample() : VulkanExampleBase(ENABLE_VALIDATION) { - zoom = -12.0f; + zoom = -10.0f; rotation = { -16.25f, -28.75f, 0.0f }; timerSpeed *= 0.5f; enableTextOverlay = true; @@ -136,19 +129,20 @@ public: // Note : Inherited destructor cleans up resources stored in base class // Frame buffer - vkDestroySampler(device, offScreenFrameBuf.colorSampler, nullptr); // Color attachment - vkDestroyImageView(device, offScreenFrameBuf.color.view, nullptr); - vkDestroyImage(device, offScreenFrameBuf.color.image, nullptr); - vkFreeMemory(device, offScreenFrameBuf.color.mem, nullptr); + vkDestroyImageView(device, offscreenPass.color.view, nullptr); + vkDestroyImage(device, offscreenPass.color.image, nullptr); + vkFreeMemory(device, offscreenPass.color.mem, nullptr); // Depth attachment - vkDestroyImageView(device, offScreenFrameBuf.depth.view, nullptr); - vkDestroyImage(device, offScreenFrameBuf.depth.image, nullptr); - vkFreeMemory(device, offScreenFrameBuf.depth.mem, nullptr); + vkDestroyImageView(device, offscreenPass.depth.view, nullptr); + vkDestroyImage(device, offscreenPass.depth.image, nullptr); + vkFreeMemory(device, offscreenPass.depth.mem, nullptr); - vkDestroyFramebuffer(device, offScreenFrameBuf.frameBuffer, nullptr); + vkDestroyRenderPass(device, offscreenPass.renderPass, nullptr); + vkDestroySampler(device, offscreenPass.sampler, nullptr); + vkDestroyFramebuffer(device, offscreenPass.frameBuffer, nullptr); vkDestroyPipeline(device, pipelines.radialBlur, nullptr); vkDestroyPipeline(device, pipelines.phongPass, nullptr); @@ -169,98 +163,16 @@ public: vkTools::destroyUniformData(device, &uniformData.vsQuad); vkTools::destroyUniformData(device, &uniformData.fsQuad); - vkFreeCommandBuffers(device, cmdPool, 1, &offScreenCmdBuffer); - vkDestroySemaphore(device, offscreenSemaphore, nullptr); - } - - // Preapre an empty texture as the blit target from - // the offscreen framebuffer - void prepareTextureTarget(vkTools::VulkanTexture *tex, uint32_t width, uint32_t height, VkFormat format) - { - VkCommandBuffer cmdBuffer = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - VkFormatProperties formatProperties; - - // Get device properites for the requested texture format - vkGetPhysicalDeviceFormatProperties(physicalDevice, format, &formatProperties); - // Check if blit destination is supported for the requested format - // Only try for optimal tiling, linear tiling usually won't support blit as destination anyway - assert(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT); - - // Prepare blit target texture - tex->width = width; - tex->height = height; - - VkImageCreateInfo imageCreateInfo = vkTools::initializers::imageCreateInfo(); - imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; - imageCreateInfo.format = format; - imageCreateInfo.extent = { width, height, 1 }; - imageCreateInfo.mipLevels = 1; - imageCreateInfo.arrayLayers = 1; - imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; - imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; - imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - // Texture will be sampled in a shader and is also the blit destination - imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; - - VkMemoryAllocateInfo memAllocInfo = vkTools::initializers::memoryAllocateInfo(); - VkMemoryRequirements memReqs; - - VK_CHECK_RESULT(vkCreateImage(device, &imageCreateInfo, nullptr, &tex->image)); - vkGetImageMemoryRequirements(device, tex->image, &memReqs); - memAllocInfo.allocationSize = memReqs.size; - memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &(tex->deviceMemory))); - VK_CHECK_RESULT(vkBindImageMemory(device, tex->image, tex->deviceMemory, 0)); - - // Transform image layout to transfer destination - tex->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - vkTools::setImageLayout( - cmdBuffer, - tex->image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - tex->imageLayout); - - // Create sampler - VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); - sampler.magFilter = TEX_FILTER; - sampler.minFilter = TEX_FILTER; - sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler.addressModeV = sampler.addressModeU; - sampler.addressModeW = sampler.addressModeU; - sampler.mipLodBias = 0.0f; - sampler.maxAnisotropy = 0; - sampler.compareOp = VK_COMPARE_OP_NEVER; - sampler.minLod = 0.0f; - sampler.maxLod = 0.0f; - sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &tex->sampler)); - - // Create image view - VkImageViewCreateInfo view = vkTools::initializers::imageViewCreateInfo(); - view.image = VK_NULL_HANDLE; - view.viewType = VK_IMAGE_VIEW_TYPE_2D; - view.format = format; - view.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; - view.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; - view.image = tex->image; - VK_CHECK_RESULT(vkCreateImageView(device, &view, nullptr, &tex->view)); - - VulkanExampleBase::flushCommandBuffer(cmdBuffer, queue, true); + vkFreeCommandBuffers(device, cmdPool, 1, &offscreenPass.commandBuffer); + vkDestroySemaphore(device, offscreenPass.semaphore, nullptr); } // Setup the offscreen framebuffer for rendering the blurred scene - // The color attachment of this framebuffer will then be used - // to sample frame in the fragment shader of the final pass - void prepareOffscreenFramebuffer() + // The color attachment of this framebuffer will then be used to sample frame in the fragment shader of the final pass + void prepareOffscreen() { - offScreenFrameBuf.width = FB_DIM; - offScreenFrameBuf.height = FB_DIM; - - VkFormat fbColorFormat = FB_COLOR_FORMAT; + offscreenPass.width = FB_DIM; + offscreenPass.height = FB_DIM; // Find a suitable depth format VkFormat fbDepthFormat; @@ -270,9 +182,9 @@ public: // Color attachment VkImageCreateInfo image = vkTools::initializers::imageCreateInfo(); image.imageType = VK_IMAGE_TYPE_2D; - image.format = fbColorFormat; - image.extent.width = offScreenFrameBuf.width; - image.extent.height = offScreenFrameBuf.height; + image.format = FB_COLOR_FORMAT; + image.extent.width = offscreenPass.width; + image.extent.height = offscreenPass.height; image.extent.depth = 1; image.mipLevels = 1; image.arrayLayers = 1; @@ -284,58 +196,51 @@ public: VkMemoryAllocateInfo memAlloc = vkTools::initializers::memoryAllocateInfo(); VkMemoryRequirements memReqs; + VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offscreenPass.color.image)); + vkGetImageMemoryRequirements(device, offscreenPass.color.image, &memReqs); + memAlloc.allocationSize = memReqs.size; + memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offscreenPass.color.mem)); + VK_CHECK_RESULT(vkBindImageMemory(device, offscreenPass.color.image, offscreenPass.color.mem, 0)); + VkImageViewCreateInfo colorImageView = vkTools::initializers::imageViewCreateInfo(); colorImageView.viewType = VK_IMAGE_VIEW_TYPE_2D; - colorImageView.format = fbColorFormat; + colorImageView.format = FB_COLOR_FORMAT; colorImageView.subresourceRange = {}; colorImageView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; colorImageView.subresourceRange.baseMipLevel = 0; colorImageView.subresourceRange.levelCount = 1; colorImageView.subresourceRange.baseArrayLayer = 0; colorImageView.subresourceRange.layerCount = 1; + colorImageView.image = offscreenPass.color.image; + VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &offscreenPass.color.view)); - VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offScreenFrameBuf.color.image)); - vkGetImageMemoryRequirements(device, offScreenFrameBuf.color.image, &memReqs); - memAlloc.allocationSize = memReqs.size; - memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offScreenFrameBuf.color.mem)); - VK_CHECK_RESULT(vkBindImageMemory(device, offScreenFrameBuf.color.image, offScreenFrameBuf.color.mem, 0)); - - // Get a primary command buffer for submitting image layout transitions for the framebuffer attachments - VkCommandBuffer layoutCmd = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); - - // Set the initial layout to shader read instead of attachment - // This is done as the render loop does the actualy image layout transitions - vkTools::setImageLayout( - layoutCmd, - offScreenFrameBuf.color.image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - colorImageView.image = offScreenFrameBuf.color.image; - VK_CHECK_RESULT(vkCreateImageView(device, &colorImageView, nullptr, &offScreenFrameBuf.color.view)); - - // Create sampler to sample from to collor attachment - // Used to sample in the fragment shader for final rendering - VkSamplerCreateInfo sampler = vkTools::initializers::samplerCreateInfo(); - sampler.magFilter = VK_FILTER_LINEAR; - sampler.minFilter = VK_FILTER_LINEAR; - sampler.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; - sampler.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; - sampler.addressModeV = sampler.addressModeU; - sampler.addressModeW = sampler.addressModeU; - sampler.mipLodBias = 0.0f; - sampler.maxAnisotropy = 0; - sampler.minLod = 0.0f; - sampler.maxLod = 1.0f; - sampler.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; - VK_CHECK_RESULT(vkCreateSampler(device, &sampler, nullptr, &offScreenFrameBuf.colorSampler)); + // Create sampler to sample from the attachment in the fragment shader + VkSamplerCreateInfo samplerInfo = vkTools::initializers::samplerCreateInfo(); + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; + samplerInfo.addressModeV = samplerInfo.addressModeU; + samplerInfo.addressModeW = samplerInfo.addressModeU; + samplerInfo.mipLodBias = 0.0f; + samplerInfo.maxAnisotropy = 0; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = 1.0f; + samplerInfo.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; + VK_CHECK_RESULT(vkCreateSampler(device, &samplerInfo, nullptr, &offscreenPass.sampler)); // Depth stencil attachment image.format = fbDepthFormat; image.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; + VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offscreenPass.depth.image)); + vkGetImageMemoryRequirements(device, offscreenPass.depth.image, &memReqs); + memAlloc.allocationSize = memReqs.size; + memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offscreenPass.depth.mem)); + VK_CHECK_RESULT(vkBindImageMemory(device, offscreenPass.depth.image, offscreenPass.depth.mem, 0)); + VkImageViewCreateInfo depthStencilView = vkTools::initializers::imageViewCreateInfo(); depthStencilView.viewType = VK_IMAGE_VIEW_TYPE_2D; depthStencilView.format = fbDepthFormat; @@ -346,52 +251,103 @@ public: depthStencilView.subresourceRange.levelCount = 1; depthStencilView.subresourceRange.baseArrayLayer = 0; depthStencilView.subresourceRange.layerCount = 1; + depthStencilView.image = offscreenPass.depth.image; + VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &offscreenPass.depth.view)); - VK_CHECK_RESULT(vkCreateImage(device, &image, nullptr, &offScreenFrameBuf.depth.image)); - vkGetImageMemoryRequirements(device, offScreenFrameBuf.depth.image, &memReqs); - memAlloc.allocationSize = memReqs.size; - memAlloc.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VK_CHECK_RESULT(vkAllocateMemory(device, &memAlloc, nullptr, &offScreenFrameBuf.depth.mem)); - VK_CHECK_RESULT(vkBindImageMemory(device, offScreenFrameBuf.depth.image, offScreenFrameBuf.depth.mem, 0)); + // Create a separate render pass for the offscreen rendering as it may differ from the one used for scene rendering - vkTools::setImageLayout( - layoutCmd, - offScreenFrameBuf.depth.image, - VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + std::array attchmentDescriptions = {}; + // Color attachment + attchmentDescriptions[0].format = FB_COLOR_FORMAT; + attchmentDescriptions[0].samples = VK_SAMPLE_COUNT_1_BIT; + attchmentDescriptions[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attchmentDescriptions[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attchmentDescriptions[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attchmentDescriptions[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attchmentDescriptions[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attchmentDescriptions[0].finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + // Depth attachment + attchmentDescriptions[1].format = fbDepthFormat; + attchmentDescriptions[1].samples = VK_SAMPLE_COUNT_1_BIT; + attchmentDescriptions[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + attchmentDescriptions[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attchmentDescriptions[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attchmentDescriptions[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attchmentDescriptions[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attchmentDescriptions[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - // Submit the command buffer to apply the image memory barrier - VulkanExampleBase::flushCommandBuffer(layoutCmd, queue, true); + VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL }; + VkAttachmentReference depthReference = { 1, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL }; - depthStencilView.image = offScreenFrameBuf.depth.image; - VK_CHECK_RESULT(vkCreateImageView(device, &depthStencilView, nullptr, &offScreenFrameBuf.depth.view)); + VkSubpassDescription subpassDescription = {}; + subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpassDescription.colorAttachmentCount = 1; + subpassDescription.pColorAttachments = &colorReference; + subpassDescription.pDepthStencilAttachment = &depthReference; + + // Use subpass dependencies for layout transitions + std::array dependencies; + + dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; + dependencies[0].dstSubpass = 0; + dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + dependencies[1].srcSubpass = 0; + dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; + dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; + dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; + + // Create the actual renderpass + VkRenderPassCreateInfo renderPassInfo = {}; + renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + renderPassInfo.attachmentCount = static_cast(attchmentDescriptions.size()); + renderPassInfo.pAttachments = attchmentDescriptions.data(); + renderPassInfo.subpassCount = 1; + renderPassInfo.pSubpasses = &subpassDescription; + renderPassInfo.dependencyCount = static_cast(dependencies.size()); + renderPassInfo.pDependencies = dependencies.data(); + + VK_CHECK_RESULT(vkCreateRenderPass(device, &renderPassInfo, nullptr, &offscreenPass.renderPass)); VkImageView attachments[2]; - attachments[0] = offScreenFrameBuf.color.view; - attachments[1] = offScreenFrameBuf.depth.view; + attachments[0] = offscreenPass.color.view; + attachments[1] = offscreenPass.depth.view; VkFramebufferCreateInfo fbufCreateInfo = vkTools::initializers::framebufferCreateInfo(); - fbufCreateInfo.renderPass = renderPass; + fbufCreateInfo.renderPass = offscreenPass.renderPass; fbufCreateInfo.attachmentCount = 2; fbufCreateInfo.pAttachments = attachments; - fbufCreateInfo.width = offScreenFrameBuf.width; - fbufCreateInfo.height = offScreenFrameBuf.height; + fbufCreateInfo.width = offscreenPass.width; + fbufCreateInfo.height = offscreenPass.height; fbufCreateInfo.layers = 1; - VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offScreenFrameBuf.frameBuffer)); + + VK_CHECK_RESULT(vkCreateFramebuffer(device, &fbufCreateInfo, nullptr, &offscreenPass.frameBuffer)); + + // Fill a descriptor for later use in a descriptor set + offscreenPass.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + offscreenPass.descriptor.imageView = offscreenPass.color.view; + offscreenPass.descriptor.sampler = offscreenPass.sampler; } // Sets up the command buffer that renders the scene to the offscreen frame buffer void buildOffscreenCommandBuffer() { - if (offScreenCmdBuffer == VK_NULL_HANDLE) + if (offscreenPass.commandBuffer == VK_NULL_HANDLE) { - offScreenCmdBuffer = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false); + offscreenPass.commandBuffer = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false); + } + if (offscreenPass.semaphore == VK_NULL_HANDLE) + { + VkSemaphoreCreateInfo semaphoreCreateInfo = vkTools::initializers::semaphoreCreateInfo(); + VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &offscreenPass.semaphore)); } - - // Create a semaphore used to synchronize offscreen rendering and usage - VkSemaphoreCreateInfo semaphoreCreateInfo = vkTools::initializers::semaphoreCreateInfo(); - VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &offscreenSemaphore)); VkCommandBufferBeginInfo cmdBufInfo = vkTools::initializers::commandBufferBeginInfo(); @@ -400,50 +356,34 @@ public: clearValues[1].depthStencil = { 1.0f, 0 }; VkRenderPassBeginInfo renderPassBeginInfo = vkTools::initializers::renderPassBeginInfo(); - renderPassBeginInfo.renderPass = renderPass; - renderPassBeginInfo.framebuffer = offScreenFrameBuf.frameBuffer; - renderPassBeginInfo.renderArea.extent.width = offScreenFrameBuf.width; - renderPassBeginInfo.renderArea.extent.height = offScreenFrameBuf.height; + renderPassBeginInfo.renderPass = offscreenPass.renderPass; + renderPassBeginInfo.framebuffer = offscreenPass.frameBuffer; + renderPassBeginInfo.renderArea.extent.width = offscreenPass.width; + renderPassBeginInfo.renderArea.extent.height = offscreenPass.height; renderPassBeginInfo.clearValueCount = 2; renderPassBeginInfo.pClearValues = clearValues; - VK_CHECK_RESULT(vkBeginCommandBuffer(offScreenCmdBuffer, &cmdBufInfo)); + VK_CHECK_RESULT(vkBeginCommandBuffer(offscreenPass.commandBuffer, &cmdBufInfo)); - // Change back layout of the color attachment after sampling in the fragment shader - vkTools::setImageLayout( - offScreenCmdBuffer, - offScreenFrameBuf.color.image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + VkViewport viewport = vkTools::initializers::viewport((float)offscreenPass.width, (float)offscreenPass.height, 0.0f, 1.0f); + vkCmdSetViewport(offscreenPass.commandBuffer, 0, 1, &viewport); - VkViewport viewport = vkTools::initializers::viewport((float)offScreenFrameBuf.width, (float)offScreenFrameBuf.height, 0.0f, 1.0f); - vkCmdSetViewport(offScreenCmdBuffer, 0, 1, &viewport); + VkRect2D scissor = vkTools::initializers::rect2D(offscreenPass.width, offscreenPass.height, 0, 0); + vkCmdSetScissor(offscreenPass.commandBuffer, 0, 1, &scissor); - VkRect2D scissor = vkTools::initializers::rect2D(offScreenFrameBuf.width, offScreenFrameBuf.height, 0, 0); - vkCmdSetScissor(offScreenCmdBuffer, 0, 1, &scissor); + vkCmdBeginRenderPass(offscreenPass.commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - vkCmdBeginRenderPass(offScreenCmdBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); - - vkCmdBindDescriptorSets(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL); - vkCmdBindPipeline(offScreenCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.colorPass); + vkCmdBindDescriptorSets(offscreenPass.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL); + vkCmdBindPipeline(offscreenPass.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.colorPass); VkDeviceSize offsets[1] = { 0 }; - vkCmdBindVertexBuffers(offScreenCmdBuffer, VERTEX_BUFFER_BIND_ID, 1, &meshes.example.vertices.buf, offsets); - vkCmdBindIndexBuffer(offScreenCmdBuffer, meshes.example.indices.buf, 0, VK_INDEX_TYPE_UINT32); - vkCmdDrawIndexed(offScreenCmdBuffer, meshes.example.indexCount, 1, 0, 0, 0); + vkCmdBindVertexBuffers(offscreenPass.commandBuffer, VERTEX_BUFFER_BIND_ID, 1, &meshes.example.vertices.buf, offsets); + vkCmdBindIndexBuffer(offscreenPass.commandBuffer, meshes.example.indices.buf, 0, VK_INDEX_TYPE_UINT32); + vkCmdDrawIndexed(offscreenPass.commandBuffer, meshes.example.indexCount, 1, 0, 0, 0); - vkCmdEndRenderPass(offScreenCmdBuffer); + vkCmdEndRenderPass(offscreenPass.commandBuffer); - // Change layout of the color attachment for sampling in the fragment shader - vkTools::setImageLayout( - offScreenCmdBuffer, - offScreenFrameBuf.color.image, - VK_IMAGE_ASPECT_COLOR_BIT, - VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, - VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); - - VK_CHECK_RESULT(vkEndCommandBuffer(offScreenCmdBuffer)); + VK_CHECK_RESULT(vkEndCommandBuffer(offscreenPass.commandBuffer)); } void reBuildCommandBuffers() @@ -514,7 +454,7 @@ public: } } - void loadMeshes() + void loadAssets() { loadMesh(getAssetPath() + "models/glowsphere.dae", &meshes.example, vertexLayout, 0.05f); } @@ -675,13 +615,6 @@ public: VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &allocInfo, &descriptorSets.quad)); - // Image descriptor for the offscreen color attachment image - VkDescriptorImageInfo texDescriptor = - vkTools::initializers::descriptorImageInfo( - offScreenFrameBuf.colorSampler, - offScreenFrameBuf.color.view, - VK_IMAGE_LAYOUT_GENERAL); - std::vector writeDescriptorSets = { // Binding 0 : Vertex shader uniform buffer @@ -695,7 +628,7 @@ public: descriptorSets.quad, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, - &texDescriptor), + &offscreenPass.descriptor), // Binding 2 : Fragment shader uniform buffer vkTools::initializers::writeDescriptorSet( descriptorSets.quad, @@ -803,7 +736,6 @@ public: blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD; blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_DST_ALPHA; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.radialBlur)); // No blending (for debug display) @@ -813,17 +745,16 @@ public: // Phong pass shaderStages[0] = loadShader(getAssetPath() + "shaders/radialblur/phongpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[1] = loadShader(getAssetPath() + "shaders/radialblur/phongpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - pipelineCreateInfo.layout = pipelineLayouts.scene; blendAttachmentState.blendEnable = VK_FALSE; depthStencilState.depthWriteEnable = VK_TRUE; - VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.phongPass)); // Color only pass (offscreen blur base) shaderStages[0] = loadShader(getAssetPath() + "shaders/radialblur/colorpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[1] = loadShader(getAssetPath() + "shaders/radialblur/colorpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); - + pipelineCreateInfo.renderPass = offscreenPass.renderPass; + pipelineCreateInfo.layout = pipelineLayouts.radialBlur; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.colorPass)); } @@ -905,33 +836,22 @@ public: { VulkanExampleBase::prepareFrame(); - // The scene render command buffer has to wait for the offscreen - // rendering to be finished before we can use the framebuffer - // color image for sampling during final rendering - // To ensure this we use a dedicated offscreen synchronization - // semaphore that will be signaled when offscreen renderin - // has been finished - // This is necessary as an implementation may start both - // command buffers at the same time, there is no guarantee - // that command buffers will be executed in the order they - // have been submitted by the application - // Offscreen rendering // Wait for swap chain presentation to finish submitInfo.pWaitSemaphores = &semaphores.presentComplete; // Signal ready with offscreen semaphore - submitInfo.pSignalSemaphores = &offscreenSemaphore; + submitInfo.pSignalSemaphores = &offscreenPass.semaphore; // Submit work submitInfo.commandBufferCount = 1; - submitInfo.pCommandBuffers = &offScreenCmdBuffer; + submitInfo.pCommandBuffers = &offscreenPass.commandBuffer; VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); // Scene rendering // Wait for offscreen semaphore - submitInfo.pWaitSemaphores = &offscreenSemaphore; + submitInfo.pWaitSemaphores = &offscreenPass.semaphore; // Signal ready with render complete semaphpre submitInfo.pSignalSemaphores = &semaphores.renderComplete; @@ -945,9 +865,9 @@ public: void prepare() { VulkanExampleBase::prepare(); + loadAssets(); generateQuad(); - loadMeshes(); - prepareOffscreenFramebuffer(); + prepareOffscreen(); setupVertexDescriptions(); prepareUniformBuffers(); setupDescriptorSetLayout(); @@ -976,6 +896,19 @@ public: updateUniformBuffersScreen(); } + void toggleBlur() + { + blur = !blur; + updateUniformBuffersScene(); + reBuildCommandBuffers(); + } + + void toggleTextureDisplay() + { + displayTexture = !displayTexture; + reBuildCommandBuffers(); + } + virtual void keyPressed(uint32_t keyCode) { switch (keyCode) @@ -1001,79 +934,6 @@ public: textOverlay->addText("Press \"T\" to display offscreen texture", 5.0f, 105.0f, VulkanTextOverlay::alignLeft); #endif } - - void toggleBlur() - { - blur = !blur; - updateUniformBuffersScene(); - reBuildCommandBuffers(); - } - - void toggleTextureDisplay() - { - displayTexture = !displayTexture; - reBuildCommandBuffers(); - } - }; -VulkanExample *vulkanExample; - -#if defined(_WIN32) -LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -{ - if (vulkanExample != NULL) - { - vulkanExample->handleMessages(hWnd, uMsg, wParam, lParam); - } - return (DefWindowProc(hWnd, uMsg, wParam, lParam)); -} -#elif defined(__linux__) && !defined(__ANDROID__) -static void handleEvent(const xcb_generic_event_t *event) -{ - if (vulkanExample != NULL) - { - vulkanExample->handleEvent(event); - } -} -#endif - -// Main entry point -#if defined(_WIN32) -// Windows entry point -int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR pCmdLine, int nCmdShow) -#elif defined(__ANDROID__) -// Android entry point -void android_main(android_app* state) -#elif defined(__linux__) -// Linux entry point -int main(const int argc, const char *argv[]) -#endif -{ -#if defined(__ANDROID__) - // Removing this may cause the compiler to omit the main entry point - // which would make the application crash at start - app_dummy(); -#endif - vulkanExample = new VulkanExample(); -#if defined(_WIN32) - vulkanExample->setupWindow(hInstance, WndProc); -#elif defined(__ANDROID__) - // Attach vulkan example to global android application state - state->userData = vulkanExample; - state->onAppCmd = VulkanExample::handleAppCommand; - state->onInputEvent = VulkanExample::handleAppInput; - vulkanExample->androidApp = state; -#elif defined(__linux__) - vulkanExample->setupWindow(); -#endif -#if !defined(__ANDROID__) - vulkanExample->initSwapchain(); - vulkanExample->prepare(); -#endif - vulkanExample->renderLoop(); - delete(vulkanExample); -#if !defined(__ANDROID__) - return 0; -#endif -} +VULKAN_EXAMPLE_MAIN()