Replace dedicated offscreen command buffer and complicated synchronization with single command buffer and sync via sub pass dependencies

Proper stage and access masks
Code cleanup
This commit is contained in:
Sascha Willems 2019-04-02 20:05:21 +02:00
parent cd3ef3e713
commit 71e4d68548

View file

@ -1,7 +1,7 @@
/* /*
* Vulkan Example - Implements a separable two-pass fullscreen blur (also known as bloom) * Vulkan Example - Implements a separable two-pass fullscreen blur (also known as bloom)
* *
* Copyright (C) 2016 by Sascha Willems - www.saschawillems.de * Copyright (C) Sascha Willems - www.saschawillems.de
* *
* This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT) * This code is licensed under the MIT license (MIT) (http://opensource.org/licenses/MIT)
*/ */
@ -23,8 +23,7 @@
#include "VulkanModel.hpp" #include "VulkanModel.hpp"
#include "VulkanBuffer.hpp" #include "VulkanBuffer.hpp"
#define VERTEX_BUFFER_BIND_ID 0 #define ENABLE_VALIDATION true
#define ENABLE_VALIDATION false
// Offscreen frame buffer properties // Offscreen frame buffer properties
#define FB_DIM 256 #define FB_DIM 256
@ -53,12 +52,6 @@ public:
vks::Model skyBox; vks::Model skyBox;
} models; } models;
struct {
VkPipelineVertexInputStateCreateInfo inputState;
std::vector<VkVertexInputBindingDescription> bindingDescriptions;
std::vector<VkVertexInputAttributeDescription> attributeDescriptions;
} vertices;
struct { struct {
vks::Buffer scene; vks::Buffer scene;
vks::Buffer skyBox; vks::Buffer skyBox;
@ -121,9 +114,6 @@ public:
int32_t width, height; int32_t width, height;
VkRenderPass renderPass; VkRenderPass renderPass;
VkSampler sampler; VkSampler sampler;
VkCommandBuffer commandBuffer = VK_NULL_HANDLE;
// Semaphore used to synchronize between offscreen and final scene rendering
VkSemaphore semaphore = VK_NULL_HANDLE;
std::array<FrameBuffer, 2> framebuffers; std::array<FrameBuffer, 2> framebuffers;
} offscreenPass; } offscreenPass;
@ -159,8 +149,6 @@ public:
vkDestroyFramebuffer(device, framebuffer.framebuffer, nullptr); vkDestroyFramebuffer(device, framebuffer.framebuffer, nullptr);
} }
vkDestroyRenderPass(device, offscreenPass.renderPass, nullptr); vkDestroyRenderPass(device, offscreenPass.renderPass, nullptr);
vkFreeCommandBuffers(device, cmdPool, 1, &offscreenPass.commandBuffer);
vkDestroySemaphore(device, offscreenPass.semaphore, nullptr);
vkDestroyPipeline(device, pipelines.blurHorz, nullptr); vkDestroyPipeline(device, pipelines.blurHorz, nullptr);
vkDestroyPipeline(device, pipelines.blurVert, nullptr); vkDestroyPipeline(device, pipelines.blurVert, nullptr);
@ -321,18 +309,18 @@ public:
dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL; dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
dependencies[0].dstSubpass = 0; dependencies[0].dstSubpass = 0;
dependencies[0].srcStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[0].srcStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[0].dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[0].srcAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[0].srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[0].dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependencies[0].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
dependencies[1].srcSubpass = 0; dependencies[1].srcSubpass = 0;
dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL; dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependencies[1].srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependencies[1].dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; dependencies[1].dstStageMask = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; dependencies[1].srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
dependencies[1].dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; dependencies[1].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT; dependencies[1].dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
// Create the actual renderpass // Create the actual renderpass
@ -367,159 +355,139 @@ public:
prepareOffscreenFramebuffer(&offscreenPass.framebuffers[1], FB_COLOR_FORMAT, fbDepthFormat); prepareOffscreenFramebuffer(&offscreenPass.framebuffers[1], FB_COLOR_FORMAT, fbDepthFormat);
} }
// Sets up the command buffer that renders the scene to the offscreen frame buffer
// The blur method used in this example is multi pass and renders the vertical
// blur first and then the horizontal one.
// While it's possible to blur in one pass, this method is widely used as it
// requires far less samples to generate the blur
void buildOffscreenCommandBuffer()
{
if (offscreenPass.commandBuffer == VK_NULL_HANDLE)
{
offscreenPass.commandBuffer = VulkanExampleBase::createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false);
}
if (offscreenPass.semaphore == VK_NULL_HANDLE)
{
VkSemaphoreCreateInfo semaphoreCreateInfo = vks::initializers::semaphoreCreateInfo();
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &offscreenPass.semaphore));
}
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
// First pass: Render glow parts of the model (separate mesh)
// -------------------------------------------------------------------------------------------------------
VkClearValue clearValues[2];
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
clearValues[1].depthStencil = { 1.0f, 0 };
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
renderPassBeginInfo.renderPass = offscreenPass.renderPass;
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[0].framebuffer;
renderPassBeginInfo.renderArea.extent.width = offscreenPass.width;
renderPassBeginInfo.renderArea.extent.height = offscreenPass.height;
renderPassBeginInfo.clearValueCount = 2;
renderPassBeginInfo.pClearValues = clearValues;
VK_CHECK_RESULT(vkBeginCommandBuffer(offscreenPass.commandBuffer, &cmdBufInfo));
VkViewport viewport = vks::initializers::viewport((float)offscreenPass.width, (float)offscreenPass.height, 0.0f, 1.0f);
vkCmdSetViewport(offscreenPass.commandBuffer, 0, 1, &viewport);
VkRect2D scissor = vks::initializers::rect2D(offscreenPass.width, offscreenPass.height, 0, 0);
vkCmdSetScissor(offscreenPass.commandBuffer, 0, 1, &scissor);
vkCmdBeginRenderPass(offscreenPass.commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
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.glowPass);
VkDeviceSize offsets[1] = { 0 };
vkCmdBindVertexBuffers(offscreenPass.commandBuffer, VERTEX_BUFFER_BIND_ID, 1, &models.ufoGlow.vertices.buffer, offsets);
vkCmdBindIndexBuffer(offscreenPass.commandBuffer, models.ufoGlow.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(offscreenPass.commandBuffer, models.ufoGlow.indexCount, 1, 0, 0, 0);
vkCmdEndRenderPass(offscreenPass.commandBuffer);
// Second pass: Render contents of the first pass into second framebuffer and apply a vertical blur
// This is the first blur pass, the horizontal blur is applied when rendering on top of the scene
// -------------------------------------------------------------------------------------------------------
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[1].framebuffer;
vkCmdBeginRenderPass(offscreenPass.commandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindDescriptorSets(offscreenPass.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurVert, 0, NULL);
vkCmdBindPipeline(offscreenPass.commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurVert);
vkCmdDraw(offscreenPass.commandBuffer, 3, 1, 0, 0);
vkCmdEndRenderPass(offscreenPass.commandBuffer);
VK_CHECK_RESULT(vkEndCommandBuffer(offscreenPass.commandBuffer));
}
void reBuildCommandBuffers()
{
if (!checkCommandBuffers())
{
destroyCommandBuffers();
createCommandBuffers();
}
buildCommandBuffers();
}
void buildCommandBuffers() void buildCommandBuffers()
{ {
VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo(); VkCommandBufferBeginInfo cmdBufInfo = vks::initializers::commandBufferBeginInfo();
VkClearValue clearValues[2]; VkClearValue clearValues[2];
clearValues[0].color = defaultClearColor; VkViewport viewport;
clearValues[1].depthStencil = { 1.0f, 0 }; VkRect2D scissor;
VkDeviceSize offsets[1] = { 0 };
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo(); /*
renderPassBeginInfo.renderPass = renderPass; The blur method used in this example is multi pass and renders the vertical blur first and then the horizontal one
renderPassBeginInfo.renderArea.offset.x = 0; While it's possible to blur in one pass, this method is widely used as it requires far less samples to generate the blur
renderPassBeginInfo.renderArea.offset.y = 0; */
renderPassBeginInfo.renderArea.extent.width = width;
renderPassBeginInfo.renderArea.extent.height = height;
renderPassBeginInfo.clearValueCount = 2;
renderPassBeginInfo.pClearValues = clearValues;
for (int32_t i = 0; i < drawCmdBuffers.size(); ++i) for (int32_t i = 0; i < drawCmdBuffers.size(); ++i)
{ {
// Set target frame buffer
renderPassBeginInfo.framebuffer = frameBuffers[i];
VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo)); VK_CHECK_RESULT(vkBeginCommandBuffer(drawCmdBuffers[i], &cmdBufInfo));
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); if (bloom) {
clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
clearValues[1].depthStencil = { 1.0f, 0 };
VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f); VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport); renderPassBeginInfo.renderPass = offscreenPass.renderPass;
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[0].framebuffer;
renderPassBeginInfo.renderArea.extent.width = offscreenPass.width;
renderPassBeginInfo.renderArea.extent.height = offscreenPass.height;
renderPassBeginInfo.clearValueCount = 2;
renderPassBeginInfo.pClearValues = clearValues;
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0); viewport = vks::initializers::viewport((float)offscreenPass.width, (float)offscreenPass.height, 0.0f, 1.0f);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor); vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
VkDeviceSize offsets[1] = { 0 }; scissor = vks::initializers::rect2D(offscreenPass.width, offscreenPass.height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
// Skybox /*
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.skyBox, 0, NULL); First render pass: Render glow parts of the model (separate mesh) to an offscreen frame buffer
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skyBox); */
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.skyBox.vertices.buffer, offsets); vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.skyBox.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.skyBox.indexCount, 1, 0, 0, 0);
// 3D scene vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL); vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.glowPass);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.phongPass);
vkCmdBindVertexBuffers(drawCmdBuffers[i], VERTEX_BUFFER_BIND_ID, 1, &models.ufo.vertices.buffer, offsets); VkDeviceSize offsets[1] = { 0 };
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.ufo.indices.buffer, 0, VK_INDEX_TYPE_UINT32); vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.ufoGlow.vertices.buffer, offsets);
vkCmdDrawIndexed(drawCmdBuffers[i], models.ufo.indexCount, 1, 0, 0, 0); vkCmdBindIndexBuffer(drawCmdBuffers[i], models.ufoGlow.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.ufoGlow.indexCount, 1, 0, 0, 0);
// Render vertical blurred scene applying a horizontal blur vkCmdEndRenderPass(drawCmdBuffers[i]);
// Render the (vertically blurred) contents of the second framebuffer and apply a horizontal blur
// ------------------------------------------------------------------------------------------------------- /*
if (bloom) Second render pass: Vertical blur
{
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurHorz, 0, NULL); Render contents of the first pass into a second framebuffer and apply a vertical blur
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurHorz); This is the first blur pass, the horizontal blur is applied when rendering on top of the scene
*/
renderPassBeginInfo.framebuffer = offscreenPass.framebuffers[1].framebuffer;
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurVert, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurVert);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0); vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
vkCmdEndRenderPass(drawCmdBuffers[i]);
} }
drawUI(drawCmdBuffers[i]); /*
Note: Explicit synchronization is not required between the render pass, as this is done implicit via sub pass dependencies
*/
vkCmdEndRenderPass(drawCmdBuffers[i]); /*
Third render pass: Scene rendering with applied vertical blur
Renders the scene and the (vertically blurred) contents of the second framebuffer and apply a horizontal blur
*/
{
clearValues[0].color = defaultClearColor;
clearValues[1].depthStencil = { 1.0f, 0 };
VkRenderPassBeginInfo renderPassBeginInfo = vks::initializers::renderPassBeginInfo();
renderPassBeginInfo.renderPass = renderPass;
renderPassBeginInfo.framebuffer = frameBuffers[i];
renderPassBeginInfo.renderArea.extent.width = width;
renderPassBeginInfo.renderArea.extent.height = height;
renderPassBeginInfo.clearValueCount = 2;
renderPassBeginInfo.pClearValues = clearValues;
vkCmdBeginRenderPass(drawCmdBuffers[i], &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
VkViewport viewport = vks::initializers::viewport((float)width, (float)height, 0.0f, 1.0f);
vkCmdSetViewport(drawCmdBuffers[i], 0, 1, &viewport);
VkRect2D scissor = vks::initializers::rect2D(width, height, 0, 0);
vkCmdSetScissor(drawCmdBuffers[i], 0, 1, &scissor);
VkDeviceSize offsets[1] = { 0 };
// Skybox
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.skyBox, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skyBox);
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.skyBox.vertices.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.skyBox.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.skyBox.indexCount, 1, 0, 0, 0);
// 3D scene
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.scene, 0, 1, &descriptorSets.scene, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.phongPass);
vkCmdBindVertexBuffers(drawCmdBuffers[i], 0, 1, &models.ufo.vertices.buffer, offsets);
vkCmdBindIndexBuffer(drawCmdBuffers[i], models.ufo.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
vkCmdDrawIndexed(drawCmdBuffers[i], models.ufo.indexCount, 1, 0, 0, 0);
if (bloom)
{
vkCmdBindDescriptorSets(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayouts.blur, 0, 1, &descriptorSets.blurHorz, 0, NULL);
vkCmdBindPipeline(drawCmdBuffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.blurHorz);
vkCmdDraw(drawCmdBuffers[i], 3, 1, 0, 0);
}
drawUI(drawCmdBuffers[i]);
vkCmdEndRenderPass(drawCmdBuffers[i]);
}
VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i])); VK_CHECK_RESULT(vkEndCommandBuffer(drawCmdBuffers[i]));
} }
if (bloom)
{
buildOffscreenCommandBuffer();
}
} }
void loadAssets() void loadAssets()
@ -530,55 +498,6 @@ public:
textures.cubemap.loadFromFile(getAssetPath() + "textures/cubemap_space.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); textures.cubemap.loadFromFile(getAssetPath() + "textures/cubemap_space.ktx", VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue);
} }
void setupVertexDescriptions()
{
// Binding description
// Same for all meshes used in this example
vertices.bindingDescriptions.resize(1);
vertices.bindingDescriptions[0] =
vks::initializers::vertexInputBindingDescription(
VERTEX_BUFFER_BIND_ID,
vertexLayout.stride(),
VK_VERTEX_INPUT_RATE_VERTEX);
// Attribute descriptions
vertices.attributeDescriptions.resize(4);
// Location 0 : Position
vertices.attributeDescriptions[0] =
vks::initializers::vertexInputAttributeDescription(
VERTEX_BUFFER_BIND_ID,
0,
VK_FORMAT_R32G32B32_SFLOAT,
0);
// Location 1 : Texture coordinates
vertices.attributeDescriptions[1] =
vks::initializers::vertexInputAttributeDescription(
VERTEX_BUFFER_BIND_ID,
1,
VK_FORMAT_R32G32_SFLOAT,
sizeof(float) * 3);
// Location 2 : Color
vertices.attributeDescriptions[2] =
vks::initializers::vertexInputAttributeDescription(
VERTEX_BUFFER_BIND_ID,
2,
VK_FORMAT_R32G32B32_SFLOAT,
sizeof(float) * 5);
// Location 3 : Normal
vertices.attributeDescriptions[3] =
vks::initializers::vertexInputAttributeDescription(
VERTEX_BUFFER_BIND_ID,
3,
VK_FORMAT_R32G32B32_SFLOAT,
sizeof(float) * 8);
vertices.inputState = vks::initializers::pipelineVertexInputStateCreateInfo();
vertices.inputState.vertexBindingDescriptionCount = vertices.bindingDescriptions.size();
vertices.inputState.pVertexBindingDescriptions = vertices.bindingDescriptions.data();
vertices.inputState.vertexAttributeDescriptionCount = vertices.attributeDescriptions.size();
vertices.inputState.pVertexAttributeDescriptions = vertices.attributeDescriptions.data();
}
void setupDescriptorPool() void setupDescriptorPool()
{ {
std::vector<VkDescriptorPoolSize> poolSizes = std::vector<VkDescriptorPoolSize> poolSizes =
@ -667,78 +586,36 @@ public:
void preparePipelines() void preparePipelines()
{ {
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = vks::initializers::pipelineInputAssemblyStateCreateInfo(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0, VK_FALSE);
vks::initializers::pipelineInputAssemblyStateCreateInfo( VkPipelineRasterizationStateCreateInfo rasterizationStateCI = vks::initializers::pipelineRasterizationStateCreateInfo(VK_POLYGON_MODE_FILL, VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE, 0);
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, VkPipelineColorBlendAttachmentState blendAttachmentState = vks::initializers::pipelineColorBlendAttachmentState(0xf, VK_FALSE);
0, VkPipelineColorBlendStateCreateInfo colorBlendStateCI = vks::initializers::pipelineColorBlendStateCreateInfo(1, &blendAttachmentState);
VK_FALSE); VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = vks::initializers::pipelineDepthStencilStateCreateInfo(VK_TRUE, VK_TRUE, VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportStateCI = vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineRasterizationStateCreateInfo rasterizationState = VkPipelineMultisampleStateCreateInfo multisampleStateCI = vks::initializers::pipelineMultisampleStateCreateInfo(VK_SAMPLE_COUNT_1_BIT, 0);
vks::initializers::pipelineRasterizationStateCreateInfo( std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
VK_POLYGON_MODE_FILL, VkPipelineDynamicStateCreateInfo dynamicStateCI = vks::initializers::pipelineDynamicStateCreateInfo(dynamicStateEnables.data(), dynamicStateEnables.size(), 0);
VK_CULL_MODE_NONE,
VK_FRONT_FACE_CLOCKWISE,
0);
VkPipelineColorBlendAttachmentState blendAttachmentState =
vks::initializers::pipelineColorBlendAttachmentState(
0xf,
VK_FALSE);
VkPipelineColorBlendStateCreateInfo colorBlendState =
vks::initializers::pipelineColorBlendStateCreateInfo(
1,
&blendAttachmentState);
VkPipelineDepthStencilStateCreateInfo depthStencilState =
vks::initializers::pipelineDepthStencilStateCreateInfo(
VK_TRUE,
VK_TRUE,
VK_COMPARE_OP_LESS_OR_EQUAL);
VkPipelineViewportStateCreateInfo viewportState =
vks::initializers::pipelineViewportStateCreateInfo(1, 1, 0);
VkPipelineMultisampleStateCreateInfo multisampleState =
vks::initializers::pipelineMultisampleStateCreateInfo(
VK_SAMPLE_COUNT_1_BIT,
0);
std::vector<VkDynamicState> dynamicStateEnables = {
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR
};
VkPipelineDynamicStateCreateInfo dynamicState =
vks::initializers::pipelineDynamicStateCreateInfo(
dynamicStateEnables.data(),
dynamicStateEnables.size(),
0);
std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages; std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
VkGraphicsPipelineCreateInfo pipelineCreateInfo = VkGraphicsPipelineCreateInfo pipelineCI = vks::initializers::pipelineCreateInfo(pipelineLayouts.blur, renderPass, 0);
vks::initializers::pipelineCreateInfo( pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
pipelineLayouts.blur, pipelineCI.pRasterizationState = &rasterizationStateCI;
renderPass, pipelineCI.pColorBlendState = &colorBlendStateCI;
0); pipelineCI.pMultisampleState = &multisampleStateCI;
pipelineCI.pViewportState = &viewportStateCI;
pipelineCreateInfo.pInputAssemblyState = &inputAssemblyState; pipelineCI.pDepthStencilState = &depthStencilStateCI;
pipelineCreateInfo.pRasterizationState = &rasterizationState; pipelineCI.pDynamicState = &dynamicStateCI;
pipelineCreateInfo.pColorBlendState = &colorBlendState; pipelineCI.stageCount = shaderStages.size();
pipelineCreateInfo.pMultisampleState = &multisampleState; pipelineCI.pStages = shaderStages.data();
pipelineCreateInfo.pViewportState = &viewportState;
pipelineCreateInfo.pDepthStencilState = &depthStencilState;
pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.stageCount = shaderStages.size();
pipelineCreateInfo.pStages = shaderStages.data();
// Blur pipelines // Blur pipelines
shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/gaussblur.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/gaussblur.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/gaussblur.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/gaussblur.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
// Empty vertex input state // Empty vertex input state
VkPipelineVertexInputStateCreateInfo emptyInputState = vks::initializers::pipelineVertexInputStateCreateInfo(); VkPipelineVertexInputStateCreateInfo emptyInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
pipelineCreateInfo.pVertexInputState = &emptyInputState; pipelineCI.pVertexInputState = &emptyInputState;
pipelineCreateInfo.layout = pipelineLayouts.blur; pipelineCI.layout = pipelineLayouts.blur;
// Additive blending // Additive blending
blendAttachmentState.colorWriteMask = 0xF; blendAttachmentState.colorWriteMask = 0xF;
blendAttachmentState.blendEnable = VK_TRUE; blendAttachmentState.blendEnable = VK_TRUE;
@ -755,37 +632,52 @@ public:
VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(1, &specializationMapEntry, sizeof(uint32_t), &blurdirection); VkSpecializationInfo specializationInfo = vks::initializers::specializationInfo(1, &specializationMapEntry, sizeof(uint32_t), &blurdirection);
shaderStages[1].pSpecializationInfo = &specializationInfo; shaderStages[1].pSpecializationInfo = &specializationInfo;
// Vertical blur pipeline // Vertical blur pipeline
pipelineCreateInfo.renderPass = offscreenPass.renderPass; pipelineCI.renderPass = offscreenPass.renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.blurVert)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.blurVert));
// Horizontal blur pipeline // Horizontal blur pipeline
blurdirection = 1; blurdirection = 1;
pipelineCreateInfo.renderPass = renderPass; pipelineCI.renderPass = renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.blurHorz)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.blurHorz));
// Phong pass (3D model) // Phong pass (3D model)
pipelineCreateInfo.layout = pipelineLayouts.scene; // Vertex bindings and attributes
pipelineCreateInfo.pVertexInputState = &vertices.inputState; std::vector<VkVertexInputBindingDescription> vertexInputBindings = {
vks::initializers::vertexInputBindingDescription(0, vertexLayout.stride(), VK_VERTEX_INPUT_RATE_VERTEX),
};
std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
vks::initializers::vertexInputAttributeDescription(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position
vks::initializers::vertexInputAttributeDescription(0, 1, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 3), // Texture coordinates
vks::initializers::vertexInputAttributeDescription(0, 2, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 5), // Color
vks::initializers::vertexInputAttributeDescription(0, 3, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 8), // Normal
};
VkPipelineVertexInputStateCreateInfo vertexInputState = vks::initializers::pipelineVertexInputStateCreateInfo();
vertexInputState.vertexBindingDescriptionCount = static_cast<uint32_t>(vertexInputBindings.size());
vertexInputState.pVertexBindingDescriptions = vertexInputBindings.data();
vertexInputState.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
vertexInputState.pVertexAttributeDescriptions = vertexInputAttributes.data();
pipelineCI.pVertexInputState = &vertexInputState;
pipelineCI.layout = pipelineLayouts.scene;
shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/phongpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/phongpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/phongpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/phongpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
blendAttachmentState.blendEnable = VK_FALSE; blendAttachmentState.blendEnable = VK_FALSE;
depthStencilState.depthWriteEnable = VK_TRUE; depthStencilStateCI.depthWriteEnable = VK_TRUE;
rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT; rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT;
pipelineCreateInfo.renderPass = renderPass; pipelineCI.renderPass = renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.phongPass)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.phongPass));
// Color only pass (offscreen blur base) // Color only pass (offscreen blur base)
shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/colorpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/colorpass.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/colorpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/colorpass.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
pipelineCreateInfo.renderPass = offscreenPass.renderPass; pipelineCI.renderPass = offscreenPass.renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.glowPass)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.glowPass));
// Skybox (cubemap) // Skybox (cubemap)
shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/skybox.vert.spv", VK_SHADER_STAGE_VERTEX_BIT); shaderStages[0] = loadShader(getAssetPath() + "shaders/bloom/skybox.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/skybox.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT); shaderStages[1] = loadShader(getAssetPath() + "shaders/bloom/skybox.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
depthStencilState.depthWriteEnable = VK_FALSE; depthStencilStateCI.depthWriteEnable = VK_FALSE;
rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT; rasterizationStateCI.cullMode = VK_CULL_MODE_FRONT_BIT;
pipelineCreateInfo.renderPass = renderPass; pipelineCI.renderPass = renderPass;
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCreateInfo, nullptr, &pipelines.skyBox)); VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skyBox));
} }
// Prepare and initialize uniform buffer containing shader uniforms // Prepare and initialize uniform buffer containing shader uniforms
@ -852,36 +744,9 @@ public:
void draw() void draw()
{ {
VulkanExampleBase::prepareFrame(); 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 rendering 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 = &offscreenPass.semaphore;
// Submit work
submitInfo.commandBufferCount = 1; submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &offscreenPass.commandBuffer;
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
// Scene rendering
// Wait for offscreen semaphore
submitInfo.pWaitSemaphores = &offscreenPass.semaphore;
// Signal ready with render complete semaphpre
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
// Submit work
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE)); VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanExampleBase::submitFrame(); VulkanExampleBase::submitFrame();
} }
@ -889,7 +754,6 @@ public:
{ {
VulkanExampleBase::prepare(); VulkanExampleBase::prepare();
loadAssets(); loadAssets();
setupVertexDescriptions();
prepareUniformBuffers(); prepareUniformBuffers();
prepareOffscreen(); prepareOffscreen();
setupDescriptorSetLayout(); setupDescriptorSetLayout();
@ -905,17 +769,12 @@ public:
if (!prepared) if (!prepared)
return; return;
draw(); draw();
if (!paused) if (!paused || camera.updated)
{ {
updateUniformBuffersScene(); updateUniformBuffersScene();
} }
} }
virtual void viewChanged()
{
updateUniformBuffersScene();
}
virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay) virtual void OnUpdateUIOverlay(vks::UIOverlay *overlay)
{ {
if (overlay->header("Settings")) { if (overlay->header("Settings")) {