2470 lines
108 KiB
C++
2470 lines
108 KiB
C++
|
||
|
||
#ifndef TINYGLTF_IMPLEMENTATION
|
||
#define TINYGLTF_IMPLEMENTATION
|
||
|
||
#include "PushConstBlockMaterial.h"
|
||
#include "VulkanTexture.hpp"
|
||
#include "glTFModel.h"
|
||
#include "glm/gtc/matrix_transform.hpp"
|
||
#include "renderUniformBufferSet.h"
|
||
#include "vulkan/vulkan_core.h"
|
||
#include <cmath>
|
||
#include <cstddef>
|
||
|
||
#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:
|
||
if (primitive->material.doubleSided)
|
||
{
|
||
pipeline = m_pipelineList.getPbrDoubleSided();
|
||
}
|
||
else
|
||
{
|
||
pipeline = m_pipelineList.getPbr();
|
||
}
|
||
break;
|
||
case glTFModel::Material::ALPHAMODE_BLEND:
|
||
pipeline = m_pipelineList.getPbrAlphaBlend();
|
||
break;
|
||
}
|
||
|
||
if (pipeline != boundPipeline)
|
||
{
|
||
vkCmdBindPipeline(commandBuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||
boundPipeline = pipeline;
|
||
}
|
||
|
||
const std::vector<VkDescriptorSet> descriptorsets = {
|
||
descriptorSets[cbIndex].getScene(),
|
||
primitive->material.descriptorSet,
|
||
node->mesh->uniformBuffer.descriptorSet,
|
||
};
|
||
vkCmdBindDescriptorSets(commandBuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, static_cast<uint32_t>(descriptorsets.size()), descriptorsets.data(), 0, NULL);
|
||
|
||
// Pass material parameters as push constants
|
||
PushConstBlockMaterial m_pushConstBlockMaterial;
|
||
m_pushConstBlockMaterial.setEmissiveFactor(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
|
||
if (primitive->material.baseColorTexture != nullptr)
|
||
{
|
||
|
||
m_pushConstBlockMaterial.setColorTextureSet(primitive->material.texCoordSets.baseColor);
|
||
}
|
||
else
|
||
{
|
||
m_pushConstBlockMaterial.setColorTextureSet(-1);
|
||
}
|
||
|
||
if (primitive->material.normalTexture != nullptr)
|
||
{
|
||
m_pushConstBlockMaterial.setNormalTextureSet(primitive->material.texCoordSets.normal);
|
||
}
|
||
else
|
||
{
|
||
m_pushConstBlockMaterial.setNormalTextureSet(-1);
|
||
}
|
||
if (primitive->material.occlusionTexture != nullptr)
|
||
{
|
||
m_pushConstBlockMaterial.setOcclusionTextureSet(primitive->material.texCoordSets.occlusion);
|
||
}
|
||
else
|
||
{
|
||
m_pushConstBlockMaterial.setOcclusionTextureSet(-1);
|
||
}
|
||
|
||
if (primitive->material.emissiveTexture != nullptr)
|
||
{
|
||
m_pushConstBlockMaterial.setEmissiveTextureSet(primitive->material.texCoordSets.emissive);
|
||
}
|
||
else
|
||
{
|
||
m_pushConstBlockMaterial.setEmissiveTextureSet(-1);
|
||
}
|
||
if (primitive->material.alphaMode == glTFModel::Material::ALPHAMODE_MASK)
|
||
{
|
||
m_pushConstBlockMaterial.setAlphaMask(static_cast<float>(primitive->material.alphaMode));
|
||
}
|
||
m_pushConstBlockMaterial.setAlphaMaskCutoff(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
|
||
m_pushConstBlockMaterial.setWorkflow(static_cast<float>(PBR_WORKFLOW_METALLIC_ROUGHNESS));
|
||
m_pushConstBlockMaterial.setBaseColorFactor(primitive->material.baseColorFactor);
|
||
m_pushConstBlockMaterial.setMetallicFactor(primitive->material.metallicFactor);
|
||
m_pushConstBlockMaterial.setRoughnessFactor(primitive->material.roughnessFactor);
|
||
if (primitive->material.metallicRoughnessTexture != nullptr)
|
||
{
|
||
m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(primitive->material.texCoordSets.metallicRoughness);
|
||
}
|
||
else
|
||
{
|
||
m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(-1);
|
||
}
|
||
|
||
if (primitive->material.baseColorTexture != nullptr)
|
||
{
|
||
m_pushConstBlockMaterial.setColorTextureSet(primitive->material.texCoordSets.baseColor);
|
||
}
|
||
else
|
||
{
|
||
m_pushConstBlockMaterial.setColorTextureSet(-1);
|
||
}
|
||
}
|
||
|
||
if (primitive->material.pbrWorkflows.specularGlossiness)
|
||
{
|
||
|
||
// Specular glossiness workflow
|
||
m_pushConstBlockMaterial.setWorkflow(static_cast<float>(PBR_WORKFLOW_SPECULAR_GLOSINESS));
|
||
if (primitive->material.extension.specularGlossinessTexture != nullptr)
|
||
{
|
||
m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(primitive->material.texCoordSets.specularGlossiness);
|
||
}
|
||
else
|
||
{
|
||
m_pushConstBlockMaterial.setPhysicalDescriptorTextureSet(-1);
|
||
}
|
||
|
||
if (primitive->material.extension.diffuseTexture != nullptr)
|
||
{
|
||
m_pushConstBlockMaterial.setColorTextureSet(primitive->material.texCoordSets.baseColor);
|
||
}
|
||
else
|
||
{
|
||
m_pushConstBlockMaterial.setColorTextureSet(-1);
|
||
}
|
||
|
||
m_pushConstBlockMaterial.setDiffuseFactor(primitive->material.extension.diffuseFactor);
|
||
m_pushConstBlockMaterial.setSpecularFactor(glm::vec4(primitive->material.extension.specularFactor, 1.0f));
|
||
}
|
||
|
||
vkCmdPushConstants(commandBuffers[cbIndex], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlockMaterial), &m_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)
|
||
{
|
||
VkDescriptorSet skyboxDescriptorSet = descriptorSets[i].getSkybox();
|
||
vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &skyboxDescriptorSet, 0, nullptr);
|
||
vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pipelineList.getSkybox());
|
||
m_sceneModel.getSkyBox().draw(currentCB);
|
||
}
|
||
|
||
glTFModel::Model &model = m_sceneModel.getScene();
|
||
|
||
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;
|
||
m_sceneModel.destroyScene(device);
|
||
|
||
animationIndex = 0;
|
||
animationTimer = 0.0f;
|
||
auto tStart = std::chrono::high_resolution_clock::now();
|
||
m_sceneModel.getScene().loadFromFile(filename, vulkanDevice, queue);
|
||
auto tFileLoad = std::chrono::duration<double, std::milli>(std::chrono::high_resolution_clock::now() - tStart).count();
|
||
std::cout << "Loading took " << tFileLoad << " ms" << std::endl;
|
||
camera.setPosition({0.0f, 0.0f, -2.0f});
|
||
camera.setRotation({0.0f, 0.0f, 0.0f});
|
||
}
|
||
|
||
void PlumageRender::loadEnvironment(std::string filename)
|
||
{
|
||
std::cout << "Loading environment from " << filename << std::endl;
|
||
if (m_sceneTextures.getEnvironmentCube().image)
|
||
{
|
||
m_sceneTextures.destroyEnvironmentCube();
|
||
m_sceneTextures.destroyIrradianceCube();
|
||
m_sceneTextures.destroyPrefilteredCube();
|
||
}
|
||
m_sceneTextures.getEnvironmentCube().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 running 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);
|
||
|
||
m_sceneTextures.getEmpty().loadFromFile(PlumageRender::m_configFilePath.getEmptyEnvmapFilePath(), VK_FORMAT_R8G8B8A8_UNORM, vulkanDevice, queue);
|
||
|
||
std::string sceneFile = m_configFilePath.getGlTFModelFilePath();
|
||
std::string envMapFile = m_configFilePath.getEnvMapFilePath();
|
||
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());
|
||
m_sceneModel.getSkyBox().loadFromFile(PlumageRender::m_configFilePath.getSkyboxModleFilePath(), 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;
|
||
VkDescriptorSetLayout nodeDescriptorSetLayout = m_descriptorSetLayoutList.getNode();
|
||
descriptorSetAllocInfo.pSetLayouts = &nodeDescriptorSetLayout;
|
||
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, brdflut)
|
||
imageSamplerCount += 3;
|
||
|
||
std::vector<glTFModel::Model *> modellist = {&m_sceneModel.getSkyBox(), &m_sceneModel.getScene()};
|
||
for (auto &model : modellist)
|
||
{
|
||
for (auto &material : model->materials)
|
||
{
|
||
imageSamplerCount += 5;
|
||
materialCount++;
|
||
}
|
||
for (auto node : model->linearNodes)
|
||
{
|
||
if (node->mesh)
|
||
{
|
||
meshCount++;
|
||
}
|
||
}
|
||
}
|
||
|
||
std::vector<VkDescriptorPoolSize> 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<VkDescriptorSetLayoutBinding> 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<uint32_t>(setLayoutBindings.size());
|
||
VkDescriptorSetLayout sceneDescriptorSetLayout = m_descriptorSetLayoutList.getScene();
|
||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &sceneDescriptorSetLayout));
|
||
|
||
vks::TextureCubeMap refIrradianceCube = m_sceneTextures.getIrradianceCube();
|
||
for (auto i = 0; i < descriptorSets.size(); i++)
|
||
{
|
||
|
||
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
||
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
||
VkDescriptorSetLayout sceneDescriptorSetLayout = m_descriptorSetLayoutList.getScene();
|
||
descriptorSetAllocInfo.pSetLayouts = &sceneDescriptorSetLayout;
|
||
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||
VkDescriptorSet sceneDescriptorSet = descriptorSets[i].getScene();
|
||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &sceneDescriptorSet));
|
||
|
||
std::array<VkWriteDescriptorSet, 5> 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].getScene();
|
||
writeDescriptorSets[0].dstBinding = 0;
|
||
writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].getScene().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].getScene();
|
||
writeDescriptorSets[1].dstBinding = 1;
|
||
writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].getParams().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].getScene();
|
||
writeDescriptorSets[2].dstBinding = 2;
|
||
writeDescriptorSets[2].pImageInfo = &refIrradianceCube.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].getScene();
|
||
writeDescriptorSets[3].dstBinding = 3;
|
||
writeDescriptorSets[3].pImageInfo = &refIrradianceCube.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].getScene();
|
||
writeDescriptorSets[4].dstBinding = 4;
|
||
writeDescriptorSets[4].pImageInfo = &refIrradianceCube.descriptor;
|
||
|
||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
|
||
}
|
||
}
|
||
|
||
// Material (samplers)
|
||
{
|
||
std::vector<VkDescriptorSetLayoutBinding> 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<uint32_t>(setLayoutBindings.size());
|
||
VkDescriptorSetLayout materialDescriptorSetLayout = m_descriptorSetLayoutList.getMaterial();
|
||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &materialDescriptorSetLayout));
|
||
|
||
// Per-Material descriptor sets
|
||
for (auto &material : m_sceneModel.getScene().materials)
|
||
{
|
||
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
||
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
||
VkDescriptorSetLayout materialDescriptorSetLayout = m_descriptorSetLayoutList.getMaterial();
|
||
descriptorSetAllocInfo.pSetLayouts = &materialDescriptorSetLayout;
|
||
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &material.descriptorSet));
|
||
VkDescriptorImageInfo emptyTexDescriptor = m_sceneTextures.getEmpty().descriptor;
|
||
std::vector<VkDescriptorImageInfo> imageDescriptors = {
|
||
emptyTexDescriptor,
|
||
emptyTexDescriptor,
|
||
material.normalTexture ? material.normalTexture->descriptor : emptyTexDescriptor,
|
||
material.occlusionTexture ? material.occlusionTexture->descriptor : emptyTexDescriptor,
|
||
material.emissiveTexture ? material.emissiveTexture->descriptor : emptyTexDescriptor};
|
||
|
||
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<VkWriteDescriptorSet, 5> 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<uint32_t>(i);
|
||
writeDescriptorSets[i].pImageInfo = &imageDescriptors[i];
|
||
}
|
||
|
||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
|
||
}
|
||
|
||
// Model node (matrices)
|
||
{
|
||
std::vector<VkDescriptorSetLayoutBinding> 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<uint32_t>(setLayoutBindings.size());
|
||
VkDescriptorSetLayout nodeDescriptorSetLayout = m_descriptorSetLayoutList.getNode();
|
||
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCI, nullptr, &nodeDescriptorSetLayout));
|
||
|
||
// Per-Node descriptor set
|
||
for (auto &node : m_sceneModel.getScene().nodes)
|
||
{
|
||
setupNodeDescriptorSet(node);
|
||
}
|
||
}
|
||
}
|
||
|
||
vks::TextureCubeMap refPrefilterCube = m_sceneTextures.getPrefilteredCube();
|
||
// 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;
|
||
VkDescriptorSetLayout sceneDescriptorSetLayouts = m_descriptorSetLayoutList.getScene();
|
||
descriptorSetAllocInfo.pSetLayouts = &sceneDescriptorSetLayouts;
|
||
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||
VkDescriptorSet skyboxDescriptorSet = descriptorSets[i].getSkybox();
|
||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &skyboxDescriptorSet));
|
||
|
||
std::array<VkWriteDescriptorSet, 3> 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].getSkybox();
|
||
writeDescriptorSets[0].dstBinding = 0;
|
||
writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].getSkybox().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].getSkybox();
|
||
writeDescriptorSets[1].dstBinding = 1;
|
||
writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].getParams().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].getSkybox();
|
||
writeDescriptorSets[2].dstBinding = 2;
|
||
writeDescriptorSets[2].pImageInfo = &refPrefilterCube.descriptor;
|
||
|
||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(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<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());
|
||
|
||
// Pipeline layout
|
||
const std::vector<VkDescriptorSetLayout> setLayouts = {
|
||
m_descriptorSetLayoutList.getScene(), m_descriptorSetLayoutList.getMaterial(), m_descriptorSetLayoutList.getNode()};
|
||
VkPipelineLayoutCreateInfo pipelineLayoutCI{};
|
||
pipelineLayoutCI.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||
pipelineLayoutCI.setLayoutCount = static_cast<uint32_t>(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<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},
|
||
{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<uint32_t>(vertexInputAttributes.size());
|
||
vertexInputStateCI.pVertexAttributeDescriptions = vertexInputAttributes.data();
|
||
|
||
// Pipelines
|
||
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 = static_cast<uint32_t>(shaderStages.size());
|
||
pipelineCI.pStages = shaderStages.data();
|
||
|
||
if (settings.multiSampling)
|
||
{
|
||
multisampleStateCI.rasterizationSamples = settings.sampleCount;
|
||
}
|
||
|
||
// Skybox pipeline (background cube)
|
||
shaderStages = {
|
||
loadShader(device, m_configFilePath.getSkyboxVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT),
|
||
loadShader(device, m_configFilePath.getSkyboxFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT)};
|
||
VkPipeline skyboxPipeline = m_pipelineList.getSkybox();
|
||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &skyboxPipeline));
|
||
for (auto shaderStage : shaderStages)
|
||
{
|
||
vkDestroyShaderModule(device, shaderStage.module, nullptr);
|
||
}
|
||
|
||
// PBR pipeline
|
||
shaderStages = {
|
||
loadShader(device, m_configFilePath.getPbrVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT),
|
||
loadShader(device, m_configFilePath.getPbrFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT)};
|
||
depthStencilStateCI.depthWriteEnable = VK_TRUE;
|
||
depthStencilStateCI.depthTestEnable = VK_TRUE;
|
||
VkPipeline pbrPipeline = m_pipelineList.getPbr();
|
||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pbrPipeline));
|
||
rasterizationStateCI.cullMode = VK_CULL_MODE_NONE;
|
||
VkPipeline pbrDoubleSidedPipeline = m_pipelineList.getPbrDoubleSided();
|
||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pbrDoubleSidedPipeline));
|
||
|
||
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;
|
||
VkPipeline pbrAlphaBlendPipeline = m_pipelineList.getPbrAlphaBlend();
|
||
VK_CHECK_RESULT(vkCreateGraphicsPipelines(device, pipelineCache, 1, &pipelineCI, nullptr, &pbrAlphaBlendPipeline));
|
||
|
||
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 = 128;
|
||
break;
|
||
case PREFILTEREDENV:
|
||
format = VK_FORMAT_R16G16B16A16_SFLOAT;
|
||
dim = 4096;
|
||
break;
|
||
};
|
||
|
||
const uint32_t numMips = static_cast<uint32_t>(floor(log2(dim))) + 1;
|
||
|
||
// Create target cubemap static_cast<uint32_t>(floor(log2(dim))) +
|
||
{
|
||
// 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<float>(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<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, &renderPassCI, nullptr, &renderpass));
|
||
|
||
// Create offscreen framebuffer
|
||
{
|
||
VkImage offscreenImage = m_offScreen.getImage();
|
||
VkDeviceMemory offscreenMemory = m_offScreen.getMemory();
|
||
// 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, &offscreenImage));
|
||
VkMemoryRequirements memReqs;
|
||
vkGetImageMemoryRequirements(device, offscreenImage, &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, &offscreenMemory));
|
||
VK_CHECK_RESULT(vkBindImageMemory(device, offscreenImage, offscreenMemory, 0));
|
||
|
||
// View
|
||
VkImageView offscreenImageView = m_offScreen.getView();
|
||
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 = offscreenImage;
|
||
VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &offscreenImageView));
|
||
|
||
// Framebuffer
|
||
VkFramebufferCreateInfo framebufferCI{};
|
||
framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||
framebufferCI.renderPass = renderpass;
|
||
framebufferCI.attachmentCount = 1;
|
||
framebufferCI.pAttachments = &offscreenImageView;
|
||
framebufferCI.width = dim;
|
||
framebufferCI.height = dim;
|
||
framebufferCI.layers = 1;
|
||
VkFramebuffer offscreenFrameBuffer = m_offScreen.getFramebuffer();
|
||
VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCI, nullptr, &offscreenFrameBuffer));
|
||
|
||
VkCommandBuffer layoutCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||
VkImageMemoryBarrier imageMemoryBarrier{};
|
||
imageMemoryBarrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||
imageMemoryBarrier.image = offscreenImage;
|
||
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 = &m_sceneTextures.getEnvironmentCube().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<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(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<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, m_configFilePath.getFilterVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT);
|
||
switch (target)
|
||
{
|
||
case IRRADIANCE:
|
||
shaderStages[1] = loadShader(device, m_configFilePath.getIrradianceFragShaderPath(), VK_SHADER_STAGE_FRAGMENT_BIT);
|
||
break;
|
||
case PREFILTEREDENV:
|
||
shaderStages[1] = loadShader(device, m_configFilePath.getPrefilterEnvmapFragShaderPath(), 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 = m_offScreen.getFramebuffer();
|
||
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 = 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;
|
||
viewport.x = 0;
|
||
viewport.y = -viewport.height;
|
||
|
||
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<float>(dim * std::pow(0.5f, m));
|
||
viewport.height = -static_cast<float>(dim * std::pow(0.5f, m));
|
||
viewport.x = 0;
|
||
viewport.y = -viewport.height;
|
||
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:
|
||
m_irradiancePushBlock.setMvp(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), &m_irradiancePushBlock);
|
||
break;
|
||
case PREFILTEREDENV:
|
||
m_prefilterPushBlock.setMvp(glm::perspective((float)(M_PI / 2.0), 1.0f, 0.1f, 512.0f) * matrices[f]);
|
||
m_prefilterPushBlock.setRoughness(0.0); //(float)m / (float)(numMips - 1);
|
||
vkCmdPushConstants(cmdBuf, pipelinelayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PrefilterPushBlock), &m_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};
|
||
|
||
m_sceneModel.getSkyBox().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 = m_offScreen.getImage();
|
||
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,
|
||
m_offScreen.getImage(),
|
||
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 = m_offScreen.getImage();
|
||
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, m_offScreen.getFramebuffer(), nullptr);
|
||
vkFreeMemory(device, m_offScreen.getMemory(), nullptr);
|
||
vkDestroyImageView(device, m_offScreen.getView(), nullptr);
|
||
vkDestroyImage(device, m_offScreen.getImage(), 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:
|
||
m_sceneTextures.getIrradianceCube() = cubemap;
|
||
break;
|
||
case PREFILTEREDENV:
|
||
m_sceneTextures.getPrefilteredCube() = cubemap;
|
||
m_shaderData.setPrefilteredCubeMipLevels(static_cast<float>(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;
|
||
}
|
||
}
|
||
// 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 = 2048;
|
||
vks::Texture2D RefLutBrdfTex = m_sceneTextures.getLutBrdf();
|
||
|
||
// 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, &RefLutBrdfTex.image));
|
||
VkMemoryRequirements memReqs;
|
||
|
||
vkGetImageMemoryRequirements(device, RefLutBrdfTex.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, &RefLutBrdfTex.deviceMemory));
|
||
VK_CHECK_RESULT(vkBindImageMemory(device, RefLutBrdfTex.image, RefLutBrdfTex.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 = RefLutBrdfTex.image;
|
||
VK_CHECK_RESULT(vkCreateImageView(device, &viewCI, nullptr, &RefLutBrdfTex.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, &RefLutBrdfTex.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, &renderPassCI, nullptr, &renderpass));
|
||
|
||
VkFramebufferCreateInfo framebufferCI{};
|
||
framebufferCI.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||
framebufferCI.renderPass = renderpass;
|
||
framebufferCI.attachmentCount = 1;
|
||
framebufferCI.pAttachments = &RefLutBrdfTex.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<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, m_configFilePath.getBrdfVertShaderPath(), VK_SHADER_STAGE_VERTEX_BIT),
|
||
loadShader(device, m_configFilePath.getBrdfFragShaderPath(), 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;
|
||
// viewport.x = 0;
|
||
// viewport.y = -viewport.height;
|
||
|
||
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);
|
||
|
||
RefLutBrdfTex.descriptor.imageView = RefLutBrdfTex.view;
|
||
RefLutBrdfTex.descriptor.sampler = RefLutBrdfTex.sampler;
|
||
RefLutBrdfTex.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||
RefLutBrdfTex.device = vulkanDevice;
|
||
|
||
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;
|
||
}
|
||
|
||
// Prepare and initialize uniform buffer containing shader uniforms
|
||
void PlumageRender::prepareUniformBuffers()
|
||
{
|
||
for (auto &uniformBuffer : uniformBuffers)
|
||
{
|
||
uniformBuffer.getScene().create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(m_shaderDataScene));
|
||
uniformBuffer.getSkybox().create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(m_shaderDataSkybox));
|
||
uniformBuffer.getParams().create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(m_shaderData));
|
||
}
|
||
updateUniformBuffers();
|
||
}
|
||
|
||
void PlumageRender::updateUniformBuffers()
|
||
{
|
||
// Scene
|
||
m_shaderDataScene.setProjection(camera.matrices.perspective);
|
||
m_shaderDataScene.setView(camera.matrices.view);
|
||
glTFModel::Model &models = m_sceneModel.getScene();
|
||
|
||
float modelSize = std::max(models.aabb[0][0], std::max(models.aabb[1][1], models.aabb[2][2]));
|
||
// Center and scale model
|
||
float scale = (1.0f / modelSize) * 0.5f;
|
||
glm::vec3 translate = -glm::vec3(models.aabb[3][0], models.aabb[3][1], models.aabb[3][2]);
|
||
translate += -0.5f * glm::vec3(models.aabb[0][0], models.aabb[1][1], models.aabb[2][2]);
|
||
|
||
// camera.setPosition(glm::vec3(0, 0, -modelSize - 2));
|
||
|
||
m_shaderDataScene.setModel(glm::mat4(1.0f));
|
||
m_shaderDataScene.setModel(glm::scale(m_shaderDataScene.getModel(), glm::vec3(scale)));
|
||
m_shaderDataScene.setModel(glm::translate(m_shaderDataScene.getModel(), translate));
|
||
|
||
if (settings.rotateModel)
|
||
{
|
||
// shaderDataScene.model = glm::mat4(1.0f);
|
||
m_shaderDataScene.setModel(glm::rotate(m_shaderDataScene.getModel(), glm::radians(modelrot), glm::vec3(0, 1, 0)));
|
||
}
|
||
|
||
m_shaderDataScene.setCamPos(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
|
||
m_shaderDataSkybox.setProjection(camera.matrices.perspective);
|
||
m_shaderDataSkybox.setView(camera.matrices.view);
|
||
m_shaderDataSkybox.setModel(glm::mat4(glm::mat3(camera.matrices.view)));
|
||
}
|
||
|
||
void PlumageRender::updateShaderData()
|
||
{
|
||
glm::vec4 currentLightDir = glm::vec4(
|
||
sin(glm::radians(m_lightSource.getRotation().x)) * cos(glm::radians(m_lightSource.getRotation().y)),
|
||
sin(glm::radians(m_lightSource.getRotation().y)),
|
||
cos(glm::radians(m_lightSource.getRotation().x)) * cos(glm::radians(m_lightSource.getRotation().y)),
|
||
0.0f);
|
||
m_shaderData.setLightDir(currentLightDir);
|
||
}
|
||
|
||
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;
|
||
|
||
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<uint32_t>(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::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue)
|
||
{
|
||
VkSubmitInfo submitInfo = vks::initializers::submitInfo();
|
||
submitInfo.commandBufferCount = 1;
|
||
submitInfo.pCommandBuffers = &cmdBuffer;
|
||
VkFenceCreateInfo fenceInfo = vks::initializers::fenceCreateInfo();
|
||
VkFence fence;
|
||
VK_CHECK_RESULT(vkCreateFence(device, &fenceInfo, nullptr, &fence));
|
||
VK_CHECK_RESULT(vkQueueSubmit(queue, 1, &submitInfo, fence));
|
||
VK_CHECK_RESULT(vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX));
|
||
vkDestroyFence(device, fence, nullptr);
|
||
}
|
||
|
||
// todo :根据physicalDeviceIndex确定子文件夹路径,frameIndex确定fileName
|
||
void PlumageRender::writeImageToFile(std::string filePath)
|
||
{
|
||
|
||
bool screenshotSaved = false;
|
||
bool supportsBlit = true;
|
||
|
||
// Check blit support for source and destination
|
||
VkFormatProperties formatProps;
|
||
|
||
// Check if the device supports blitting from optimal images (the swapchain images are in optimal format)
|
||
vkGetPhysicalDeviceFormatProperties(physicalDevice, swapChain.colorFormat, &formatProps);
|
||
if (!(formatProps.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT))
|
||
{
|
||
std::cerr << "Device does not support blitting from optimal tiled images, using copy instead of blit!" << std::endl;
|
||
supportsBlit = false;
|
||
}
|
||
|
||
// Check if the device supports blitting to linear images
|
||
vkGetPhysicalDeviceFormatProperties(physicalDevice, VK_FORMAT_R8G8B8A8_UNORM, &formatProps);
|
||
if (!(formatProps.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT))
|
||
{
|
||
std::cerr << "Device does not support blitting to linear tiled images, using copy instead of blit!" << std::endl;
|
||
supportsBlit = false;
|
||
}
|
||
|
||
// Source for the copy is the last rendered swapchain image
|
||
VkImage srcImage = swapChain.images[currentBuffer];
|
||
|
||
// Create the linear tiled destination image to copy to and to read the memory from
|
||
VkImageCreateInfo imageCreateCI(vks::initializers::imageCreateInfo());
|
||
imageCreateCI.imageType = VK_IMAGE_TYPE_2D;
|
||
// Note that vkCmdBlitImage (if supported) will also do format conversions if the swapchain color format would differ
|
||
imageCreateCI.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||
imageCreateCI.extent.width = width;
|
||
imageCreateCI.extent.height = height;
|
||
imageCreateCI.extent.depth = 1;
|
||
imageCreateCI.arrayLayers = 1;
|
||
imageCreateCI.mipLevels = 1;
|
||
imageCreateCI.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||
imageCreateCI.samples = VK_SAMPLE_COUNT_1_BIT;
|
||
imageCreateCI.tiling = VK_IMAGE_TILING_LINEAR;
|
||
imageCreateCI.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||
// Create the image
|
||
VkImage dstImage;
|
||
VK_CHECK_RESULT(vkCreateImage(device, &imageCreateCI, nullptr, &dstImage));
|
||
// Create memory to back up the image
|
||
VkMemoryRequirements memRequirements;
|
||
VkMemoryAllocateInfo memAllocInfo(vks::initializers::memoryAllocateInfo());
|
||
VkDeviceMemory dstImageMemory;
|
||
vkGetImageMemoryRequirements(device, dstImage, &memRequirements);
|
||
memAllocInfo.allocationSize = memRequirements.size;
|
||
// Memory must be host visible to copy from
|
||
memAllocInfo.memoryTypeIndex = vulkanDevice->getMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
|
||
VK_CHECK_RESULT(vkAllocateMemory(device, &memAllocInfo, nullptr, &dstImageMemory));
|
||
VK_CHECK_RESULT(vkBindImageMemory(device, dstImage, dstImageMemory, 0));
|
||
|
||
// Do the actual blit from the swapchain image to our host visible destination image
|
||
VkCommandBuffer copyCmd = vulkanDevice->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||
|
||
// Transition destination image to transfer destination layout
|
||
vks::tools::insertImageMemoryBarrier(
|
||
copyCmd,
|
||
dstImage,
|
||
0,
|
||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||
VK_IMAGE_LAYOUT_UNDEFINED,
|
||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
|
||
|
||
// Transition swapchain image from present to transfer source layout
|
||
vks::tools::insertImageMemoryBarrier(
|
||
copyCmd,
|
||
srcImage,
|
||
VK_ACCESS_MEMORY_READ_BIT,
|
||
VK_ACCESS_TRANSFER_READ_BIT,
|
||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
|
||
|
||
// If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB)
|
||
if (supportsBlit)
|
||
{
|
||
// Define the region to blit (we will blit the whole swapchain image)
|
||
VkOffset3D blitSize;
|
||
blitSize.x = width;
|
||
blitSize.y = height;
|
||
blitSize.z = 1;
|
||
VkImageBlit imageBlitRegion{};
|
||
imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
imageBlitRegion.srcSubresource.layerCount = 1;
|
||
imageBlitRegion.srcOffsets[1] = blitSize;
|
||
imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
imageBlitRegion.dstSubresource.layerCount = 1;
|
||
imageBlitRegion.dstOffsets[1] = blitSize;
|
||
|
||
// Issue the blit command
|
||
vkCmdBlitImage(
|
||
copyCmd,
|
||
srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
1,
|
||
&imageBlitRegion,
|
||
VK_FILTER_NEAREST);
|
||
}
|
||
else
|
||
{
|
||
// Otherwise use image copy (requires us to manually flip components)
|
||
VkImageCopy imageCopyRegion{};
|
||
imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
imageCopyRegion.srcSubresource.layerCount = 1;
|
||
imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
imageCopyRegion.dstSubresource.layerCount = 1;
|
||
imageCopyRegion.extent.width = width;
|
||
imageCopyRegion.extent.height = height;
|
||
imageCopyRegion.extent.depth = 1;
|
||
|
||
// Issue the copy command
|
||
vkCmdCopyImage(
|
||
copyCmd,
|
||
srcImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
dstImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
1,
|
||
&imageCopyRegion);
|
||
}
|
||
|
||
// Transition destination image to general layout, which is the required layout for mapping the image memory later on
|
||
vks::tools::insertImageMemoryBarrier(
|
||
copyCmd,
|
||
dstImage,
|
||
VK_ACCESS_TRANSFER_WRITE_BIT,
|
||
VK_ACCESS_MEMORY_READ_BIT,
|
||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
VK_IMAGE_LAYOUT_GENERAL,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
|
||
|
||
// Transition back the swap chain image after the blit is done
|
||
vks::tools::insertImageMemoryBarrier(
|
||
copyCmd,
|
||
srcImage,
|
||
VK_ACCESS_TRANSFER_READ_BIT,
|
||
VK_ACCESS_MEMORY_READ_BIT,
|
||
VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
|
||
|
||
vulkanDevice->flushCommandBuffer(copyCmd, queue);
|
||
|
||
// Get layout of the image (including row pitch)
|
||
VkImageSubresource subResource{VK_IMAGE_ASPECT_COLOR_BIT, 0, 0};
|
||
VkSubresourceLayout subResourceLayout;
|
||
vkGetImageSubresourceLayout(device, dstImage, &subResource, &subResourceLayout);
|
||
|
||
// Map image memory so we can start copying from it
|
||
const char *data;
|
||
vkMapMemory(device, dstImageMemory, 0, VK_WHOLE_SIZE, 0, (void **)&data);
|
||
data += subResourceLayout.offset;
|
||
|
||
if (settings.outputPNGimage)
|
||
{
|
||
stbi_write_png(filePath.c_str(), width, height, 4, data, static_cast<int>(subResourceLayout.rowPitch));
|
||
}
|
||
else
|
||
{
|
||
std::ofstream file(filePath, std::ios::out | std::ios::binary);
|
||
|
||
// ppm header
|
||
file << "P6\n"
|
||
<< width << "\n"
|
||
<< height << "\n"
|
||
<< 255 << "\n";
|
||
|
||
// If source is BGR (destination is always RGB) and we can't use blit (which does automatic conversion), we'll have to manually swizzle color components
|
||
bool colorSwizzle = false;
|
||
// Check if source is BGR
|
||
// Note: Not complete, only contains most common and basic BGR surface formats for demonstration purposes
|
||
if (!supportsBlit)
|
||
{
|
||
std::vector<VkFormat> formatsBGR = {VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM};
|
||
colorSwizzle = (std::find(formatsBGR.begin(), formatsBGR.end(), swapChain.colorFormat) != formatsBGR.end());
|
||
}
|
||
// ppm binary pixel data
|
||
for (uint32_t y = 0; y < height; y++)
|
||
{
|
||
unsigned int *row = (unsigned int *)data;
|
||
for (uint32_t x = 0; x < width; x++)
|
||
{
|
||
if (colorSwizzle)
|
||
{
|
||
file.write((char *)row + 2, 1);
|
||
file.write((char *)row + 1, 1);
|
||
file.write((char *)row, 1);
|
||
}
|
||
else
|
||
{
|
||
file.write((char *)row, 3);
|
||
}
|
||
row++;
|
||
}
|
||
data += subResourceLayout.rowPitch;
|
||
}
|
||
file.close();
|
||
}
|
||
|
||
std::cout << "Screenshot saved to " << filePath << std::endl;
|
||
|
||
// Clean up resources
|
||
vkUnmapMemory(device, dstImageMemory);
|
||
vkFreeMemory(device, dstImageMemory, nullptr);
|
||
vkDestroyImage(device, dstImage, nullptr);
|
||
|
||
screenshotSaved = true;
|
||
}
|
||
|
||
void PlumageRender::outputImageSequence()
|
||
{
|
||
|
||
if (savedFrameCounter == settings.startFrameCount)
|
||
{
|
||
std::cout << "clean up directory for image sequence generation" << std::endl;
|
||
removeImageSequence();
|
||
}
|
||
|
||
m_configFilePath.setDeviceSpecFilePath(m_configFilePath.getImageOutputPath() + "/device" + std::to_string(selectedPhysicalDeviceIndex));
|
||
|
||
if (savedFrameCounter > settings.outputFrameCount)
|
||
{
|
||
if (m_videoOutputState->getImageSequenceOutputComplete()) // 避免重复改变为true
|
||
{
|
||
return;
|
||
}
|
||
m_videoOutputState->setImageSequenceOutputComplete(true);
|
||
|
||
std::string fileName;
|
||
|
||
if (settings.outputPNGimage)
|
||
{
|
||
fileName = "/%dresult.png";
|
||
}
|
||
else
|
||
{
|
||
fileName = "/%dresult.ppm";
|
||
}
|
||
m_configFilePath.setTotalImageOutputPath(m_configFilePath.getDeviceSpecFilePath() + fileName);
|
||
return;
|
||
}
|
||
if (_access(m_configFilePath.getDeviceSpecFilePath().c_str(), 0) == -1)
|
||
{
|
||
std::filesystem::create_directories(m_configFilePath.getDeviceSpecFilePath().c_str());
|
||
}
|
||
std::string fileNameSuffix;
|
||
if (settings.outputPNGimage)
|
||
{
|
||
fileNameSuffix = "result.png";
|
||
}
|
||
else
|
||
{
|
||
fileNameSuffix = "result.ppm";
|
||
}
|
||
std::string fileName = "/" + std::to_string(savedFrameCounter) + fileNameSuffix;
|
||
m_configFilePath.setTotalImageOutputPath(m_configFilePath.getDeviceSpecFilePath() + fileName);
|
||
|
||
// std::cout << outputPath << std::endl;
|
||
writeImageToFile(m_configFilePath.getTotalImageOutputPath().c_str());
|
||
savedFrameCounter++;
|
||
}
|
||
|
||
void PlumageRender::imageSequenceToVideo()
|
||
{
|
||
if (!m_videoOutputState->getImageSequenceOutputComplete())
|
||
{
|
||
return;
|
||
}
|
||
if (m_videoOutputState->getImageSequenceToVideoComplete())
|
||
{
|
||
return;
|
||
}
|
||
|
||
std::string deviceFilePath = m_configFilePath.getVideoOutputPath() + "/device" + std::to_string(selectedPhysicalDeviceIndex);
|
||
if (_access(deviceFilePath.c_str(), 0) == -1)
|
||
{
|
||
std::filesystem::create_directories(deviceFilePath.c_str());
|
||
}
|
||
|
||
std::string resultVideoPath = deviceFilePath + "/result.mp4";
|
||
|
||
std::string commandLineImageSequencePath = m_configFilePath.getTotalImageOutputPath();
|
||
// std::string commandLineCodecAndResultPath = resultVideoPath;
|
||
std::string commandLineFrameRate = std::to_string(settings.videoFrameRate);
|
||
#if defined(_WIN32)
|
||
std::string commandLine = m_configFilePath.getImage2videoBatFilePath() + " " + commandLineFrameRate + " " + commandLineImageSequencePath + " " + resultVideoPath;
|
||
#else
|
||
std::string commandLine = filePath.image2videoShFilePath + " " + commandLineFrameRate + " " + commandLineImageSequencePath + " " + resultVideoPath;
|
||
#endif
|
||
std::cout << commandLine << std::endl;
|
||
std::system(commandLine.c_str());
|
||
|
||
m_videoOutputState->setImageSequenceToVideoComplete(true);
|
||
std::cout << "vidoe codec complete,saved in:" << resultVideoPath << std::endl;
|
||
std::cout << "star to clean up image sequence" << std::endl;
|
||
removeImageSequence();
|
||
}
|
||
|
||
void PlumageRender::removeImageSequence()
|
||
{
|
||
if (savedFrameCounter != settings.startFrameCount)
|
||
{
|
||
if (!m_videoOutputState->getImageSequenceToVideoComplete())
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
if (std::filesystem::exists(m_configFilePath.getDeviceSpecFilePath()))
|
||
{
|
||
for (const auto &entry : std::filesystem::directory_iterator(m_configFilePath.getDeviceSpecFilePath()))
|
||
{
|
||
if (std::filesystem::is_directory(entry.path()))
|
||
{
|
||
std::filesystem::remove_all(entry.path());
|
||
}
|
||
else
|
||
{
|
||
std::filesystem::remove(entry.path());
|
||
}
|
||
}
|
||
std::filesystem::remove(m_configFilePath.getDeviceSpecFilePath());
|
||
std::cout << "clean up complete" << std::endl;
|
||
}
|
||
return;
|
||
}
|
||
|
||
uint32_t PlumageRender::getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties)
|
||
{
|
||
VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
|
||
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &deviceMemoryProperties);
|
||
for (uint32_t i = 0; i < deviceMemoryProperties.memoryTypeCount; i++)
|
||
{
|
||
if ((typeBits & 1) == 1)
|
||
{
|
||
if ((deviceMemoryProperties.memoryTypes[i].propertyFlags & properties) == properties)
|
||
{
|
||
return i;
|
||
}
|
||
}
|
||
typeBits >>= 1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void PlumageRender::render()
|
||
{
|
||
if (!prepared)
|
||
{
|
||
return;
|
||
}
|
||
|
||
updateUIOverlay();
|
||
// 加入写到文件的函数
|
||
// swapChainImage = swapChain.images[frameIndex];
|
||
// outputImageSequeue(swapChainImage,filePath.imageSequenceFilePath);
|
||
|
||
// outputImageSequence();
|
||
|
||
VK_CHECK_RESULT(vkWaitForFences(device, 1, &waitFences[frameIndex], VK_TRUE, UINT64_MAX));
|
||
|
||
// imageSequenceToVideo();
|
||
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();
|
||
RenderUniformBufferSet currentUB = uniformBuffers[currentBuffer];
|
||
memcpy(currentUB.getScene().mapped, &m_shaderDataScene, sizeof(m_shaderDataScene));
|
||
memcpy(currentUB.getParams().mapped, &m_shaderData, sizeof(m_shaderData));
|
||
memcpy(currentUB.getSkybox().mapped, &m_shaderDataSkybox, sizeof(m_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 (settings.rotateModel)
|
||
{
|
||
modelrot += frameTimer * 2.0f;
|
||
if (modelrot > 360.0f)
|
||
{
|
||
modelrot -= 360.0f;
|
||
}
|
||
}
|
||
if ((animate) && (m_sceneModel.getScene().animations.size() > 0))
|
||
{
|
||
animationTimer += frameTimer;
|
||
if (animationTimer > m_sceneModel.getScene().animations[animationIndex].end)
|
||
{
|
||
animationTimer -= m_sceneModel.getScene().animations[animationIndex].end;
|
||
}
|
||
m_sceneModel.getScene().updateAnimation(animationIndex, animationTimer);
|
||
}
|
||
updateShaderData();
|
||
if (settings.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(10000, 10000));
|
||
// ImGui::SetNextWindowSize(ImVec2(200 * scale, (models.scene.animations.size() > 0 ? 440 : 360) * scale), ImGuiSetCond_Always);
|
||
ImGui::Begin(title.c_str(), nullptr, ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||
|
||
ImGui::PushItemWidth(100.0f * scale);
|
||
|
||
if (gui->beginMainMenuBar())
|
||
{
|
||
if (gui->beginMenu(m_localizationStrings.getMenuFile()))
|
||
{
|
||
if (gui->menuItem(m_localizationStrings.getMenuOpenNewModel()))
|
||
{
|
||
std::wstring filename = L"";
|
||
wchar_t buffer[MAX_PATH];
|
||
OPENFILENAMEW ofn;
|
||
ZeroMemory(&buffer, sizeof(buffer));
|
||
ZeroMemory(&ofn, sizeof(ofn));
|
||
ofn.lStructSize = sizeof(ofn);
|
||
ofn.lpstrFilter = L"glTF files\0*.gltf;*.glb\0";
|
||
ofn.lpstrFile = buffer;
|
||
ofn.nMaxFile = MAX_PATH;
|
||
ofn.lpstrTitle = L"Select a glTF file to load";
|
||
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
|
||
if (GetOpenFileNameW(&ofn))
|
||
{
|
||
filename = buffer;
|
||
}
|
||
|
||
if (!filename.empty())
|
||
{
|
||
vkDeviceWaitIdle(device);
|
||
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
|
||
|
||
std::string stringFilename = converter.to_bytes(filename);
|
||
loadScene(stringFilename);
|
||
setupDescriptors();
|
||
updateCBs = true;
|
||
m_videoOutputState->setImageSequenceOutputComplete(false);
|
||
m_videoOutputState->setImageSequenceToVideoComplete(false);
|
||
savedFrameCounter = 1;
|
||
}
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu(m_localizationStrings.getMenuEnvironment()))
|
||
{
|
||
if (gui->beginMenu(m_localizationStrings.getMenuEnvironmentConfig()))
|
||
{
|
||
if (gui->combo(m_localizationStrings.getEnvironmentMap(), selectedEnvironment, environments))
|
||
{
|
||
vkDeviceWaitIdle(device);
|
||
loadEnvironment(environments[selectedEnvironment]);
|
||
setupDescriptors();
|
||
updateCBs = true;
|
||
}
|
||
if (gui->checkbox("模型自转", &settings.rotateModel))
|
||
{
|
||
updateShaderParams = true;
|
||
}
|
||
if (gui->checkbox(m_localizationStrings.getEnvironmentBackGround(), &displayBackground))
|
||
{
|
||
updateShaderParams = true;
|
||
}
|
||
if (gui->slider("Exposure", &m_shaderData.getExposure(), 0.1f, 10.0f))
|
||
{
|
||
updateShaderParams = true;
|
||
}
|
||
if (gui->slider("Gamma", &m_shaderData.getGamma(), 0.1f, 4.0f))
|
||
{
|
||
updateShaderParams = true;
|
||
}
|
||
if (gui->slider("IBL", &m_shaderData.getScaleIBLAmbient(), 0.0f, 1.0f))
|
||
{
|
||
updateShaderParams = true;
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu("debug"))
|
||
{
|
||
if (gui->beginMenu(m_localizationStrings.getMenuDebugInput()))
|
||
{
|
||
const std::vector<std::string> debugNamesInputs = {
|
||
"none", "Base color", "Normal", "Occlusion", "Emissive", "Metallic", "Roughness"};
|
||
if (gui->combo(m_localizationStrings.getDebugInput(), &debugViewInputs, debugNamesInputs))
|
||
{
|
||
m_shaderData.setDebugViewInputs(static_cast<float>(debugViewInputs));
|
||
updateShaderParams = true;
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu("PBR"))
|
||
{
|
||
const std::vector<std::string> debugNamesEquation = {
|
||
"none", "Diff (l,n)", "F (l,h)", "G (l,v,h)", "D (h)", "Specular"};
|
||
if (gui->combo(m_localizationStrings.getDebugPBREquation(), &debugViewEquation, debugNamesEquation))
|
||
{
|
||
m_shaderData.setDebugViewEquation(static_cast<float>(debugViewEquation));
|
||
updateShaderParams = true;
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
|
||
if (gui->beginMenu(m_localizationStrings.getMenuDebugFrameRate()))
|
||
{
|
||
gui->text("%.1d fps (%.2f ms)", lastFPS, (1000.0f / lastFPS));
|
||
gui->endMenu();
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu(m_localizationStrings.getMenuAnimation()))
|
||
{
|
||
if (m_sceneModel.getScene().animations.size() > 0)
|
||
{
|
||
if (gui->beginMenu(m_localizationStrings.getMenuAnimationActivation()))
|
||
{
|
||
gui->checkbox(m_localizationStrings.getPauseAnimation(), &animate);
|
||
gui->endMenu();
|
||
}
|
||
if (gui->beginMenu(m_localizationStrings.getMenuAnimationAnimationSequence()))
|
||
{
|
||
std::vector<std::string> animationNames;
|
||
for (auto animation : m_sceneModel.getScene().animations)
|
||
{
|
||
animationNames.push_back(animation.name);
|
||
}
|
||
gui->combo(m_localizationStrings.getAnimationSeq(), &animationIndex, animationNames);
|
||
gui->endMenu();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
gui->text(m_localizationStrings.getMenuAnimationNoAnimation());
|
||
}
|
||
gui->endMenu();
|
||
}
|
||
|
||
gui->endMainMenuBar();
|
||
}
|
||
|
||
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
|