#ifndef TINYGLTF_IMPLEMENTATION #define TINYGLTF_IMPLEMENTATION #endif #ifndef STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #endif #ifndef TINYGLTF_NO_STB_IMAGE_WRITE #define TINYGLTF_NO_STB_IMAGE_WRITE #endif #include "render.h" //#include "VulkanUtils.hpp" //#include "assetLoader.h" PlumageRender::PlumageRender() { title = "plumage render"; } void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode) { if (node->mesh) { // Render mesh primitives for (glTFModel::Primitive* primitive : node->mesh->primitives) { if (primitive->material.alphaMode == alphaMode) { VkPipeline pipeline = VK_NULL_HANDLE; switch (alphaMode) { case glTFModel::Material::ALPHAMODE_OPAQUE: case glTFModel::Material::ALPHAMODE_MASK: pipeline = primitive->material.doubleSided ? pipelines.pbrDoubleSided : pipelines.pbr; break; case glTFModel::Material::ALPHAMODE_BLEND: pipeline = pipelines.pbrAlphaBlend; break; } if (pipeline != boundPipeline) { vkCmdBindPipeline(commandBuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); boundPipeline = pipeline; } const std::vector descriptorsets = { descriptorSets[cbIndex].scene, primitive->material.descriptorSet, node->mesh->uniformBuffer.descriptorSet, }; vkCmdBindDescriptorSets(commandBuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast(descriptorsets.size()), descriptorsets.data(), 0, NULL); // Pass material parameters as push constants PushConstBlockMaterial pushConstBlockMaterial{}; pushConstBlockMaterial.emissiveFactor = primitive->material.emissiveFactor; // To save push constant space, availabilty and texture coordiante set are combined // -1 = texture not used for this material, >= 0 texture used and index of texture coordinate set pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1; pushConstBlockMaterial.normalTextureSet = primitive->material.normalTexture != nullptr ? primitive->material.texCoordSets.normal : -1; pushConstBlockMaterial.occlusionTextureSet = primitive->material.occlusionTexture != nullptr ? primitive->material.texCoordSets.occlusion : -1; pushConstBlockMaterial.emissiveTextureSet = primitive->material.emissiveTexture != nullptr ? primitive->material.texCoordSets.emissive : -1; pushConstBlockMaterial.alphaMask = static_cast(primitive->material.alphaMode == glTFModel::Material::ALPHAMODE_MASK); pushConstBlockMaterial.alphaMaskCutoff = primitive->material.alphaCutoff; // TODO: glTF specs states that metallic roughness should be preferred, even if specular glosiness is present if (primitive->material.pbrWorkflows.metallicRoughness) { // Metallic roughness workflow pushConstBlockMaterial.workflow = static_cast(PBR_WORKFLOW_METALLIC_ROUGHNESS); pushConstBlockMaterial.baseColorFactor = primitive->material.baseColorFactor; pushConstBlockMaterial.metallicFactor = primitive->material.metallicFactor; pushConstBlockMaterial.roughnessFactor = primitive->material.roughnessFactor; pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.metallicRoughnessTexture != nullptr ? primitive->material.texCoordSets.metallicRoughness : -1; pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1; } if (primitive->material.pbrWorkflows.specularGlossiness) { // Specular glossiness workflow pushConstBlockMaterial.workflow = static_cast(PBR_WORKFLOW_SPECULAR_GLOSINESS); pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.extension.specularGlossinessTexture != nullptr ? primitive->material.texCoordSets.specularGlossiness : -1; pushConstBlockMaterial.colorTextureSet = primitive->material.extension.diffuseTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1; pushConstBlockMaterial.diffuseFactor = primitive->material.extension.diffuseFactor; pushConstBlockMaterial.specularFactor = glm::vec4(primitive->material.extension.specularFactor, 1.0f); } vkCmdPushConstants(commandBuffers[cbIndex], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlockMaterial), &pushConstBlockMaterial); if (primitive->hasIndices) { vkCmdDrawIndexed(commandBuffers[cbIndex], primitive->indexCount, 1, primitive->firstIndex, 0, 0); } else { vkCmdDraw(commandBuffers[cbIndex], primitive->vertexCount, 1, 0, 0); } } } }; for (auto child : node->children) { renderNode(child, cbIndex, alphaMode); } } void PlumageRender::buildCommandBuffers() { VkCommandBufferBeginInfo cmdBufferBeginInfo{}; cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; VkClearValue clearValues[3]; if (settings.multiSampling) { clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; clearValues[1].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; clearValues[2].depthStencil = { 1.0f, 0 }; } else { clearValues[0].color = { { 0.0f, 0.0f, 0.0f, 1.0f } }; clearValues[1].depthStencil = { 1.0f, 0 }; } VkRenderPassBeginInfo renderPassBeginInfo{}; renderPassBeginInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; renderPassBeginInfo.renderPass = renderPass; renderPassBeginInfo.renderArea.offset.x = 0; renderPassBeginInfo.renderArea.offset.y = 0; renderPassBeginInfo.renderArea.extent.width = width; renderPassBeginInfo.renderArea.extent.height = height; renderPassBeginInfo.clearValueCount = settings.multiSampling ? 3 : 2; renderPassBeginInfo.pClearValues = clearValues; for (uint32_t i = 0; i < commandBuffers.size(); ++i) { renderPassBeginInfo.framebuffer = frameBuffers[i]; VkCommandBuffer currentCB = commandBuffers[i]; VK_CHECK_RESULT(vkBeginCommandBuffer(currentCB, &cmdBufferBeginInfo)); vkCmdBeginRenderPass(currentCB, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE); VkViewport viewport{}; viewport.width = (float)width; viewport.height = (float)height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(currentCB, 0, 1, &viewport); VkRect2D scissor{}; scissor.extent = { width, height }; vkCmdSetScissor(currentCB, 0, 1, &scissor); VkDeviceSize offsets[1] = { 0 }; if (displayBackground) { vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i].skybox, 0, nullptr); vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox); models.skybox.draw(currentCB); } glTFModel::Model& model = models.scene; vkCmdBindVertexBuffers(currentCB, 0, 1, &model.vertices.buffer, offsets); if (model.indices.buffer != VK_NULL_HANDLE) { vkCmdBindIndexBuffer(currentCB, model.indices.buffer, 0, VK_INDEX_TYPE_UINT32); } boundPipeline = VK_NULL_HANDLE; // Opaque primitives first for (auto node : model.nodes) { renderNode(node, i, glTFModel::Material::ALPHAMODE_OPAQUE); } // Alpha masked primitives for (auto node : model.nodes) { renderNode(node, i, glTFModel::Material::ALPHAMODE_MASK); } // Transparent primitives // TODO: Correct depth sorting for (auto node : model.nodes) { renderNode(node, i, glTFModel::Material::ALPHAMODE_BLEND); } // User interface gui->draw(currentCB); vkCmdEndRenderPass(currentCB); VK_CHECK_RESULT(vkEndCommandBuffer(currentCB)); } } void PlumageRender::loadScene(std::string filename) { std::cout << "Loading scene from " << filename << std::endl; models.scene.destroy(device); animationIndex = 0; animationTimer = 0.0f; auto tStart = std::chrono::high_resolution_clock::now(); models.scene.loadFromFile(filename, vulkanDevice, queue); auto tFileLoad = std::chrono::duration(std::chrono::high_resolution_clock::now() - tStart).count(); std::cout << "Loading took " << tFileLoad << " ms" << std::endl; camera.setPosition({ 0.0f, 0.0f, -1.0f }); camera.setRotation({ 0.0f, 0.0f, 0.0f }); } void PlumageRender::loadEnvironment(std::string filename) { std::cout << "Loading environment from " << filename << std::endl; if (textures.environmentCube.image) { textures.environmentCube.destroy(); textures.irradianceCube.destroy(); textures.prefilteredCube.destroy(); } textures.environmentCube.loadFromFile(filename, VK_FORMAT_R16G16B16A16_SFLOAT, vulkanDevice, queue); generateCubemaps(); } void PlumageRender::loadAssets() { const std::string assetpath = getAssetPath(); if (_access(assetpath.c_str(),0) != 0) { std::string msg = "Could not locate asset path in \"" + assetpath + "\".\nMake sure binary is run from correct relative directory!"; std::cerr << msg << std::endl; system("pause"); //exit(-1); } else { std::string msg = "asset path get " + assetpath; std::cout << msg << std::endl; } readDirectory(assetpath + "environments", "*.ktx", environments, false); textures.empty.loadFromFile(PlumageRender::filePath.emptyEnvmapFilePath, VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue); std::string sceneFile = filePath.glTFModelFilePath; std::string envMapFile = filePath.envMapFilePath; for (size_t i = 0; i < args.size(); i++) { if ((std::string(args[i]).find(".gltf") != std::string::npos) || (std::string(args[i]).find(".glb") != std::string::npos)) { std::ifstream file(args[i]); if (file.good()) { sceneFile = args[i]; } else { std::cout << "could not load \"" << args[i] << "\"" << std::endl; } } if (std::string(args[i]).find(".ktx") != std::string::npos) { std::ifstream file(args[i]); if (file.good()) { envMapFile = args[i]; } else { std::cout << "could not load \"" << args[i] << "\"" << std::endl; } } } loadScene(sceneFile.c_str()); models.skybox.loadFromFile(PlumageRender::filePath.skyboxModleFilePath, vulkanDevice, queue); loadEnvironment(envMapFile.c_str()); } void PlumageRender::setupNodeDescriptorSet(glTFModel::Node* node) { /* This sample uses separate descriptor sets (and layouts) for the matrices and materials (textures) */ if (node->mesh) { VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.node; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &node->mesh->uniformBuffer.descriptorSet)); VkWriteDescriptorSet writeDescriptorSet{}; writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSet.descriptorCount = 1; writeDescriptorSet.dstSet = node->mesh->uniformBuffer.descriptorSet; writeDescriptorSet.dstBinding = 0; writeDescriptorSet.pBufferInfo = &node->mesh->uniformBuffer.descriptor; vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr); } for (auto& child : node->children) { setupNodeDescriptorSet(child); } } void PlumageRender::setupDescriptors() { /* Descriptor Pool */ uint32_t imageSamplerCount = 0; uint32_t materialCount = 0; uint32_t meshCount = 0; // Environment samplers (radiance, irradiance, brdf lut) imageSamplerCount += 3; std::vector modellist = { &models.skybox, &models.scene }; for (auto& model : modellist) { for (auto& material : model->materials) { imageSamplerCount += 5; materialCount++; } for (auto node : model->linearNodes) { if (node->mesh) { meshCount++; } } } std::vector poolSizes = { { VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, (4 + meshCount) * swapChain.imageCount }, { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, imageSamplerCount * swapChain.imageCount } }; VkDescriptorPoolCreateInfo descriptorPoolCI{}; descriptorPoolCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; descriptorPoolCI.poolSizeCount = 2; descriptorPoolCI.pPoolSizes = poolSizes.data(); descriptorPoolCI.maxSets = (2 + materialCount + meshCount) * swapChain.imageCount; VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool)); /* Descriptor sets */ // Scene (matrices and environment maps) { std::vector setLayoutBindings = { { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 4, 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 = setLayoutBindings.data(); descriptorSetLayoutCI.bindingCount = static_cast(setLayoutBindings.size()); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.scene)); for (auto i = 0; i < descriptorSets.size(); i++) { VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].scene)); std::array writeDescriptorSets{}; writeDescriptorSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSets[0].descriptorCount = 1; writeDescriptorSets[0].dstSet = descriptorSets[i].scene; writeDescriptorSets[0].dstBinding = 0; writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].scene.descriptor; writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSets[1].descriptorCount = 1; writeDescriptorSets[1].dstSet = descriptorSets[i].scene; writeDescriptorSets[1].dstBinding = 1; writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor; writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[2].descriptorCount = 1; writeDescriptorSets[2].dstSet = descriptorSets[i].scene; writeDescriptorSets[2].dstBinding = 2; writeDescriptorSets[2].pImageInfo = &textures.irradianceCube.descriptor; writeDescriptorSets[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[3].descriptorCount = 1; writeDescriptorSets[3].dstSet = descriptorSets[i].scene; writeDescriptorSets[3].dstBinding = 3; writeDescriptorSets[3].pImageInfo = &textures.prefilteredCube.descriptor; writeDescriptorSets[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[4].descriptorCount = 1; writeDescriptorSets[4].dstSet = descriptorSets[i].scene; writeDescriptorSets[4].dstBinding = 4; writeDescriptorSets[4].pImageInfo = &textures.lutBrdf.descriptor; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); } } // Material (samplers) { std::vector setLayoutBindings = { { 0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr }, { 4, 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 = setLayoutBindings.data(); descriptorSetLayoutCI.bindingCount = static_cast(setLayoutBindings.size()); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.material)); // Per-Material descriptor sets for (auto& material : models.scene.materials) { VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.material; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &material.descriptorSet)); std::vector imageDescriptors = { textures.empty.descriptor, textures.empty.descriptor, material.normalTexture ? material.normalTexture->descriptor : textures.empty.descriptor, material.occlusionTexture ? material.occlusionTexture->descriptor : textures.empty.descriptor, material.emissiveTexture ? material.emissiveTexture->descriptor : textures.empty.descriptor }; if (material.pbrWorkflows.metallicRoughness) { if (material.baseColorTexture) { imageDescriptors[0] = material.baseColorTexture->descriptor; } if (material.metallicRoughnessTexture) { imageDescriptors[1] = material.metallicRoughnessTexture->descriptor; } } if (material.pbrWorkflows.specularGlossiness) { if (material.extension.diffuseTexture) { imageDescriptors[0] = material.extension.diffuseTexture->descriptor; } if (material.extension.specularGlossinessTexture) { imageDescriptors[1] = material.extension.specularGlossinessTexture->descriptor; } } std::array writeDescriptorSets{}; for (size_t i = 0; i < imageDescriptors.size(); i++) { writeDescriptorSets[i].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[i].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[i].descriptorCount = 1; writeDescriptorSets[i].dstSet = material.descriptorSet; writeDescriptorSets[i].dstBinding = static_cast(i); writeDescriptorSets[i].pImageInfo = &imageDescriptors[i]; } vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL); } // Model node (matrices) { std::vector setLayoutBindings = { { 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr }, }; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; descriptorSetLayoutCI.pBindings = setLayoutBindings.data(); descriptorSetLayoutCI.bindingCount = static_cast(setLayoutBindings.size()); VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &descriptorSetLayouts.node)); // Per-Node descriptor set for (auto& node : models.scene.nodes) { setupNodeDescriptorSet(node); } } } // Skybox (fixed set) for (auto i = 0; i < uniformBuffers.size(); i++) { VkDescriptorSetAllocateInfo descriptorSetAllocInfo{}; descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; descriptorSetAllocInfo.descriptorPool = descriptorPool; descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene; descriptorSetAllocInfo.descriptorSetCount = 1; VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].skybox)); std::array writeDescriptorSets{}; writeDescriptorSets[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSets[0].descriptorCount = 1; writeDescriptorSets[0].dstSet = descriptorSets[i].skybox; writeDescriptorSets[0].dstBinding = 0; writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].skybox.descriptor; writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; writeDescriptorSets[1].descriptorCount = 1; writeDescriptorSets[1].dstSet = descriptorSets[i].skybox; writeDescriptorSets[1].dstBinding = 1; writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor; writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; writeDescriptorSets[2].descriptorCount = 1; writeDescriptorSets[2].dstSet = descriptorSets[i].skybox; writeDescriptorSets[2].dstBinding = 2; writeDescriptorSets[2].pImageInfo = &textures.prefilteredCube.descriptor; vkUpdateDescriptorSets(device, static_cast(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr); } } void PlumageRender::preparePipelines() { 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_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{}; 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; if (settings.multiSampling) { multisampleStateCI.rasterizationSamples = settings.sampleCount; } std::vector 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(dynamicStateEnables.size()); // Pipeline layout const std::vector setLayouts = { descriptorSetLayouts.scene, descriptorSetLayouts.material, descriptorSetLayouts.node }; VkPipelineLayoutCreateInfo pipelineLayoutCI{}; pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipelineLayoutCI.setLayoutCount = static_cast(setLayouts.size()); pipelineLayoutCI.pSetLayouts = setLayouts.data(); VkPushConstantRange pushConstantRange{}; pushConstantRange.size = sizeof(PushConstBlockMaterial); pushConstantRange.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; pipelineLayoutCI.pushConstantRangeCount = 1; pipelineLayoutCI.pPushConstantRanges = &pushConstantRange; VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCI, nullptr, &pipelineLayout)); // Vertex bindings an attributes VkVertexInputBindingDescription vertexInputBinding = { 0, sizeof(glTFModel::Model::Vertex), VK_VERTEX_INPUT_RATE_VERTEX }; std::vector 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 }, { 3, 0, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 8 }, { 4, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 10 }, { 5, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 14 }, { 6, 0, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float) * 18 } }; VkPipelineVertexInputStateCreateInfo vertexInputStateCI{}; vertexInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertexInputStateCI.vertexBindingDescriptionCount = 1; vertexInputStateCI.pVertexBindingDescriptions = &vertexInputBinding; vertexInputStateCI.vertexAttributeDescriptionCount = static_cast(vertexInputAttributes.size()); vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data(); // Pipelines std::array 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 = static_cast(shaderStages.size()); pipelineCI.pStages = shaderStages.data(); if (settings.multiSampling) { multisampleStateCI.rasterizationSamples = settings.sampleCount; } // Skybox pipeline (background cube) shaderStages = { loadShader(device,PlumageRender::filePath.skyboxVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT), loadShader(device,PlumageRender::filePath.skyboxFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT) }; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.skybox)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device, shaderStage.module, nullptr); } // PBR pipeline shaderStages = { loadShader(device,PlumageRender::filePath.pbrVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT), loadShader(device,PlumageRender::filePath.pbrFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT) }; depthStencilStateCI.depthWriteEnable = VK_TRUE; depthStencilStateCI.depthTestEnable = VK_TRUE; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbr)); rasterizationStateCI.cullMode = VK_CULL_MODE_NONE; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbrDoubleSided)); 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, pipelineCache, 1, &pipelineCI, nullptr, &pipelines.pbrAlphaBlend)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device, shaderStage.module, nullptr); } //Create Tone Mapping render pipeline //CreateToneMappingPipeline(); } // generate two cube maps // irradiance cube map // prefileter environment cube map void PlumageRender::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(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, &imageCI, nullptr, &cubemap.image)); VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device, cubemap.image, &memReqs); VkMemoryAllocateInfo memAllocInfo{}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &cubemap.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, 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, &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(numMips); samplerCI.maxAnisotropy = 1.0f; samplerCI.borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE; VK_CHECK_RESULT(vkCreateSampler(device, &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 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, &renderPassCI, nullptr, &renderpass)); // 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, &imageCI, nullptr, &offscreen.image)); VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device, offscreen.image, &memReqs); VkMemoryAllocateInfo memAllocInfo{}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &offscreen.memory)); VK_CHECK_RESULT(vkBindImageMemory(device, 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, &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, &framebufferCI, nullptr, &offscreen.framebuffer)); VkCommandBuffer layoutCmd = vulkanDevice->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); vulkanDevice->flushCommandBuffer(layoutCmd, 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, &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, &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, &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, 1, &writeDescriptorSet, 0, nullptr); // 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(IrradiancePushBlock); break; case PREFILTEREDENV: pushConstantRange.size = sizeof(PrefilterPushBlock); 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, &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 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(dynamicStateEnables.size()); // Vertex input state VkVertexInputBindingDescription vertexInputBinding = { 0, sizeof(glTFModel::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 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, PlumageRender::filePath.filterVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT); switch (target) { case IRRADIANCE: shaderStages[1] = loadShader(device, PlumageRender::filePath.irradianceFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT); break; case PREFILTEREDENV: shaderStages[1] = loadShader(device, PlumageRender::filePath.prefilterEnvmapFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT); break; }; VkPipeline pipeline; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device, shaderStage.module, nullptr); } // Render cubemap VkClearValue clearValues[1]; clearValues[0].color = { { 0.0f, 0.0f, 0.2f, 0.0f } }; VkRenderPassBeginInfo renderPassBeginInfo{}; renderPassBeginInfo.sType = 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 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 = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, false); VkViewport viewport{}; viewport.width = (float)dim; viewport.height = (float)dim; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkRect2D scissor{}; scissor.extent.width = dim; scissor.extent.height = dim; VkImageSubresourceRange subresourceRange{}; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = numMips; subresourceRange.layerCount = 6; // Change image layout for all cubemap faces to transfer destination { vulkanDevice->beginCommandBuffer(cmdBuf); 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); vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); } for (uint32_t m = 0; m < numMips; m++) { for (uint32_t f = 0; f < 6; f++) { vulkanDevice->beginCommandBuffer(cmdBuf); viewport.width = static_cast(dim * std::pow(0.5f, m)); viewport.height = static_cast(dim * std::pow(0.5f, m)); vkCmdSetViewport(cmdBuf, 0, 1, &viewport); vkCmdSetScissor(cmdBuf, 0, 1, &scissor); // 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: irradiancePushBlock.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(IrradiancePushBlock), &irradiancePushBlock); break; case PREFILTEREDENV: prefilterPushBlock.mvp = glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]; prefilterPushBlock.roughness = (float)m / (float)(numMips - 1); vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &prefilterPushBlock); break; }; vkCmdBindPipeline(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); vkCmdBindDescriptorSets(cmdBuf, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelinelayout, 0, 1, &descriptorset, 0, NULL); VkDeviceSize offsets[1] = { 0 }; models.skybox.draw(cmdBuf); vkCmdEndRenderPass(cmdBuf); VkImageSubresourceRange subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; subresourceRange.baseMipLevel = 0; subresourceRange.levelCount = numMips; subresourceRange.layerCount = 6; { 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(viewport.width); copyRegion.extent.height = static_cast(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, ©Region); { 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); } vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); } } { vulkanDevice->beginCommandBuffer(cmdBuf); 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); vulkanDevice->flushCommandBuffer(cmdBuf, queue, false); } vkDestroyRenderPass(device, renderpass, nullptr); vkDestroyFramebuffer(device, offscreen.framebuffer, nullptr); vkFreeMemory(device, offscreen.memory, nullptr); vkDestroyImageView(device, offscreen.view, nullptr); vkDestroyImage(device, offscreen.image, nullptr); vkDestroyDescriptorPool(device, descriptorpool, nullptr); vkDestroyDescriptorSetLayout(device, descriptorsetlayout, nullptr); vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, pipelinelayout, nullptr); cubemap.descriptor.imageView = cubemap.view; cubemap.descriptor.sampler = cubemap.sampler; cubemap.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; cubemap.device = vulkanDevice; switch (target) { case IRRADIANCE: textures.irradianceCube = cubemap; break; case PREFILTEREDENV: textures.prefilteredCube = cubemap; shaderData.prefilteredCubeMipLevels = static_cast(numMips); break; }; auto tEnd = std::chrono::high_resolution_clock::now(); auto tDiff = std::chrono::duration(tEnd - tStart).count(); std::cout << "Generating cube map with " << numMips << " mip levels took " << tDiff << " ms" << std::endl; } } // generate BRDF integration map for roughness/NdotV void PlumageRender::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, &imageCI, nullptr, &textures.lutBrdf.image)); VkMemoryRequirements memReqs; vkGetImageMemoryRequirements(device, textures.lutBrdf.image, &memReqs); VkMemoryAllocateInfo memAllocInfo{}; memAllocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; memAllocInfo.allocationSize = memReqs.size; memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &textures.lutBrdf.deviceMemory)); VK_CHECK_RESULT(vkBindImageMemory(device, 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, &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, &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 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, &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, &framebufferCI, nullptr, &framebuffer)); // Desriptors VkDescriptorSetLayout descriptorsetlayout; VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI{}; descriptorSetLayoutCI.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &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, &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 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(dynamicStateEnables.size()); VkPipelineVertexInputStateCreateInfo emptyInputStateCI{}; emptyInputStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; std::array 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,PlumageRender::filePath.brdfVertShaderPath, VK_SHADER_STAGE_VERTEX_BIT), loadShader(device,PlumageRender::filePath.brdfFragShaderPath, VK_SHADER_STAGE_FRAGMENT_BIT) }; VkPipeline pipeline; VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pipeline)); for (auto shaderStage : shaderStages) { vkDestroyShaderModule(device, 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 = vulkanDevice->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 = dim; scissor.extent.height = dim; 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); vulkanDevice->flushCommandBuffer(cmdBuf, queue); vkQueueWaitIdle(queue); vkDestroyPipeline(device, pipeline, nullptr); vkDestroyPipelineLayout(device, pipelinelayout, nullptr); vkDestroyRenderPass(device, renderpass, nullptr); vkDestroyFramebuffer(device, framebuffer, nullptr); vkDestroyDescriptorSetLayout(device, 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 = vulkanDevice; auto tEnd = std::chrono::high_resolution_clock::now(); auto tDiff = std::chrono::duration(tEnd - tStart).count(); std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl; } // Prepare and initialize uniform buffer containing shader uniforms void PlumageRender::prepareUniformBuffers() { for (auto& uniformBuffer : uniformBuffers) { uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene)); uniformBuffer.skybox.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataSkybox)); uniformBuffer.params.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderData)); } updateUniformBuffers(); } void PlumageRender::updateUniformBuffers() { // Scene shaderDataScene.projection = camera.matrices.perspective; shaderDataScene.view = camera.matrices.view; // Center and scale model float scale = (1.0f / std::max(models.scene.aabb[0][0], std::max(models.scene.aabb[1][1], models.scene.aabb[2][2]))) * 0.5f; glm::vec3 translate = -glm::vec3(models.scene.aabb[3][0], models.scene.aabb[3][1], models.scene.aabb[3][2]); translate += -0.5f * glm::vec3(models.scene.aabb[0][0], models.scene.aabb[1][1], models.scene.aabb[2][2]); shaderDataScene.model = glm::mat4(1.0f); shaderDataScene.model[0][0] = scale; shaderDataScene.model[1][1] = scale; shaderDataScene.model[2][2] = scale; shaderDataScene.model = glm::translate(shaderDataScene.model, translate); shaderDataScene.camPos = glm::vec3( -camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)), -camera.position.z * sin(glm::radians(camera.rotation.x)), camera.position.z * cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)) ); // Skybox shaderDataSkybox.projection = camera.matrices.perspective; shaderDataSkybox.view = camera.matrices.view; shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view)); } void PlumageRender::updateShaderData() { shaderData.lightDir = glm::vec4( sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), sin(glm::radians(lightSource.rotation.y)), cos(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)), 0.0f); } void PlumageRender::windowResized() { buildCommandBuffers(); vkDeviceWaitIdle(device); updateUniformBuffers(); //update UI updateUIOverlay(); } void PlumageRender::prepare() { VulkanExampleBase::prepare(); camera.type = Camera::CameraType::lookat; camera.setPerspective(45.0f, (float)width / (float)height, 0.1f, 256.0f); camera.rotationSpeed = 0.25f; camera.movementSpeed = 0.1f; camera.setPosition({ 0.0f, 0.0f, -1.0f }); camera.setRotation({ 0.0f, 0.0f, 0.0f }); waitFences.resize(renderAhead); presentCompleteSemaphores.resize(renderAhead); renderCompleteSemaphores.resize(renderAhead); commandBuffers.resize(swapChain.imageCount); uniformBuffers.resize(swapChain.imageCount); descriptorSets.resize(swapChain.imageCount); // Command buffer execution fences for (auto& waitFence : waitFences) { VkFenceCreateInfo fenceCI{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT }; VK_CHECK_RESULT(vkCreateFence(device, &fenceCI, nullptr, &waitFence)); } // Queue ordering semaphores for (auto& semaphore : presentCompleteSemaphores) { VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 }; VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore)); } for (auto& semaphore : renderCompleteSemaphores) { VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 }; VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore)); } // Command buffers { VkCommandBufferAllocateInfo cmdBufAllocateInfo{}; cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdBufAllocateInfo.commandPool = cmdPool; cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cmdBufAllocateInfo.commandBufferCount = static_cast(commandBuffers.size()); VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, commandBuffers.data())); } loadAssets(); generateBRDFLUT(); generateCubemaps(); prepareUniformBuffers(); setupDescriptors(); preparePipelines(); gui = new UI(vulkanDevice, renderPass, queue, pipelineCache, settings.sampleCount); updateUIOverlay(); buildCommandBuffers(); prepared = true; } void PlumageRender::render() { if (!prepared) { return; } updateUIOverlay(); VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX)); VK_CHECK_RESULT(vkResetFences(device, 1, &waitFences[frameIndex])); VkResult acquire = swapChain.acquireNextImage(presentCompleteSemaphores[frameIndex], ¤tBuffer); if ((acquire == VK_ERROR_OUT_OF_DATE_KHR) || (acquire == VK_SUBOPTIMAL_KHR)) { windowResize(); } else { VK_CHECK_RESULT(acquire); } // Update UBOs updateUniformBuffers(); UniformBufferSet currentUB = uniformBuffers[currentBuffer]; memcpy(currentUB.scene.mapped, &shaderDataScene, sizeof(shaderDataScene)); memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData)); memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox)); const VkPipelineStageFlags waitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submitInfo{}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pWaitDstStageMask = &waitDstStageMask; submitInfo.pWaitSemaphores = &presentCompleteSemaphores[frameIndex]; submitInfo.waitSemaphoreCount = 1; submitInfo.pSignalSemaphores = &renderCompleteSemaphores[frameIndex]; submitInfo.signalSemaphoreCount = 1; submitInfo.pCommandBuffers = &commandBuffers[currentBuffer]; submitInfo.commandBufferCount = 1; VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, waitFences[frameIndex])); VkResult present = swapChain.queuePresent(queue, currentBuffer, renderCompleteSemaphores[frameIndex]); if (!((present == VK_SUCCESS) || (present == VK_SUBOPTIMAL_KHR))) { if (present == VK_ERROR_OUT_OF_DATE_KHR) { windowResize(); return; } else { VK_CHECK_RESULT(present); } } frameIndex += 1; frameIndex %= renderAhead; if (!paused) { if (rotateModel) { modelrot.y += frameTimer * 35.0f; if (modelrot.y > 360.0f) { modelrot.y -= 360.0f; } } if ((animate) && (models.scene.animations.size() > 0)) { animationTimer += frameTimer; if (animationTimer > models.scene.animations[animationIndex].end) { animationTimer -= models.scene.animations[animationIndex].end; } models.scene.updateAnimation(animationIndex, animationTimer); } updateShaderData(); if (rotateModel) { updateUniformBuffers(); } } if (camera.updated) { updateUniformBuffers(); } } void PlumageRender::fileDropped(std::string filename) { vkDeviceWaitIdle(device); loadScene(filename); setupDescriptors(); buildCommandBuffers(); } void PlumageRender::updateUIOverlay() { ImGuiIO& io = ImGui::GetIO(); ImVec2 lastDisplaySize = io.DisplaySize; io.DisplaySize = ImVec2((float)width, (float)height); io.DeltaTime = frameTimer; io.MousePos = ImVec2(mousePos.x, mousePos.y); io.MouseDown[0] = mouseButtons.left; io.MouseDown[1] = mouseButtons.right; gui->pushConstBlock.scale = glm::vec2(2.0f / io.DisplaySize.x, 2.0f / io.DisplaySize.y); gui->pushConstBlock.translate = glm::vec2(-1.0f); bool updateShaderParams = false; bool updateCBs = false; float scale = 1.0f; ImGui::NewFrame(); ImGui::SetNextWindowPos(ImVec2(10, 10)); ImGui::SetNextWindowSize(ImVec2(200 * scale, (models.scene.animations.size() > 0 ? 440 : 360) * scale), ImGuiSetCond_Always); ImGui::Begin("Plumage Render", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); ImGui::PushItemWidth(100.0f * scale); gui->text("%.1d fps (%.2f ms)", lastFPS, (1000.0f / lastFPS)); // TO DO : language switch if (gui->header(chineseUI.model)) { if (gui->button(chineseUI.openNewModel)) { std::string filename = ""; char buffer[MAX_PATH]; OPENFILENAME ofn; ZeroMemory(&buffer, sizeof(buffer)); ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.lpstrFilter = "glTF files\0*.gltf;*.glb\0"; ofn.lpstrFile = buffer; ofn.nMaxFile = MAX_PATH; ofn.lpstrTitle = "Select a glTF file to load"; ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR; if (GetOpenFileNameA(&ofn)) { filename = buffer; } if (!filename.empty()) { vkDeviceWaitIdle(device); loadScene(filename); setupDescriptors(); updateCBs = true; } } if (gui->combo(chineseUI.environmentMap, selectedEnvironment, environments)) { vkDeviceWaitIdle(device); loadEnvironment(environments[selectedEnvironment]); setupDescriptors(); updateCBs = true; } } if (gui->header(chineseUI.environment)) { if (gui->checkbox("Background", &displayBackground)) { updateShaderParams = true; } if (gui->slider("Exposure", &shaderData.exposure, 0.1f, 10.0f)) { updateShaderParams = true; } if (gui->slider("Gamma", &shaderData.gamma, 0.1f, 4.0f)) { updateShaderParams = true; } if (gui->slider("IBL", &shaderData.scaleIBLAmbient, 0.0f, 1.0f)) { updateShaderParams = true; } } if (gui->header("Debug ")) { const std::vector debugNamesInputs = { "none", "Base color", "Normal", "Occlusion", "Emissive", "Metallic", "Roughness" }; if (gui->combo(chineseUI.debugInput, &debugViewInputs, debugNamesInputs)) { shaderData.debugViewInputs = static_cast(debugViewInputs); updateShaderParams = true; } const std::vector debugNamesEquation = { "none", "Diff (l,n)", "F (l,h)", "G (l,v,h)", "D (h)", "Specular" }; if (gui->combo(chineseUI.debugPBREquation, &debugViewEquation, debugNamesEquation)) { shaderData.debugViewEquation = static_cast(debugViewEquation); updateShaderParams = true; } } if (models.scene.animations.size() > 0) { if (gui->header(chineseUI.animation)) { gui->checkbox(chineseUI.pauseAnimation, &animate); std::vector animationNames; for (auto animation : models.scene.animations) { animationNames.push_back(animation.name); } gui->combo(chineseUI.animationSeq, &animationIndex, animationNames); } } ImGui::PopItemWidth(); ImGui::End(); ImGui::Render(); ImDrawData* imDrawData = ImGui::GetDrawData(); // Check if ui buffers need to be recreated if (imDrawData) { VkDeviceSize vertexBufferSize = imDrawData->TotalVtxCount * sizeof(ImDrawVert); VkDeviceSize indexBufferSize = imDrawData->TotalIdxCount * sizeof(ImDrawIdx); bool updateBuffers = (gui->vertexBuffer.buffer == VK_NULL_HANDLE) || (gui->vertexBuffer.count != imDrawData->TotalVtxCount) || (gui->indexBuffer.buffer == VK_NULL_HANDLE) || (gui->indexBuffer.count != imDrawData->TotalIdxCount); if (updateBuffers) { vkDeviceWaitIdle(device); if (gui->vertexBuffer.buffer) { gui->vertexBuffer.destroy(); } gui->vertexBuffer.create(vulkanDevice, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, vertexBufferSize); gui->vertexBuffer.count = imDrawData->TotalVtxCount; if (gui->indexBuffer.buffer) { gui->indexBuffer.destroy(); } gui->indexBuffer.create(vulkanDevice, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indexBufferSize); gui->indexBuffer.count = imDrawData->TotalIdxCount; } // Upload data ImDrawVert* vtxDst = (ImDrawVert*)gui->vertexBuffer.mapped; ImDrawIdx* idxDst = (ImDrawIdx*)gui->indexBuffer.mapped; for (int n = 0; n < imDrawData->CmdListsCount; n++) { const ImDrawList* cmd_list = imDrawData->CmdLists[n]; memcpy(vtxDst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); memcpy(idxDst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); vtxDst += cmd_list->VtxBuffer.Size; idxDst += cmd_list->IdxBuffer.Size; } gui->vertexBuffer.flush(); gui->indexBuffer.flush(); updateCBs = updateCBs || updateBuffers; } if (lastDisplaySize.x != io.DisplaySize.x || lastDisplaySize.y != io.DisplaySize.y) { updateCBs = true; } if (updateCBs) { vkDeviceWaitIdle(device); buildCommandBuffers(); vkDeviceWaitIdle(device); } if (updateShaderParams) { updateShaderData(); } } PlumageRender* plumageRender; // OS specific macros for the example main entry points #if defined(_WIN32) LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (plumageRender != NULL) { plumageRender->handleMessages(hWnd, uMsg, wParam, lParam); } return (DefWindowProc(hWnd, uMsg, wParam, lParam)); } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int) { for (int32_t i = 0; i < __argc; i++) { PlumageRender::args.push_back(__argv[i]); }; plumageRender = new PlumageRender(); plumageRender->initVulkan(); plumageRender->setupWindow(hInstance, WndProc); plumageRender->prepare(); plumageRender->renderLoop(); delete(plumageRender); return 0; } #endif