aboutsummaryrefslogtreecommitdiffstatsci
path: root/src/pbrrenderer2.cpp
diff options
context:
space:
mode:
authorJean-Philippe Bruyère <jp_bruyere@hotmail.com>2018-10-17 18:49:56 +0200
committerJean-Philippe Bruyère <jp_bruyere@hotmail.com>2018-10-17 18:49:56 +0200
commit328c11f4dfd2653f9b6d12633b38d7baa49958da (patch)
treea5483d189f3a46f5fbbb32e3c63a8cda9039dfee /src/pbrrenderer2.cpp
first commit, splitted from vkChess
Diffstat (limited to 'src/pbrrenderer2.cpp')
-rw-r--r--src/pbrrenderer2.cpp1054
1 files changed, 1054 insertions, 0 deletions
diff --git a/src/pbrrenderer2.cpp b/src/pbrrenderer2.cpp
new file mode 100644
index 0000000..2161c75
--- /dev/null
+++ b/src/pbrrenderer2.cpp
@@ -0,0 +1,1054 @@
+#include "pbrrenderer2.h"
+
+pbrRenderer::pbrRenderer () : vkRenderer()
+{
+}
+
+pbrRenderer::~pbrRenderer() {
+ if (prepared)
+ destroy();
+}
+
+void pbrRenderer::destroy() {
+ prepared = false;
+
+ vkDestroyPipeline(device->dev, pipelines.skybox, nullptr);
+ vkDestroyPipeline(device->dev, pipelines.pbr, nullptr);
+ vkDestroyPipeline(device->dev, pipelines.pbrAlphaBlend, nullptr);
+
+ for (uint i=0; i<models.size(); i++)
+ models[i].destroy();
+
+ vkRenderer::destroy();
+}
+
+void pbrRenderer::prepareModels() {
+ for (uint i=0; i<models.size(); i++) {
+ models[i].buildInstanceBuffer();
+ models[i].allocateDescriptorSet (shadingCtx);
+ }
+ buildCommandBuffer();
+}
+
+void pbrRenderer::configurePipelineLayout () {
+ shadingCtx = new vks::ShadingContext (device, 3);
+
+ shadingCtx->addDescriptorSetLayout(
+ {//scene
+ { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//matrices
+ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//params
+ { 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//irradiance
+ { 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//prefiltered cube
+ { 4, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//lutBrdf
+ });
+ shadingCtx->addDescriptorSetLayout(
+ {//meshes
+ { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//texture sampler
+ { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//material ubo
+ });
+ shadingCtx->addDescriptorSetLayout(
+ {//meshes
+ { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr },//vkvg texture
+ });
+
+
+ shadingCtx->prepare();
+
+}
+void pbrRenderer::loadRessources() {
+ textures.environmentCube.loadFromFile("data/textures/papermill_hdr16f_cube.ktx", VK_FORMAT_R16G16B16A16_SFLOAT, device );
+ skybox.loadFromFile("data/models/Box/glTF-Embedded/Box.gltf", device);
+
+ generateBRDFLUT();
+ generateCubemaps();
+}
+void pbrRenderer::freeRessources() {
+ skybox.destroy();
+
+ textures.environmentCube.destroy();
+ textures.irradianceCube.destroy();
+ textures.prefilteredCube.destroy();
+ textures.lutBrdf.destroy();
+}
+void pbrRenderer::prepareDescriptors()
+{
+ dsScene = shadingCtx->allocateDescriptorSet(0);
+
+ shadingCtx->updateDescriptorSet (dsScene,
+ {
+ {0,0,&sharedUBOs.matrices},
+ {0,1,&sharedUBOs.params},
+ {0,2,&textures.irradianceCube},
+ {0,3,&textures.prefilteredCube},
+ {0,4,&textures.lutBrdf}
+ });
+
+ descriptorSet = shadingCtx->allocateDescriptorSet (2);
+ shadingCtx->updateDescriptorSet (descriptorSet,
+ {
+ {2,0,&fullScreenTex},
+ });
+}
+void pbrRenderer::preparePipeline() {
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI = {VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
+ inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ VkPipelineRasterizationStateCreateInfo rasterizationStateCI = {VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
+ rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT;
+ rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterizationStateCI.lineWidth = 1.0f;
+
+ VkPipelineColorBlendAttachmentState blendAttachmentState{};
+ blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ blendAttachmentState.blendEnable = VK_FALSE;
+
+ VkPipelineColorBlendStateCreateInfo colorBlendStateCI = {VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
+ colorBlendStateCI.attachmentCount = 1;
+ colorBlendStateCI.pAttachments = &blendAttachmentState;
+
+ VkPipelineDepthStencilStateCreateInfo depthStencilStateCI = {VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
+ depthStencilStateCI.depthTestEnable = VK_FALSE;
+ depthStencilStateCI.depthWriteEnable = VK_FALSE;
+ depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
+ depthStencilStateCI.front = depthStencilStateCI.back;
+ depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS;
+
+ VkPipelineViewportStateCreateInfo viewportStateCI = {VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
+ viewportStateCI.viewportCount = 1;
+ viewportStateCI.scissorCount = 1;
+
+ VkPipelineMultisampleStateCreateInfo multisampleStateCI = {VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
+
+ if (renderTarget->samples > VK_SAMPLE_COUNT_1_BIT)
+ multisampleStateCI.rasterizationSamples = renderTarget->samples;
+
+ std::vector<VkDynamicState> dynamicStateEnables = {
+ VK_DYNAMIC_STATE_VIEWPORT,
+ VK_DYNAMIC_STATE_SCISSOR
+ };
+ VkPipelineDynamicStateCreateInfo dynamicStateCI = {VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
+ dynamicStateCI.pDynamicStates = dynamicStateEnables.data();
+ dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
+
+ // Pipeline layout
+
+ VkPipelineLayoutCreateInfo pipelineLayoutCI = {VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
+ pipelineLayoutCI.setLayoutCount = shadingCtx->layouts.size();
+ pipelineLayoutCI.pSetLayouts = shadingCtx->layouts.data();
+ VK_CHECK_RESULT(vkCreatePipelineLayout(device->dev, &pipelineLayoutCI, nullptr, &pipelineLayout));
+
+ // Vertex bindings an attributes
+ std::vector<VkVertexInputBindingDescription> vertexInputBinding = {
+ {0, sizeof(vkglTF::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX},
+ {1, sizeof(vkglTF::InstanceData), VK_VERTEX_INPUT_RATE_INSTANCE},
+ };
+ std::vector<VkVertexInputAttributeDescription> vertexInputAttributes = {
+ { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 },
+ { 1, 0, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3 },
+ { 2, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 6 },
+ //instance
+ { 3, 1, VK_FORMAT_R32_UINT, 0 }, //material index
+ { 4, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 4}, //model matrix
+ { 5, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 20},
+ { 6, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 36},
+ { 7, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 52},
+ { 8, 1, VK_FORMAT_R32G32B32A32_SFLOAT, 68}, //color for selection
+ };
+ VkPipelineVertexInputStateCreateInfo vertexInputStateCI = {VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
+ vertexInputStateCI.vertexBindingDescriptionCount = 2;
+ vertexInputStateCI.pVertexBindingDescriptions = vertexInputBinding.data();
+ vertexInputStateCI.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
+ vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data();
+
+ // Pipelines
+ std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
+
+ VkGraphicsPipelineCreateInfo pipelineCI = {VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
+ pipelineCI.layout = pipelineLayout;
+ pipelineCI.renderPass = renderTarget->renderPass;
+ pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
+ pipelineCI.pVertexInputState = &vertexInputStateCI;
+ pipelineCI.pRasterizationState = &rasterizationStateCI;
+ pipelineCI.pColorBlendState = &colorBlendStateCI;
+ pipelineCI.pMultisampleState = &multisampleStateCI;
+ pipelineCI.pViewportState = &viewportStateCI;
+ pipelineCI.pDepthStencilState = &depthStencilStateCI;
+ pipelineCI.pDynamicState = &dynamicStateCI;
+ pipelineCI.stageCount = static_cast<uint32_t>(shaderStages.size());
+ pipelineCI.pStages = shaderStages.data();
+
+ // Skybox pipeline (background cube)
+ rasterizationStateCI.cullMode = VK_CULL_MODE_FRONT_BIT;
+ vertexInputStateCI.vertexBindingDescriptionCount = 1;
+ vertexInputStateCI.vertexAttributeDescriptionCount = 3;
+
+ shaderStages = {
+ loadShader(device->dev, "skybox.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
+ loadShader(device->dev, "skybox.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)
+ };
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skybox));
+
+ for (auto shaderStage : shaderStages)
+ vkDestroyShaderModule(device->dev, shaderStage.module, nullptr);
+
+ // PBR pipeline
+ rasterizationStateCI.cullMode = VK_CULL_MODE_BACK_BIT;
+ vertexInputStateCI.vertexBindingDescriptionCount = 2;
+ vertexInputStateCI.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertexInputAttributes.size());
+ depthStencilStateCI.depthWriteEnable = VK_TRUE;
+ depthStencilStateCI.depthTestEnable = VK_TRUE;
+
+ shaderStages = {
+ loadShader(device->dev, "pbr.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
+ loadShader(device->dev, "pbr.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)
+ };
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbr));
+
+ depthStencilStateCI.depthWriteEnable = VK_FALSE;
+ depthStencilStateCI.depthTestEnable = VK_FALSE;
+
+ rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
+ blendAttachmentState.blendEnable = VK_TRUE;
+ blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
+ blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
+ blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
+ blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
+ blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbrAlphaBlend));
+
+ for (auto shaderStage : shaderStages)
+ vkDestroyShaderModule(device->dev, shaderStage.module, nullptr);
+
+ //create full screen quad pipeline to render vkvg texture
+ rasterizationStateCI.cullMode = VK_CULL_MODE_FRONT_BIT;
+ rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ vertexInputStateCI.vertexBindingDescriptionCount = 0;
+ vertexInputStateCI.vertexAttributeDescriptionCount = 0;
+ vertexInputStateCI.pVertexBindingDescriptions = nullptr;
+ vertexInputStateCI.pVertexAttributeDescriptions = nullptr;
+
+ shaderStages = {
+ loadShader(device->dev, "FullScreenQuad.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
+ loadShader(device->dev, "simpletexture.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)
+ };
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
+
+ for (auto shaderStage : shaderStages)
+ vkDestroyShaderModule(device->dev, shaderStage.module, nullptr);
+}
+
+void pbrRenderer::draw(VkCommandBuffer cmdBuff) {
+ vkCmdBindDescriptorSets(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &dsScene, 0, NULL);
+
+ vkCmdBindPipeline(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
+
+ skybox.buildCommandBuffer (cmdBuff, pipelineLayout);
+
+ // Opaque primitives
+ vkCmdBindPipeline(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.pbr);
+ models[0].buildCommandBuffer (cmdBuff, pipelineLayout);
+
+ //full screen quad
+ fullScreenTex.setImageLayout(cmdBuff, VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
+
+ vkCmdBindDescriptorSets(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 2, 1, &descriptorSet, 0, NULL);
+ vkCmdBindPipeline(cmdBuff, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+
+ vkCmdDraw(cmdBuff, 3, 1, 0, 0);
+
+ fullScreenTex.setImageLayout(cmdBuff, VK_IMAGE_ASPECT_COLOR_BIT,
+ VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
+ VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
+}
+
+void pbrRenderer::generateBRDFLUT()
+{
+ auto tStart = std::chrono::high_resolution_clock::now();
+
+ const VkFormat format = VK_FORMAT_R16G16_SFLOAT;
+ const int32_t dim = 512;
+
+ // Image
+ VkImageCreateInfo imageCI{};
+ imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageCI.imageType = VK_IMAGE_TYPE_2D;
+ imageCI.format = format;
+ imageCI.extent.width = dim;
+ imageCI.extent.height = dim;
+ imageCI.extent.depth = 1;
+ imageCI.mipLevels = 1;
+ imageCI.arrayLayers = 1;
+ imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
+ VK_CHECK_RESULT(vkCreateImage(device->dev, &imageCI, nullptr, &textures.lutBrdf.image));
+ VkMemoryRequirements memReqs;
+ vkGetImageMemoryRequirements(device->dev, textures.lutBrdf.image, &memReqs);
+ VkMemoryAllocateInfo memAllocInfo{};
+ memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ memAllocInfo.allocationSize = memReqs.size;
+ memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK_RESULT(vkAllocateMemory(device->dev, &memAllocInfo, nullptr, &textures.lutBrdf.deviceMemory));
+ VK_CHECK_RESULT(vkBindImageMemory(device->dev, textures.lutBrdf.image, textures.lutBrdf.deviceMemory, 0));
+
+ // View
+ VkImageViewCreateInfo viewCI{};
+ viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewCI.format = format;
+ viewCI.subresourceRange = {};
+ viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewCI.subresourceRange.levelCount = 1;
+ viewCI.subresourceRange.layerCount = 1;
+ viewCI.image = textures.lutBrdf.image;
+ VK_CHECK_RESULT(vkCreateImageView(device->dev, &viewCI, nullptr, &textures.lutBrdf.view));
+
+ // Sampler
+ VkSamplerCreateInfo samplerCI{};
+ samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ samplerCI.magFilter = VK_FILTER_LINEAR;
+ samplerCI.minFilter = VK_FILTER_LINEAR;
+ samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCI.minLod = 0.0f;
+ samplerCI.maxLod = 1.0f;
+ samplerCI.maxAnisotropy = 1.0f;
+ samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
+ VK_CHECK_RESULT(vkCreateSampler(device->dev, &samplerCI, nullptr, &textures.lutBrdf.sampler));
+
+ // FB, Att, RP, Pipe, etc.
+ VkAttachmentDescription attDesc{};
+ // Color attachment
+ attDesc.format = format;
+ attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+
+ VkSubpassDescription subpassDescription{};
+ subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDescription.colorAttachmentCount = 1;
+ subpassDescription.pColorAttachments = &colorReference;
+
+ // Use subpass dependencies for layout transitions
+ std::array<VkSubpassDependency, 2> 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 renderPassCI{};
+ renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ renderPassCI.attachmentCount = 1;
+ renderPassCI.pAttachments = &attDesc;
+ renderPassCI.subpassCount = 1;
+ renderPassCI.pSubpasses = &subpassDescription;
+ renderPassCI.dependencyCount = 2;
+ renderPassCI.pDependencies = dependencies.data();
+
+ VkRenderPass renderpass;
+ VK_CHECK_RESULT(vkCreateRenderPass(device->dev, &renderPassCI, nullptr, &renderpass));
+
+ VkFramebufferCreateInfo framebufferCI{};
+ framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ framebufferCI.renderPass = renderpass;
+ framebufferCI.attachmentCount = 1;
+ framebufferCI.pAttachments = &textures.lutBrdf.view;
+ framebufferCI.width = dim;
+ framebufferCI.height = dim;
+ framebufferCI.layers = 1;
+
+ VkFramebuffer framebuffer;
+ VK_CHECK_RESULT(vkCreateFramebuffer(device->dev, &framebufferCI, nullptr, &framebuffer));
+
+ // Desriptors
+ VkDescriptorSetLayout descriptorsetlayout;
+ VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
+ descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->dev, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout));
+
+ // Pipeline layout
+ VkPipelineLayout pipelinelayout;
+ VkPipelineLayoutCreateInfo pipelineLayoutCI{};
+ pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutCI.setLayoutCount = 1;
+ pipelineLayoutCI.pSetLayouts = &descriptorsetlayout;
+ VK_CHECK_RESULT(vkCreatePipelineLayout(device->dev, &pipelineLayoutCI, nullptr, &pipelinelayout));
+
+ // Pipeline
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{};
+ inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ VkPipelineRasterizationStateCreateInfo rasterizationStateCI{};
+ rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
+ rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterizationStateCI.lineWidth = 1.0f;
+
+ VkPipelineColorBlendAttachmentState blendAttachmentState{};
+ blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ blendAttachmentState.blendEnable = VK_FALSE;
+
+ VkPipelineColorBlendStateCreateInfo colorBlendStateCI{};
+ colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ colorBlendStateCI.attachmentCount = 1;
+ colorBlendStateCI.pAttachments = &blendAttachmentState;
+
+ VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{};
+ depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ depthStencilStateCI.depthTestEnable = VK_FALSE;
+ depthStencilStateCI.depthWriteEnable = VK_FALSE;
+ depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
+ depthStencilStateCI.front = depthStencilStateCI.back;
+ depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS;
+
+ VkPipelineViewportStateCreateInfo viewportStateCI{};
+ viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportStateCI.viewportCount = 1;
+ viewportStateCI.scissorCount = 1;
+
+ VkPipelineMultisampleStateCreateInfo multisampleStateCI{};
+ multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+ std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
+ VkPipelineDynamicStateCreateInfo dynamicStateCI{};
+ dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamicStateCI.pDynamicStates = dynamicStateEnables.data();
+ dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
+
+ VkPipelineVertexInputStateCreateInfo emptyInputStateCI{};
+ emptyInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+
+ std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
+
+ VkGraphicsPipelineCreateInfo pipelineCI{};
+ pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipelineCI.layout = pipelinelayout;
+ pipelineCI.renderPass = renderpass;
+ pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
+ pipelineCI.pVertexInputState = &emptyInputStateCI;
+ pipelineCI.pRasterizationState = &rasterizationStateCI;
+ pipelineCI.pColorBlendState = &colorBlendStateCI;
+ pipelineCI.pMultisampleState = &multisampleStateCI;
+ pipelineCI.pViewportState = &viewportStateCI;
+ pipelineCI.pDepthStencilState = &depthStencilStateCI;
+ pipelineCI.pDynamicState = &dynamicStateCI;
+ pipelineCI.stageCount = 2;
+ pipelineCI.pStages = shaderStages.data();
+
+ // Look-up-table (from BRDF) pipeline
+ shaderStages = {
+ loadShader(device->dev, "genbrdflut.vert.spv", VK_SHADER_STAGE_VERTEX_BIT),
+ loadShader(device->dev, "genbrdflut.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT)
+ };
+ VkPipeline pipeline;
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
+ for (auto shaderStage : shaderStages) {
+ vkDestroyShaderModule(device->dev, shaderStage.module, nullptr);
+ }
+
+ // Render
+ VkClearValue clearValues[1];
+ clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } };
+
+ VkRenderPassBeginInfo renderPassBeginInfo{};
+ renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
+ renderPassBeginInfo.renderPass = renderpass;
+ renderPassBeginInfo.renderArea.extent.width = dim;
+ renderPassBeginInfo.renderArea.extent.height = dim;
+ renderPassBeginInfo.clearValueCount = 1;
+ renderPassBeginInfo.pClearValues = clearValues;
+ renderPassBeginInfo.framebuffer = framebuffer;
+
+ VkCommandBuffer cmdBuf = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
+ vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ VkViewport viewport{};
+ viewport.width = (float)dim;
+ viewport.height = (float)dim;
+ viewport.minDepth = 0.0f;
+ viewport.maxDepth = 1.0f;
+
+ VkRect2D scissor{};
+ scissor.extent.width = renderTarget->width;
+ scissor.extent.height = renderTarget->height;
+
+ vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
+ vkCmdSetScissor(cmdBuf, 0, 1, &scissor);
+ vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ vkCmdDraw(cmdBuf, 3, 1, 0, 0);
+ vkCmdEndRenderPass(cmdBuf);
+ device->flushCommandBuffer(cmdBuf, device->queue);
+
+ vkQueueWaitIdle(device->queue);
+
+ vkDestroyPipeline(device->dev, pipeline, nullptr);
+ vkDestroyPipelineLayout(device->dev, pipelinelayout, nullptr);
+ vkDestroyRenderPass(device->dev, renderpass, nullptr);
+ vkDestroyFramebuffer(device->dev, framebuffer, nullptr);
+ vkDestroyDescriptorSetLayout(device->dev, descriptorsetlayout, nullptr);
+
+ textures.lutBrdf.descriptor.imageView = textures.lutBrdf.view;
+ textures.lutBrdf.descriptor.sampler = textures.lutBrdf.sampler;
+ textures.lutBrdf.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ textures.lutBrdf.device = device;
+
+ auto tEnd = std::chrono::high_resolution_clock::now();
+ auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
+ std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl;
+}
+void pbrRenderer::generateCubemaps()
+{
+ enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 };
+
+ for (uint32_t target = 0; target < PREFILTEREDENV + 1; target++) {
+
+ vks::TextureCubeMap cubemap;
+
+ auto tStart = std::chrono::high_resolution_clock::now();
+
+ VkFormat format;
+ int32_t dim;
+
+ switch (target) {
+ case IRRADIANCE:
+ format = VK_FORMAT_R32G32B32A32_SFLOAT;
+ dim = 64;
+ break;
+ case PREFILTEREDENV:
+ format = VK_FORMAT_R16G16B16A16_SFLOAT;
+ dim = 512;
+ break;
+ };
+
+ const uint32_t numMips = static_cast<uint32_t>(floor(log2(dim))) + 1;
+
+ // Create target cubemap
+ {
+ // Image
+ VkImageCreateInfo imageCI{};
+ imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageCI.imageType = VK_IMAGE_TYPE_2D;
+ imageCI.format = format;
+ imageCI.extent.width = dim;
+ imageCI.extent.height = dim;
+ imageCI.extent.depth = 1;
+ imageCI.mipLevels = numMips;
+ imageCI.arrayLayers = 6;
+ imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageCI.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ imageCI.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
+ VK_CHECK_RESULT(vkCreateImage(device->dev, &imageCI, nullptr, &cubemap.image));
+ VkMemoryRequirements memReqs;
+ vkGetImageMemoryRequirements(device->dev, cubemap.image, &memReqs);
+ VkMemoryAllocateInfo memAllocInfo{};
+ memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ memAllocInfo.allocationSize = memReqs.size;
+ memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK_RESULT(vkAllocateMemory(device->dev, &memAllocInfo, nullptr, &cubemap.deviceMemory));
+ VK_CHECK_RESULT(vkBindImageMemory(device->dev, cubemap.image, cubemap.deviceMemory, 0));
+
+ // View
+ VkImageViewCreateInfo viewCI{};
+ viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewCI.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
+ viewCI.format = format;
+ viewCI.subresourceRange = {};
+ viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewCI.subresourceRange.levelCount = numMips;
+ viewCI.subresourceRange.layerCount = 6;
+ viewCI.image = cubemap.image;
+ VK_CHECK_RESULT(vkCreateImageView(device->dev, &viewCI, nullptr, &cubemap.view));
+
+ // Sampler
+ VkSamplerCreateInfo samplerCI{};
+ samplerCI.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
+ samplerCI.magFilter = VK_FILTER_LINEAR;
+ samplerCI.minFilter = VK_FILTER_LINEAR;
+ samplerCI.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
+ samplerCI.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCI.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCI.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
+ samplerCI.minLod = 0.0f;
+ samplerCI.maxLod = static_cast<float>(numMips);
+ samplerCI.maxAnisotropy = 1.0f;
+ samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE;
+ VK_CHECK_RESULT(vkCreateSampler(device->dev, &samplerCI, nullptr, &cubemap.sampler));
+ }
+
+ // FB, Att, RP, Pipe, etc.
+ VkAttachmentDescription attDesc{};
+ // Color attachment
+ attDesc.format = format;
+ attDesc.samples = VK_SAMPLE_COUNT_1_BIT;
+ attDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
+ attDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
+ attDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+ attDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+ attDesc.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ attDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ VkAttachmentReference colorReference = { 0, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL };
+
+ VkSubpassDescription subpassDescription{};
+ subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
+ subpassDescription.colorAttachmentCount = 1;
+ subpassDescription.pColorAttachments = &colorReference;
+
+ // Use subpass dependencies for layout transitions
+ std::array<VkSubpassDependency, 2> 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;
+
+ // Renderpass
+ VkRenderPassCreateInfo renderPassCI{};
+ renderPassCI.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
+ renderPassCI.attachmentCount = 1;
+ renderPassCI.pAttachments = &attDesc;
+ renderPassCI.subpassCount = 1;
+ renderPassCI.pSubpasses = &subpassDescription;
+ renderPassCI.dependencyCount = 2;
+ renderPassCI.pDependencies = dependencies.data();
+ VkRenderPass renderpass;
+ VK_CHECK_RESULT(vkCreateRenderPass(device->dev, &renderPassCI, nullptr, &renderpass));
+
+ struct Offscreen {
+ VkImage image;
+ VkImageView view;
+ VkDeviceMemory memory;
+ VkFramebuffer framebuffer;
+ } offscreen;
+
+ // Create offscreen framebuffer
+ {
+ // Image
+ VkImageCreateInfo imageCI{};
+ imageCI.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
+ imageCI.imageType = VK_IMAGE_TYPE_2D;
+ imageCI.format = format;
+ imageCI.extent.width = dim;
+ imageCI.extent.height = dim;
+ imageCI.extent.depth = 1;
+ imageCI.mipLevels = 1;
+ imageCI.arrayLayers = 1;
+ imageCI.samples = VK_SAMPLE_COUNT_1_BIT;
+ imageCI.tiling = VK_IMAGE_TILING_OPTIMAL;
+ imageCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ imageCI.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
+ imageCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ VK_CHECK_RESULT(vkCreateImage(device->dev, &imageCI, nullptr, &offscreen.image));
+ VkMemoryRequirements memReqs;
+ vkGetImageMemoryRequirements(device->dev, offscreen.image, &memReqs);
+ VkMemoryAllocateInfo memAllocInfo{};
+ memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
+ memAllocInfo.allocationSize = memReqs.size;
+ memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ VK_CHECK_RESULT(vkAllocateMemory(device->dev, &memAllocInfo, nullptr, &offscreen.memory));
+ VK_CHECK_RESULT(vkBindImageMemory(device->dev, offscreen.image, offscreen.memory, 0));
+
+ // View
+ VkImageViewCreateInfo viewCI{};
+ viewCI.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
+ viewCI.viewType = VK_IMAGE_VIEW_TYPE_2D;
+ viewCI.format = format;
+ viewCI.flags = 0;
+ viewCI.subresourceRange = {};
+ viewCI.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ viewCI.subresourceRange.baseMipLevel = 0;
+ viewCI.subresourceRange.levelCount = 1;
+ viewCI.subresourceRange.baseArrayLayer = 0;
+ viewCI.subresourceRange.layerCount = 1;
+ viewCI.image = offscreen.image;
+ VK_CHECK_RESULT(vkCreateImageView(device->dev, &viewCI, nullptr, &offscreen.view));
+
+ // Framebuffer
+ VkFramebufferCreateInfo framebufferCI{};
+ framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
+ framebufferCI.renderPass = renderpass;
+ framebufferCI.attachmentCount = 1;
+ framebufferCI.pAttachments = &offscreen.view;
+ framebufferCI.width = dim;
+ framebufferCI.height = dim;
+ framebufferCI.layers = 1;
+ VK_CHECK_RESULT(vkCreateFramebuffer(device->dev, &framebufferCI, nullptr, &offscreen.framebuffer));
+
+ VkCommandBuffer layoutCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
+ VkImageMemoryBarrier imageMemoryBarrier{};
+ imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ imageMemoryBarrier.image = offscreen.image;
+ imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ imageMemoryBarrier.srcAccessMask = 0;
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+ vkCmdPipelineBarrier(layoutCmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
+ device->flushCommandBuffer(layoutCmd, device->queue, true);
+ }
+
+ // Descriptors
+ VkDescriptorSetLayout descriptorsetlayout;
+ VkDescriptorSetLayoutBinding setLayoutBinding = { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr };
+ VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{};
+ descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
+ descriptorSetLayoutCI.pBindings = &setLayoutBinding;
+ descriptorSetLayoutCI.bindingCount = 1;
+ VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device->dev, &descriptorSetLayoutCI, nullptr, &descriptorsetlayout));
+
+ // Descriptor Pool
+ VkDescriptorPoolSize poolSize = { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1 };
+ VkDescriptorPoolCreateInfo descriptorPoolCI{};
+ descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
+ descriptorPoolCI.poolSizeCount = 1;
+ descriptorPoolCI.pPoolSizes = &poolSize;
+ descriptorPoolCI.maxSets = 2;
+ VkDescriptorPool descriptorpool;
+ VK_CHECK_RESULT(vkCreateDescriptorPool(device->dev, &descriptorPoolCI, nullptr, &descriptorpool));
+
+ // Descriptor sets
+ VkDescriptorSet descriptorset;
+ VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
+ descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
+ descriptorSetAllocInfo.descriptorPool = descriptorpool;
+ descriptorSetAllocInfo.pSetLayouts = &descriptorsetlayout;
+ descriptorSetAllocInfo.descriptorSetCount = 1;
+ VK_CHECK_RESULT(vkAllocateDescriptorSets(device->dev, &descriptorSetAllocInfo, &descriptorset));
+ VkWriteDescriptorSet writeDescriptorSet{};
+ writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
+ writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
+ writeDescriptorSet.descriptorCount = 1;
+ writeDescriptorSet.dstSet = descriptorset;
+ writeDescriptorSet.dstBinding = 0;
+ writeDescriptorSet.pImageInfo = &textures.environmentCube.descriptor;
+ vkUpdateDescriptorSets(device->dev, 1, &writeDescriptorSet, 0, nullptr);
+
+ struct PushBlockIrradiance {
+ glm::mat4 mvp;
+ float deltaPhi = (2.0f * float(M_PI)) / 180.0f;
+ float deltaTheta = (0.5f * float(M_PI)) / 64.0f;
+ } pushBlockIrradiance;
+
+ struct PushBlockPrefilterEnv {
+ glm::mat4 mvp;
+ float roughness;
+ uint32_t numSamples = 32u;
+ } pushBlockPrefilterEnv;
+
+ // Pipeline layout
+ VkPipelineLayout pipelinelayout;
+ VkPushConstantRange pushConstantRange{};
+ pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT;
+
+ switch (target) {
+ case IRRADIANCE:
+ pushConstantRange.size = sizeof(PushBlockIrradiance);
+ break;
+ case PREFILTEREDENV:
+ pushConstantRange.size = sizeof(PushBlockPrefilterEnv);
+ break;
+ };
+
+ VkPipelineLayoutCreateInfo pipelineLayoutCI{};
+ pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
+ pipelineLayoutCI.setLayoutCount = 1;
+ pipelineLayoutCI.pSetLayouts = &descriptorsetlayout;
+ pipelineLayoutCI.pushConstantRangeCount = 1;
+ pipelineLayoutCI.pPushConstantRanges = &pushConstantRange;
+ VK_CHECK_RESULT(vkCreatePipelineLayout(device->dev, &pipelineLayoutCI, nullptr, &pipelinelayout));
+
+ // Pipeline
+ VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{};
+ inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
+ inputAssemblyStateCI.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+
+ VkPipelineRasterizationStateCreateInfo rasterizationStateCI{};
+ rasterizationStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
+ rasterizationStateCI.polygonMode = VK_POLYGON_MODE_FILL;
+ rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
+ rasterizationStateCI.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
+ rasterizationStateCI.lineWidth = 1.0f;
+
+ VkPipelineColorBlendAttachmentState blendAttachmentState{};
+ blendAttachmentState.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
+ blendAttachmentState.blendEnable = VK_FALSE;
+
+ VkPipelineColorBlendStateCreateInfo colorBlendStateCI{};
+ colorBlendStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
+ colorBlendStateCI.attachmentCount = 1;
+ colorBlendStateCI.pAttachments = &blendAttachmentState;
+
+ VkPipelineDepthStencilStateCreateInfo depthStencilStateCI{};
+ depthStencilStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
+ depthStencilStateCI.depthTestEnable = VK_FALSE;
+ depthStencilStateCI.depthWriteEnable = VK_FALSE;
+ depthStencilStateCI.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
+ depthStencilStateCI.front = depthStencilStateCI.back;
+ depthStencilStateCI.back.compareOp = VK_COMPARE_OP_ALWAYS;
+
+ VkPipelineViewportStateCreateInfo viewportStateCI{};
+ viewportStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
+ viewportStateCI.viewportCount = 1;
+ viewportStateCI.scissorCount = 1;
+
+ VkPipelineMultisampleStateCreateInfo multisampleStateCI{};
+ multisampleStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
+ multisampleStateCI.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
+
+ std::vector<VkDynamicState> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
+ VkPipelineDynamicStateCreateInfo dynamicStateCI{};
+ dynamicStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
+ dynamicStateCI.pDynamicStates = dynamicStateEnables.data();
+ dynamicStateCI.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
+
+ // Vertex input state
+ VkVertexInputBindingDescription vertexInputBinding = { 0, sizeof(vkglTF::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX };
+ VkVertexInputAttributeDescription vertexInputAttribute = { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 };
+
+ VkPipelineVertexInputStateCreateInfo vertexInputStateCI{};
+ vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
+ vertexInputStateCI.vertexBindingDescriptionCount = 1;
+ vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding;
+ vertexInputStateCI.vertexAttributeDescriptionCount = 1;
+ vertexInputStateCI.pVertexAttributeDescriptions = &vertexInputAttribute;
+
+ std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages;
+
+ VkGraphicsPipelineCreateInfo pipelineCI{};
+ pipelineCI.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
+ pipelineCI.layout = pipelinelayout;
+ pipelineCI.renderPass = renderpass;
+ pipelineCI.pInputAssemblyState = &inputAssemblyStateCI;
+ pipelineCI.pVertexInputState = &vertexInputStateCI;
+ pipelineCI.pRasterizationState = &rasterizationStateCI;
+ pipelineCI.pColorBlendState = &colorBlendStateCI;
+ pipelineCI.pMultisampleState = &multisampleStateCI;
+ pipelineCI.pViewportState = &viewportStateCI;
+ pipelineCI.pDepthStencilState = &depthStencilStateCI;
+ pipelineCI.pDynamicState = &dynamicStateCI;
+ pipelineCI.stageCount = 2;
+ pipelineCI.pStages = shaderStages.data();
+ pipelineCI.renderPass = renderpass;
+
+ shaderStages[0] = loadShader(device->dev, "filtercube.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
+ switch (target) {
+ case IRRADIANCE:
+ shaderStages[1] = loadShader(device->dev, "irradiancecube.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
+ break;
+ case PREFILTEREDENV:
+ shaderStages[1] = loadShader(device->dev, "prefilterenvmap.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
+ break;
+ };
+ VkPipeline pipeline;
+ VK_CHECK_RESULT(vkCreateGraphicsPipelines(device->dev, device->pipelineCache, 1, &pipelineCI, nullptr, &pipeline));
+ for (auto shaderStage : shaderStages) {
+ vkDestroyShaderModule(device->dev, shaderStage.module, nullptr);
+ }
+
+ // Render cubemap
+ VkClearValue clearValues[1];
+ clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } };
+
+ VkRenderPassBeginInfo renderPassBeginInfo = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO};
+ renderPassBeginInfo.renderPass = renderpass;
+ renderPassBeginInfo.framebuffer = offscreen.framebuffer;
+ renderPassBeginInfo.renderArea.extent.width = dim;
+ renderPassBeginInfo.renderArea.extent.height = dim;
+ renderPassBeginInfo.clearValueCount = 1;
+ renderPassBeginInfo.pClearValues = clearValues;
+
+ std::vector<glm::mat4> matrices = {
+ glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
+ glm::rotate(glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
+ glm::rotate(glm::mat4(1.0f), glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
+ glm::rotate(glm::mat4(1.0f), glm::radians(-90.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
+ glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(1.0f, 0.0f, 0.0f)),
+ glm::rotate(glm::mat4(1.0f), glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f)),
+ };
+
+ VkCommandBuffer cmdBuf = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
+
+ VkViewport viewport = {0, 0, (float)dim, (float)dim, 0.0f, 1.0f};
+ VkRect2D scissor = {{}, {renderTarget->width, renderTarget->height}};
+
+ vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
+ vkCmdSetScissor(cmdBuf, 0, 1, &scissor);
+
+ VkImageSubresourceRange subresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, numMips,0 , 6};
+
+ // Change image layout for all cubemap faces to transfer destination
+ {
+ VkImageMemoryBarrier imageMemoryBarrier{};
+ imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ imageMemoryBarrier.image = cubemap.image;
+ imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
+ imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ imageMemoryBarrier.srcAccessMask = 0;
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ imageMemoryBarrier.subresourceRange = subresourceRange;
+ vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
+ }
+
+ for (uint32_t m = 0; m < numMips; m++) {
+ for (uint32_t f = 0; f < 6; f++) {
+ viewport.width = static_cast<float>(dim * std::pow(0.5f, m));
+ viewport.height = static_cast<float>(dim * std::pow(0.5f, m));
+ vkCmdSetViewport(cmdBuf, 0, 1, &viewport);
+
+ // Render scene from cube face's point of view
+ vkCmdBeginRenderPass(cmdBuf, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE);
+
+ // Pass parameters for current pass using a push constant block
+ switch (target) {
+ case IRRADIANCE:
+ pushBlockIrradiance.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f];
+ vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushBlockIrradiance), &pushBlockIrradiance);
+ break;
+ case PREFILTEREDENV:
+ pushBlockPrefilterEnv.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f];
+ pushBlockPrefilterEnv.roughness = (float)m / (float)(numMips - 1);
+ vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushBlockPrefilterEnv), &pushBlockPrefilterEnv);
+ break;
+ };
+
+ vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
+ vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, NULL);
+
+ skybox.buildCommandBuffer(cmdBuf, pipelinelayout);
+
+ vkCmdEndRenderPass(cmdBuf);
+
+ {
+ VkImageMemoryBarrier imageMemoryBarrier{};
+ imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ imageMemoryBarrier.image = offscreen.image;
+ imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+ vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
+ }
+
+ // Copy region for transfer from framebuffer to cube face
+ VkImageCopy copyRegion{};
+
+ copyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copyRegion.srcSubresource.baseArrayLayer = 0;
+ copyRegion.srcSubresource.mipLevel = 0;
+ copyRegion.srcSubresource.layerCount = 1;
+ copyRegion.srcOffset = { 0, 0, 0 };
+
+ copyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
+ copyRegion.dstSubresource.baseArrayLayer = f;
+ copyRegion.dstSubresource.mipLevel = m;
+ copyRegion.dstSubresource.layerCount = 1;
+ copyRegion.dstOffset = { 0, 0, 0 };
+
+ copyRegion.extent.width = static_cast<uint32_t>(viewport.width);
+ copyRegion.extent.height = static_cast<uint32_t>(viewport.height);
+ copyRegion.extent.depth = 1;
+
+ vkCmdCopyImage(
+ cmdBuf,
+ offscreen.image,
+ VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
+ cubemap.image,
+ VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
+ 1,
+ &copyRegion);
+
+ {
+ VkImageMemoryBarrier imageMemoryBarrier{};
+ imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ imageMemoryBarrier.image = offscreen.image;
+ imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
+ imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ imageMemoryBarrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 };
+ vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
+ }
+
+ }
+ }
+
+ {
+ VkImageMemoryBarrier imageMemoryBarrier{};
+ imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
+ imageMemoryBarrier.image = cubemap.image;
+ imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
+ imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
+ imageMemoryBarrier.dstAccessMask = VK_ACCESS_HOST_WRITE_BIT | VK_ACCESS_TRANSFER_WRITE_BIT;
+ imageMemoryBarrier.subresourceRange = subresourceRange;
+ vkCmdPipelineBarrier(cmdBuf, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, 0, nullptr, 0, nullptr, 1, &imageMemoryBarrier);
+ }
+
+ device->flushCommandBuffer(cmdBuf, device->queue);
+
+ vkDestroyRenderPass(device->dev, renderpass, nullptr);
+ vkDestroyFramebuffer(device->dev, offscreen.framebuffer, nullptr);
+ vkFreeMemory(device->dev, offscreen.memory, nullptr);
+ vkDestroyImageView(device->dev, offscreen.view, nullptr);
+ vkDestroyImage(device->dev, offscreen.image, nullptr);
+ vkDestroyDescriptorPool(device->dev, descriptorpool, nullptr);
+ vkDestroyDescriptorSetLayout(device->dev, descriptorsetlayout, nullptr);
+ vkDestroyPipeline(device->dev, pipeline, nullptr);
+ vkDestroyPipelineLayout(device->dev, pipelinelayout, nullptr);
+
+ cubemap.descriptor.imageView = cubemap.view;
+ cubemap.descriptor.sampler = cubemap.sampler;
+ cubemap.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
+ cubemap.device = device;
+
+ switch (target) {
+ case IRRADIANCE:
+ textures.irradianceCube = cubemap;
+ break;
+ case PREFILTEREDENV:
+ textures.prefilteredCube = cubemap;
+ //lightingParams.prefilteredCubeMipLevels = numMips;
+ break;
+ };
+
+ auto tEnd = std::chrono::high_resolution_clock::now();
+ auto tDiff = std::chrono::duration<double, std::milli>(tEnd - tStart).count();
+ std::cout << "Generating cube map with " << numMips << " mip levels took " << tDiff << " ms" << std::endl;
+ }
+}