剥离UI和IO部分,重构完成到commandbuffer
parent
e9eb47dfda
commit
ee81b8e56d
|
@ -104,7 +104,7 @@ public:
|
||||||
|
|
||||||
uint32_t selectedPhysicalDeviceIndex = 0;
|
uint32_t selectedPhysicalDeviceIndex = 0;
|
||||||
bool prepared = false;
|
bool prepared = false;
|
||||||
float frameTimer = 1.0f;
|
|
||||||
Camera camera;
|
Camera camera;
|
||||||
glm::vec2 mousePos;
|
glm::vec2 mousePos;
|
||||||
bool paused = false;
|
bool paused = false;
|
||||||
|
@ -118,16 +118,7 @@ public:
|
||||||
VkImageView view;
|
VkImageView view;
|
||||||
} depthStencil;
|
} depthStencil;
|
||||||
|
|
||||||
struct GamePadState {
|
|
||||||
glm::vec2 axisLeft = glm::vec2(0.0f);
|
|
||||||
glm::vec2 axisRight = glm::vec2(0.0f);
|
|
||||||
} gamePadState;
|
|
||||||
|
|
||||||
struct MouseButtons {
|
|
||||||
bool left = false;
|
|
||||||
bool right = false;
|
|
||||||
bool middle = false;
|
|
||||||
} mouseButtons;
|
|
||||||
|
|
||||||
// OS specific
|
// OS specific
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
|
@ -34,7 +34,7 @@ function(buildHomework HOMEWORK_NAME)
|
||||||
"render/glTFModel.h"
|
"render/glTFModel.h"
|
||||||
"render/glTFModel.cpp"
|
"render/glTFModel.cpp"
|
||||||
|
|
||||||
"render/vulkanFoundation.h" "render/vulkanFoundation.cpp" "render/renderSetter.h" "render/renderSetter.cpp" "render/PBR.h" "render/PBR.cpp")
|
"render/vulkanFoundation.h" "render/vulkanFoundation.cpp" "render/renderSetter.h" "render/renderSetter.cpp" "render/PBR.h" "render/PBR.cpp" "render/renderUI.h" "render/renderUI.cpp" "render/renderIO.h" "render/renderIO.cpp")
|
||||||
target_link_libraries(${HOMEWORK_NAME} base ${Vulkan_LIBRARY} ${WINLIBS})
|
target_link_libraries(${HOMEWORK_NAME} base ${Vulkan_LIBRARY} ${WINLIBS})
|
||||||
else(WIN32)
|
else(WIN32)
|
||||||
add_executable(${HOMEWORK_NAME} ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES})
|
add_executable(${HOMEWORK_NAME} ${MAIN_CPP} ${SOURCE} ${MAIN_HEADER} ${SHADERS_GLSL} ${SHADERS_HLSL} ${README_FILES})
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "glm/glm.hpp"
|
#include "glm/glm.hpp"
|
||||||
|
#include <VulkanTexture.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,6 +29,16 @@ namespace PBR
|
||||||
float alphaMaskCutoff;
|
float alphaMaskCutoff;
|
||||||
} pushConstBlockMaterial;
|
} pushConstBlockMaterial;
|
||||||
|
|
||||||
|
struct Textures {
|
||||||
|
vks::TextureCubeMap environmentCube;
|
||||||
|
vks::Texture2D empty;
|
||||||
|
vks::Texture2D lutBrdf;
|
||||||
|
vks::TextureCubeMap irradianceCube;
|
||||||
|
vks::TextureCubeMap prefilteredCube;
|
||||||
|
} textures;
|
||||||
|
|
||||||
|
enum PBRWorkflows { PBR_WORKFLOW_METALLIC_ROUGHNESS = 0, PBR_WORKFLOW_SPECULAR_GLOSINESS = 1 };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
glTFModel::BoundingBox::BoundingBox(glm::vec3 min, glm::vec3 max) : min(min), max(max) {
|
glTFModel::BoundingBox::BoundingBox(glm::vec3 min, glm::vec3 max) : min(min), max(max) {
|
||||||
};
|
};
|
||||||
|
|
||||||
//Axis-Aligned Bounding Box),¼ò³Æ AABB
|
//Axis-Aligned Bounding Box),简称 AABB
|
||||||
glTFModel::BoundingBox glTFModel::BoundingBox::getAABB(glm::mat4 m) {
|
glTFModel::BoundingBox glTFModel::BoundingBox::getAABB(glm::mat4 m) {
|
||||||
glm::vec3 min = glm::vec3(m[3]);
|
glm::vec3 min = glm::vec3(m[3]);
|
||||||
glm::vec3 max = min;
|
glm::vec3 max = min;
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "VulkanDevice.hpp"
|
#include "VulkanDevice.hpp"
|
||||||
//#include "VulkanUtils.hpp"
|
//#include "VulkanUtils.hpp"
|
||||||
#include "vulkan/vulkan.h"
|
#include "vulkan/vulkan.h"
|
||||||
|
#include "vulkanFoundation.h"
|
||||||
|
|
||||||
#define ENABLE_VALIDATION false
|
#define ENABLE_VALIDATION false
|
||||||
#define MAX_NUM_JOINTS 128u
|
#define MAX_NUM_JOINTS 128u
|
||||||
|
@ -258,6 +259,7 @@ namespace glTFModel
|
||||||
void loadFromFile(std::string filename, vks::VulkanDevice* device, VkQueue transferQueue, float scale = 1.0f);
|
void loadFromFile(std::string filename, vks::VulkanDevice* device, VkQueue transferQueue, float scale = 1.0f);
|
||||||
void drawNode(Node* node, VkCommandBuffer commandBuffer);
|
void drawNode(Node* node, VkCommandBuffer commandBuffer);
|
||||||
void draw(VkCommandBuffer commandBuffer);
|
void draw(VkCommandBuffer commandBuffer);
|
||||||
|
void createNodeCommandBuffer(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode);
|
||||||
void calculateBoundingBox(Node* node, Node* parent);
|
void calculateBoundingBox(Node* node, Node* parent);
|
||||||
void getSceneDimensions();
|
void getSceneDimensions();
|
||||||
void updateAnimation(uint32_t index, float time);
|
void updateAnimation(uint32_t index, float time);
|
||||||
|
|
|
@ -18,112 +18,25 @@
|
||||||
//#include "assetLoader.h"
|
//#include "assetLoader.h"
|
||||||
|
|
||||||
|
|
||||||
|
void PlumageRender::renderMain::initWindow(int Width, int Height)
|
||||||
PlumageRender::PlumageRender()
|
|
||||||
{
|
|
||||||
title = "plumage render";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PlumageRender::initWindow(int Width, int Height)
|
|
||||||
{
|
{
|
||||||
glfwInit();
|
glfwInit();
|
||||||
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
|
||||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
|
||||||
window = glfwCreateWindow(Width, Height, "vulkan", nullptr, nullptr);
|
window = glfwCreateWindow(Width, Height, "vulkan", nullptr, nullptr);
|
||||||
glfwSetWindowUserPointer(window, this);
|
glfwSetWindowUserPointer(window, this);
|
||||||
glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
|
glfwSetFramebufferSizeCallback(window, framebufferResizeCallback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::framebufferResizeCallback(GLFWwindow* window, int width, int height)
|
void PlumageRender::renderMain::framebufferResizeCallback(GLFWwindow* window, int width, int height)
|
||||||
{
|
{
|
||||||
auto app = reinterpret_cast<PlumageRender*>(glfwGetWindowUserPointer(window));
|
auto app = reinterpret_cast<renderMain*>(glfwGetWindowUserPointer(window));
|
||||||
app->framebufferResized = true;
|
app->framebufferResized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 重构至gltfModel中
|
void PlumageRender::renderMain::buildCommandBuffers()
|
||||||
void PlumageRender::renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode) {
|
|
||||||
if (node->mesh) {
|
|
||||||
// Render mesh primitives
|
|
||||||
for (glTFModel::Primitive* primitive : node->mesh->primitives) {
|
|
||||||
if (primitive->material.alphaMode == alphaMode) {
|
|
||||||
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
||||||
switch (alphaMode) {
|
|
||||||
case glTFModel::Material::ALPHAMODE_OPAQUE:
|
|
||||||
case glTFModel::Material::ALPHAMODE_MASK:
|
|
||||||
pipeline = primitive->material.doubleSided ? pipelines.pbrDoubleSided : pipelines.pbr;
|
|
||||||
break;
|
|
||||||
case glTFModel::Material::ALPHAMODE_BLEND:
|
|
||||||
pipeline = pipelines.pbrAlphaBlend;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pipeline != boundPipeline) {
|
|
||||||
vkCmdBindPipeline(commandBuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
|
||||||
boundPipeline = pipeline;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<VkDescriptorSet> descriptorsets = {
|
|
||||||
descriptorSets[cbIndex].scene,
|
|
||||||
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 pushConstBlockMaterial{};
|
|
||||||
pushConstBlockMaterial.emissiveFactor = primitive->material.emissiveFactor;
|
|
||||||
// To save push constant space, availabilty and texture coordiante set are combined
|
|
||||||
// -1 = texture not used for this material, >= 0 texture used and index of texture coordinate set
|
|
||||||
pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
|
|
||||||
pushConstBlockMaterial.normalTextureSet = primitive->material.normalTexture != nullptr ? primitive->material.texCoordSets.normal : -1;
|
|
||||||
pushConstBlockMaterial.occlusionTextureSet = primitive->material.occlusionTexture != nullptr ? primitive->material.texCoordSets.occlusion : -1;
|
|
||||||
pushConstBlockMaterial.emissiveTextureSet = primitive->material.emissiveTexture != nullptr ? primitive->material.texCoordSets.emissive : -1;
|
|
||||||
pushConstBlockMaterial.alphaMask = static_cast<float>(primitive->material.alphaMode == glTFModel::Material::ALPHAMODE_MASK);
|
|
||||||
pushConstBlockMaterial.alphaMaskCutoff = primitive->material.alphaCutoff;
|
|
||||||
|
|
||||||
// TODO: glTF specs states that metallic roughness should be preferred, even if specular glosiness is present
|
|
||||||
|
|
||||||
if (primitive->material.pbrWorkflows.metallicRoughness) {
|
|
||||||
// Metallic roughness workflow
|
|
||||||
pushConstBlockMaterial.workflow = static_cast<float>(PBR_WORKFLOW_METALLIC_ROUGHNESS);
|
|
||||||
pushConstBlockMaterial.baseColorFactor = primitive->material.baseColorFactor;
|
|
||||||
pushConstBlockMaterial.metallicFactor = primitive->material.metallicFactor;
|
|
||||||
pushConstBlockMaterial.roughnessFactor = primitive->material.roughnessFactor;
|
|
||||||
pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.metallicRoughnessTexture != nullptr ? primitive->material.texCoordSets.metallicRoughness : -1;
|
|
||||||
pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (primitive->material.pbrWorkflows.specularGlossiness) {
|
|
||||||
// Specular glossiness workflow
|
|
||||||
pushConstBlockMaterial.workflow = static_cast<float>(PBR_WORKFLOW_SPECULAR_GLOSINESS);
|
|
||||||
pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.extension.specularGlossinessTexture != nullptr ? primitive->material.texCoordSets.specularGlossiness : -1;
|
|
||||||
pushConstBlockMaterial.colorTextureSet = primitive->material.extension.diffuseTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
|
|
||||||
pushConstBlockMaterial.diffuseFactor = primitive->material.extension.diffuseFactor;
|
|
||||||
pushConstBlockMaterial.specularFactor = glm::vec4(primitive->material.extension.specularFactor, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
vkCmdPushConstants(commandBuffers[cbIndex], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PushConstBlockMaterial), &pushConstBlockMaterial);
|
|
||||||
|
|
||||||
if (primitive->hasIndices) {
|
|
||||||
vkCmdDrawIndexed(commandBuffers[cbIndex], primitive->indexCount, 1, primitive->firstIndex, 0, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
vkCmdDraw(commandBuffers[cbIndex], primitive->vertexCount, 1, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
for (auto child : node->children) {
|
|
||||||
renderNode(child, cbIndex, alphaMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void PlumageRender::buildCommandBuffers()
|
|
||||||
{
|
{
|
||||||
VkCommandBufferBeginInfo cmdBufferBeginInfo{};
|
VkCommandBufferBeginInfo cmdBufferBeginInfo{};
|
||||||
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
@ -171,7 +84,7 @@ void PlumageRender::buildCommandBuffers()
|
||||||
vkCmdSetScissor(currentCB, 0, 1, &scissor);
|
vkCmdSetScissor(currentCB, 0, 1, &scissor);
|
||||||
|
|
||||||
VkDeviceSize offsets[1] = { 0 };
|
VkDeviceSize offsets[1] = { 0 };
|
||||||
|
|
||||||
if (displayBackground) {
|
if (displayBackground) {
|
||||||
vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i].skybox, 0, nullptr);
|
vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i].skybox, 0, nullptr);
|
||||||
vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
|
vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
|
||||||
|
@ -215,7 +128,7 @@ void PlumageRender::buildCommandBuffers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PlumageRender::loadScene(std::string filename)
|
void PlumageRender::renderMain::loadScene(std::string filename)
|
||||||
{
|
{
|
||||||
std::cout << "Loading scene from " << filename << std::endl;
|
std::cout << "Loading scene from " << filename << std::endl;
|
||||||
models.scene.destroy(device);
|
models.scene.destroy(device);
|
||||||
|
@ -229,7 +142,7 @@ void PlumageRender::loadScene(std::string filename)
|
||||||
camera.setRotation({ 0.0f, 0.0f, 0.0f });
|
camera.setRotation({ 0.0f, 0.0f, 0.0f });
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::loadEnvironment(std::string filename)
|
void PlumageRender::renderMain::loadEnvironment(std::string filename)
|
||||||
{
|
{
|
||||||
std::cout << "Loading environment from " << filename << std::endl;
|
std::cout << "Loading environment from " << filename << std::endl;
|
||||||
if (textures.environmentCube.image) {
|
if (textures.environmentCube.image) {
|
||||||
|
@ -241,7 +154,7 @@ void PlumageRender::loadEnvironment(std::string filename)
|
||||||
generateCubemaps();
|
generateCubemaps();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::loadAssets()
|
void PlumageRender::renderMain::loadAssets()
|
||||||
{
|
{
|
||||||
const std::string assetpath = getAssetPath();
|
const std::string assetpath = getAssetPath();
|
||||||
|
|
||||||
|
@ -270,35 +183,15 @@ void PlumageRender::loadAssets()
|
||||||
loadEnvironment(envMapFile.c_str());
|
loadEnvironment(envMapFile.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::setupNodeDescriptorSet(glTFModel::Node* node)
|
void PlumageRender::renderMain::setupNodeDescriptorSet(glTFModel::Node* node)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
This sample uses separate descriptor sets (and layouts) for the matrices and materials (textures)
|
This sample uses separate descriptor sets (and layouts) for the matrices and materials (textures)
|
||||||
*/
|
*/
|
||||||
if (node->mesh) {
|
|
||||||
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
|
||||||
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
||||||
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
|
||||||
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.node;
|
|
||||||
descriptorSetAllocInfo.descriptorSetCount = 1;
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &node->mesh->uniformBuffer.descriptorSet));
|
|
||||||
|
|
||||||
VkWriteDescriptorSet writeDescriptorSet{};
|
|
||||||
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
||||||
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
||||||
writeDescriptorSet.descriptorCount = 1;
|
|
||||||
writeDescriptorSet.dstSet = node->mesh->uniformBuffer.descriptorSet;
|
|
||||||
writeDescriptorSet.dstBinding = 0;
|
|
||||||
writeDescriptorSet.pBufferInfo = &node->mesh->uniformBuffer.descriptor;
|
|
||||||
|
|
||||||
vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr);
|
|
||||||
}
|
|
||||||
for (auto& child : node->children) {
|
|
||||||
setupNodeDescriptorSet(child);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::setupDescriptors()
|
void PlumageRender::renderMain::setupDescriptors()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
@ -362,101 +255,10 @@ void PlumageRender::setupDescriptors()
|
||||||
|
|
||||||
// Material (samplers)
|
// Material (samplers)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
// Per-Material descriptor sets
|
|
||||||
for (auto& material : models.scene.materials) {
|
|
||||||
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
|
||||||
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
||||||
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
|
||||||
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.material;
|
|
||||||
descriptorSetAllocInfo.descriptorSetCount = 1;
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &material.descriptorSet));
|
|
||||||
|
|
||||||
std::vector<VkDescriptorImageInfo> imageDescriptors = {
|
|
||||||
textures.empty.descriptor,
|
|
||||||
textures.empty.descriptor,
|
|
||||||
material.normalTexture ? material.normalTexture->descriptor : textures.empty.descriptor,
|
|
||||||
material.occlusionTexture ? material.occlusionTexture->descriptor : textures.empty.descriptor,
|
|
||||||
material.emissiveTexture ? material.emissiveTexture->descriptor : textures.empty.descriptor
|
|
||||||
};
|
|
||||||
|
|
||||||
if (material.pbrWorkflows.metallicRoughness) {
|
|
||||||
if (material.baseColorTexture) {
|
|
||||||
imageDescriptors[0] = material.baseColorTexture->descriptor;
|
|
||||||
}
|
|
||||||
if (material.metallicRoughnessTexture) {
|
|
||||||
imageDescriptors[1] = material.metallicRoughnessTexture->descriptor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (material.pbrWorkflows.specularGlossiness) {
|
|
||||||
if (material.extension.diffuseTexture) {
|
|
||||||
imageDescriptors[0] = material.extension.diffuseTexture->descriptor;
|
|
||||||
}
|
|
||||||
if (material.extension.specularGlossinessTexture) {
|
|
||||||
imageDescriptors[1] = material.extension.specularGlossinessTexture->descriptor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::array<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)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
// Per-Node descriptor set
|
|
||||||
for (auto& node : models.scene.nodes) {
|
|
||||||
setupNodeDescriptorSet(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skybox (fixed set)
|
|
||||||
for (auto i = 0; i < uniformBuffers.size(); i++) {
|
|
||||||
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
|
||||||
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
|
||||||
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
|
||||||
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene;
|
|
||||||
descriptorSetAllocInfo.descriptorSetCount = 1;
|
|
||||||
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].skybox));
|
|
||||||
|
|
||||||
std::array<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].skybox;
|
|
||||||
writeDescriptorSets[0].dstBinding = 0;
|
|
||||||
writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].skybox.descriptor;
|
|
||||||
|
|
||||||
writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
||||||
writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
|
||||||
writeDescriptorSets[1].descriptorCount = 1;
|
|
||||||
writeDescriptorSets[1].dstSet = descriptorSets[i].skybox;
|
|
||||||
writeDescriptorSets[1].dstBinding = 1;
|
|
||||||
writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor;
|
|
||||||
|
|
||||||
writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
|
||||||
writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
|
||||||
writeDescriptorSets[2].descriptorCount = 1;
|
|
||||||
writeDescriptorSets[2].dstSet = descriptorSets[i].skybox;
|
|
||||||
writeDescriptorSets[2].dstBinding = 2;
|
|
||||||
writeDescriptorSets[2].pImageInfo = &textures.prefilteredCube.descriptor;
|
|
||||||
|
|
||||||
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -464,7 +266,7 @@ void PlumageRender::setupDescriptors()
|
||||||
// irradiance cube map
|
// irradiance cube map
|
||||||
// prefileter environment cube map
|
// prefileter environment cube map
|
||||||
// 重构到PBR中
|
// 重构到PBR中
|
||||||
void PlumageRender::generateCubemaps()
|
void PlumageRender::renderMain::generateCubemaps()
|
||||||
{
|
{
|
||||||
enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 };
|
enum Target { IRRADIANCE = 0, PREFILTEREDENV = 1 };
|
||||||
|
|
||||||
|
@ -1003,7 +805,7 @@ void PlumageRender::generateCubemaps()
|
||||||
}
|
}
|
||||||
// generate BRDF integration map for roughness/NdotV
|
// generate BRDF integration map for roughness/NdotV
|
||||||
// 重构到PBR
|
// 重构到PBR
|
||||||
void PlumageRender::generateBRDFLUT()
|
void PlumageRender::renderMain::generateBRDFLUT()
|
||||||
{
|
{
|
||||||
auto tStart = std::chrono::high_resolution_clock::now();
|
auto tStart = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
@ -1260,8 +1062,9 @@ void PlumageRender::generateBRDFLUT()
|
||||||
std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl;
|
std::cout << "Generating BRDF LUT took " << tDiff << " ms" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare and initialize uniform buffer containing shader uniforms
|
// Prepare and initialize uniform buffer containing shader uniforms
|
||||||
void PlumageRender::prepareUniformBuffers()
|
|
||||||
|
void PlumageRender::renderMain::prepareUniformBuffers()
|
||||||
{
|
{
|
||||||
for (auto& uniformBuffer : uniformBuffers) {
|
for (auto& uniformBuffer : uniformBuffers) {
|
||||||
uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene));
|
uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene));
|
||||||
|
@ -1270,37 +1073,38 @@ void PlumageRender::prepareUniformBuffers()
|
||||||
}
|
}
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
}
|
}
|
||||||
// 更新统一缓冲区
|
|
||||||
void PlumageRender::updateUniformBuffers()
|
// 更新统一缓冲区 done
|
||||||
|
void PlumageRender::renderMain::updateUniformBuffers()
|
||||||
{
|
{
|
||||||
// Scene
|
// Scene
|
||||||
shaderDataScene.projection = camera.matrices.perspective;
|
shaderDataScene.projection = camera.matrices.perspective;
|
||||||
shaderDataScene.view = camera.matrices.view;
|
shaderDataScene.view = camera.matrices.view;
|
||||||
|
|
||||||
// Center and scale model
|
// Center and scale model
|
||||||
float scale = (1.0f / std::max(models.scene.aabb[0][0], std::max(models.scene.aabb[1][1], models.scene.aabb[2][2]))) * 0.5f;
|
float scale = (1.0f / std::max(models.scene.aabb[0][0], std::max(models.scene.aabb[1][1], models.scene.aabb[2][2]))) * 0.5f;
|
||||||
glm::vec3 translate = -glm::vec3(models.scene.aabb[3][0], models.scene.aabb[3][1], models.scene.aabb[3][2]);
|
glm::vec3 translate = -glm::vec3(models.scene.aabb[3][0], models.scene.aabb[3][1], models.scene.aabb[3][2]);
|
||||||
translate += -0.5f * glm::vec3(models.scene.aabb[0][0], models.scene.aabb[1][1], models.scene.aabb[2][2]);
|
translate += -0.5f * glm::vec3(models.scene.aabb[0][0], models.scene.aabb[1][1], models.scene.aabb[2][2]);
|
||||||
|
|
||||||
shaderDataScene.model = glm::mat4(1.0f);
|
shaderDataScene.model = glm::mat4(1.0f);
|
||||||
shaderDataScene.model[0][0] = scale;
|
shaderDataScene.model[0][0] = scale;
|
||||||
shaderDataScene.model[1][1] = scale;
|
shaderDataScene.model[1][1] = scale;
|
||||||
shaderDataScene.model[2][2] = scale;
|
shaderDataScene.model[2][2] = scale;
|
||||||
shaderDataScene.model = glm::translate(shaderDataScene.model, translate);
|
shaderDataScene.model = glm::translate(shaderDataScene.model, translate);
|
||||||
|
|
||||||
shaderDataScene.camPos = glm::vec3(
|
shaderDataScene.camPos = glm::vec3(
|
||||||
-camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)),
|
-camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)),
|
||||||
-camera.position.z * sin(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))
|
camera.position.z * cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x))
|
||||||
);
|
);
|
||||||
|
|
||||||
// Skybox
|
// Skybox
|
||||||
shaderDataSkybox.projection = camera.matrices.perspective;
|
shaderDataSkybox.projection = camera.matrices.perspective;
|
||||||
shaderDataSkybox.view = camera.matrices.view;
|
shaderDataSkybox.view = camera.matrices.view;
|
||||||
shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view));
|
shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::updateShaderData()
|
void PlumageRender::renderMain::updateShaderData()
|
||||||
{
|
{
|
||||||
shaderData.lightDir = glm::vec4(
|
shaderData.lightDir = glm::vec4(
|
||||||
sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)),
|
sin(glm::radians(lightSource.rotation.x)) * cos(glm::radians(lightSource.rotation.y)),
|
||||||
|
@ -1309,7 +1113,7 @@ void PlumageRender::updateShaderData()
|
||||||
0.0f);
|
0.0f);
|
||||||
}
|
}
|
||||||
// todo:重写成glfw的
|
// todo:重写成glfw的
|
||||||
void PlumageRender::windowResized()
|
void PlumageRender::renderMain::windowResized()
|
||||||
{
|
{
|
||||||
buildCommandBuffers();
|
buildCommandBuffers();
|
||||||
vkDeviceWaitIdle(device);
|
vkDeviceWaitIdle(device);
|
||||||
|
@ -1318,71 +1122,40 @@ void PlumageRender::windowResized()
|
||||||
updateUIOverlay();
|
updateUIOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::prepare()
|
void PlumageRender::renderMain::prepare()
|
||||||
|
{
|
||||||
|
//VulkanExampleBase::prepare();
|
||||||
|
|
||||||
|
setupCamera();
|
||||||
|
|
||||||
|
loadAssets();
|
||||||
|
generateBRDFLUT();
|
||||||
|
generateCubemaps();
|
||||||
|
prepareUniformBuffers();
|
||||||
|
setupDescriptors();
|
||||||
|
preparePipelines();
|
||||||
|
if (!setter.settings.headless)
|
||||||
{
|
{
|
||||||
//VulkanExampleBase::prepare();
|
gui = new UI(vulkanDevice, renderPass, queue, pipelineCache, setter.settings.sampleCount);
|
||||||
|
updateUIOverlay();
|
||||||
camera.type = Camera::CameraType::lookat;
|
|
||||||
|
|
||||||
camera.setPerspective(45.0f, (float)setter.settings.width / (float)setter.settings.height, 0.1f, 256.0f);
|
|
||||||
camera.rotationSpeed = 0.25f;
|
|
||||||
camera.movementSpeed = 0.1f;
|
|
||||||
camera.setPosition({ 0.0f, 0.0f, -1.0f });
|
|
||||||
camera.setRotation({ 0.0f, 0.0f, 0.0f });
|
|
||||||
|
|
||||||
waitFences.resize(renderAhead);
|
|
||||||
presentCompleteSemaphores.resize(renderAhead);
|
|
||||||
renderCompleteSemaphores.resize(renderAhead);
|
|
||||||
commandBuffers.resize(swapChain.imageCount);
|
|
||||||
uniformBuffers.resize(swapChain.imageCount);
|
|
||||||
descriptorSets.resize(swapChain.imageCount);
|
|
||||||
// Command buffer execution fences
|
|
||||||
for (auto& waitFence : waitFences) {
|
|
||||||
VkFenceCreateInfo fenceCI{ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, nullptr, VK_FENCE_CREATE_SIGNALED_BIT };
|
|
||||||
VK_CHECK_RESULT(vkCreateFence(device, &fenceCI, nullptr, &waitFence));
|
|
||||||
}
|
|
||||||
if (!setter.settings.headless)
|
|
||||||
{
|
|
||||||
for (auto& semaphore : presentCompleteSemaphores) {
|
|
||||||
VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
|
|
||||||
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Queue ordering semaphores
|
|
||||||
|
|
||||||
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();
|
|
||||||
if (!setter.settings.headless)
|
|
||||||
{
|
|
||||||
gui = new UI(vulkanDevice, renderPass, queue, pipelineCache, setter.settings.sampleCount);
|
|
||||||
updateUIOverlay();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
buildCommandBuffers();
|
|
||||||
|
|
||||||
prepared = true;
|
|
||||||
}
|
}
|
||||||
|
buildCommandBuffers();
|
||||||
|
|
||||||
void PlumageRender::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue)
|
prepared = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlumageRender::renderMain::setupCamera()
|
||||||
|
{
|
||||||
|
camera.type = Camera::CameraType::lookat;
|
||||||
|
|
||||||
|
camera.setPerspective(45.0f, (float)setter.settings.width / (float)setter.settings.height, 0.1f, 256.0f);
|
||||||
|
camera.rotationSpeed = 0.25f;
|
||||||
|
camera.movementSpeed = 0.1f;
|
||||||
|
camera.setPosition({ 0.0f, 0.0f, -1.0f });
|
||||||
|
camera.setRotation({ 0.0f, 0.0f, 0.0f });
|
||||||
|
}
|
||||||
|
|
||||||
|
void PlumageRender::renderMain::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue)
|
||||||
{
|
{
|
||||||
VkSubmitInfo submitInfo = vks::initializers::submitInfo();
|
VkSubmitInfo submitInfo = vks::initializers::submitInfo();
|
||||||
submitInfo.commandBufferCount = 1;
|
submitInfo.commandBufferCount = 1;
|
||||||
|
@ -1397,7 +1170,7 @@ void PlumageRender::submitWork(VkCommandBuffer cmdBuffer, VkQueue queue)
|
||||||
|
|
||||||
// todo :根据physicalDeviceIndex确定子文件夹路径,frameIndex确定fileName
|
// todo :根据physicalDeviceIndex确定子文件夹路径,frameIndex确定fileName
|
||||||
// 移动到fileSystem里
|
// 移动到fileSystem里
|
||||||
void PlumageRender::writeImageToFile(std::string filePath)
|
void PlumageRender::renderMain::writeImageToFile(std::string filePath)
|
||||||
{
|
{
|
||||||
|
|
||||||
bool screenshotSaved = false;
|
bool screenshotSaved = false;
|
||||||
|
@ -1630,7 +1403,7 @@ void PlumageRender::writeImageToFile(std::string filePath)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::outputImageSequence()
|
void PlumageRender::renderMain::outputImageSequence()
|
||||||
{
|
{
|
||||||
// 比较已保存的帧数和设置里的开始帧数,在生成前清理上一次生成的图片序列
|
// 比较已保存的帧数和设置里的开始帧数,在生成前清理上一次生成的图片序列
|
||||||
if (savedFrameCounter == setter.settings.startFrameCount)
|
if (savedFrameCounter == setter.settings.startFrameCount)
|
||||||
|
@ -1641,7 +1414,7 @@ void PlumageRender::outputImageSequence()
|
||||||
// 根据显卡编号设置输出路径(todo:提前到配置里)
|
// 根据显卡编号设置输出路径(todo:提前到配置里)
|
||||||
setter.filePath.deviceSpecFilePath = setter.filePath.imageOutputPath + "/device" + std::to_string(selectedPhysicalDeviceIndex);
|
setter.filePath.deviceSpecFilePath = setter.filePath.imageOutputPath + "/device" + std::to_string(selectedPhysicalDeviceIndex);
|
||||||
// 非第一次生成,生成结束的边界条件
|
// 非第一次生成,生成结束的边界条件
|
||||||
if (savedFrameCounter > setter.settings.outputFrameCount)
|
if (savedFrameCounter > setter.settings.endFrameIndex)
|
||||||
{
|
{
|
||||||
// 避免重复改变为true带来的无效性能开销
|
// 避免重复改变为true带来的无效性能开销
|
||||||
if (signal.imageSequenceOutputComplete)
|
if (signal.imageSequenceOutputComplete)
|
||||||
|
@ -1670,7 +1443,7 @@ void PlumageRender::outputImageSequence()
|
||||||
savedFrameCounter++;
|
savedFrameCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::imageSequenceToVideo()
|
void PlumageRender::renderMain::imageSequenceToVideo()
|
||||||
{
|
{
|
||||||
// 边界条件,图片序列输出未完成
|
// 边界条件,图片序列输出未完成
|
||||||
if (!signal.imageSequenceOutputComplete)
|
if (!signal.imageSequenceOutputComplete)
|
||||||
|
@ -1710,7 +1483,7 @@ void PlumageRender::imageSequenceToVideo()
|
||||||
removeImageSequence();
|
removeImageSequence();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::removeImageSequence()
|
void PlumageRender::renderMain::removeImageSequence()
|
||||||
{
|
{
|
||||||
// 函数非第一次运行的边界条件
|
// 函数非第一次运行的边界条件
|
||||||
if (savedFrameCounter != setter.settings.startFrameCount)
|
if (savedFrameCounter != setter.settings.startFrameCount)
|
||||||
|
@ -1745,7 +1518,7 @@ void PlumageRender::removeImageSequence()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void PlumageRender::render()
|
void PlumageRender::renderMain::render()
|
||||||
{
|
{
|
||||||
if (!prepared) {
|
if (!prepared) {
|
||||||
return;
|
return;
|
||||||
|
@ -1780,7 +1553,7 @@ void PlumageRender::render()
|
||||||
|
|
||||||
// Update UBOs
|
// Update UBOs
|
||||||
updateUniformBuffers();
|
updateUniformBuffers();
|
||||||
UniformBufferSet currentUB = uniformBuffers[currentBuffer];
|
UniformBufferSet[currentUB] = uniformBuffers[currentBuffer];
|
||||||
memcpy(currentUB.scene.mapped, &shaderDataScene, sizeof(shaderDataScene));
|
memcpy(currentUB.scene.mapped, &shaderDataScene, sizeof(shaderDataScene));
|
||||||
memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData));
|
memcpy(currentUB.params.mapped, &shaderData, sizeof(shaderData));
|
||||||
memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox));
|
memcpy(currentUB.skybox.mapped, &shaderDataSkybox, sizeof(shaderDataSkybox));
|
||||||
|
@ -1838,7 +1611,7 @@ void PlumageRender::render()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlumageRender::fileDropped(std::string filename)
|
void PlumageRender::renderMain::fileDropped(std::string filename)
|
||||||
{
|
{
|
||||||
vkDeviceWaitIdle(device);
|
vkDeviceWaitIdle(device);
|
||||||
loadScene(filename);
|
loadScene(filename);
|
||||||
|
@ -1847,216 +1620,9 @@ void PlumageRender::fileDropped(std::string filename)
|
||||||
|
|
||||||
}
|
}
|
||||||
// 重构到单独的UI里
|
// 重构到单独的UI里
|
||||||
void PlumageRender::updateUIOverlay()
|
void PlumageRender::renderMain::updateUIOverlay()
|
||||||
{
|
{
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
|
||||||
|
|
||||||
ImVec2 lastDisplaySize = io.DisplaySize;
|
|
||||||
io.DisplaySize = ImVec2((float)setter.settings.width, (float)setter.settings.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;
|
|
||||||
bool boolTitleWindowShow = false;
|
|
||||||
ImGui::NewFrame();
|
|
||||||
|
|
||||||
ImGui::SetNextWindowPos(ImVec2(10000, 10000));
|
|
||||||
//ImGui::SetNextWindowSize(ImVec2(200 * scale, (models.scene.animations.size() > 0 ? 440 : 360) * scale), ImGuiSetCond_Always);
|
|
||||||
ImGui::Begin("", nullptr, ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoBringToFrontOnFocus);
|
|
||||||
|
|
||||||
ImGui::PushItemWidth(100.0f * scale);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(gui->beginMainMenuBar()) {
|
|
||||||
if (gui->beginMenu(chineseUI.menuFile))
|
|
||||||
{
|
|
||||||
if (gui->menuItem(chineseUI.menuOpenNewModel))
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
signal.imageSequenceOutputComplete = false;
|
|
||||||
signal.imageSequenceToVideoComplete = false;
|
|
||||||
savedFrameCounter = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
gui->endMenu();
|
|
||||||
}
|
|
||||||
if (gui->beginMenu(chineseUI.menuEnvironment))
|
|
||||||
{
|
|
||||||
if (gui->beginMenu(chineseUI.menuEnvironmentConfig))
|
|
||||||
{
|
|
||||||
if (gui->combo(chineseUI.environmentMap, selectedEnvironment, environments)) {
|
|
||||||
vkDeviceWaitIdle(device);
|
|
||||||
loadEnvironment(environments[selectedEnvironment]);
|
|
||||||
setupDescriptors();
|
|
||||||
updateCBs = true;
|
|
||||||
}
|
|
||||||
if (gui->checkbox(chineseUI.environmentBackGround, &displayBackground)) {
|
|
||||||
updateShaderParams = true;
|
|
||||||
}
|
|
||||||
if (gui->slider("Exposure", &shaderData.exposure, 0.1f, 10.0f)) {
|
|
||||||
updateShaderParams = true;
|
|
||||||
}
|
|
||||||
if (gui->slider("Gamma", &shaderData.gamma, 0.1f, 4.0f)) {
|
|
||||||
updateShaderParams = true;
|
|
||||||
}
|
|
||||||
if (gui->slider("IBL", &shaderData.scaleIBLAmbient, 0.0f, 1.0f)) {
|
|
||||||
updateShaderParams = true;
|
|
||||||
}
|
|
||||||
gui->endMenu();
|
|
||||||
}
|
|
||||||
gui->endMenu();
|
|
||||||
}
|
|
||||||
if (gui->beginMenu("debug"))
|
|
||||||
{
|
|
||||||
if (gui->beginMenu(chineseUI.menuDebugInput))
|
|
||||||
{
|
|
||||||
const std::vector<std::string> debugNamesInputs = {
|
|
||||||
"none", "Base color", "Normal", "Occlusion", "Emissive", "Metallic", "Roughness"
|
|
||||||
};
|
|
||||||
if (gui->combo(chineseUI.debugInput, &debugViewInputs, debugNamesInputs)) {
|
|
||||||
shaderData.debugViewInputs = 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(chineseUI.debugPBREquation, &debugViewEquation, debugNamesEquation)) {
|
|
||||||
shaderData.debugViewEquation = static_cast<float>(debugViewEquation);
|
|
||||||
updateShaderParams = true;
|
|
||||||
}
|
|
||||||
gui->endMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gui->beginMenu(chineseUI.menuDebugFrameRate))
|
|
||||||
{
|
|
||||||
gui->text("%.1d fps (%.2f ms)", lastFPS, (1000.0f / lastFPS));
|
|
||||||
gui->endMenu();
|
|
||||||
}
|
|
||||||
gui->endMenu();
|
|
||||||
}
|
|
||||||
if (gui->beginMenu(chineseUI.menuAnimation))
|
|
||||||
{
|
|
||||||
if (models.scene.animations.size() > 0)
|
|
||||||
{
|
|
||||||
if (gui->beginMenu(chineseUI.menuAnimationActivation))
|
|
||||||
{
|
|
||||||
gui->checkbox(chineseUI.pauseAnimation, &animate);
|
|
||||||
gui->endMenu();
|
|
||||||
}
|
|
||||||
if (gui->beginMenu(chineseUI.menuAnimationAnimationSequence))
|
|
||||||
{
|
|
||||||
std::vector<std::string> animationNames;
|
|
||||||
for (auto animation : models.scene.animations) {
|
|
||||||
animationNames.push_back(animation.name);
|
|
||||||
}
|
|
||||||
gui->combo(chineseUI.animationSeq, &animationIndex, animationNames);
|
|
||||||
gui->endMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
gui->text(chineseUI.menuAnimationNoAnimation);
|
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2096,13 +1662,14 @@ PlumageRender* plumageRender;
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
PlumageRender* plumageRender;
|
PlumageRender::renderMain* plumageRender;
|
||||||
VulkanBackend::Setter setter;
|
PlumageRender::Setter setter;
|
||||||
if (!setter.settings.headless)
|
if (!setter.settings.headless)
|
||||||
{
|
{
|
||||||
plumageRender->initWindow(setter.settings.width, setter.settings.height);
|
plumageRender->initWindow(setter.settings.width, setter.settings.height);
|
||||||
plumageRender->initVulkan();
|
|
||||||
}
|
}
|
||||||
|
plumageRender->initVulkan();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,260 +27,176 @@
|
||||||
#include "renderSetter.h"
|
#include "renderSetter.h"
|
||||||
#include "vulkanFoundation.h"
|
#include "vulkanFoundation.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include "stb_image_write.h"
|
#include "stb_image_write.h"
|
||||||
|
|
||||||
#define ENABLE_VALIDATION false
|
#define ENABLE_VALIDATION false
|
||||||
|
|
||||||
|
namespace PlumageRender
|
||||||
|
|
||||||
class PlumageRender : public VulkanExampleBase
|
|
||||||
{
|
{
|
||||||
public:
|
class renderMain : public VulkanExampleBase
|
||||||
|
|
||||||
GLFWwindow* window;
|
|
||||||
|
|
||||||
VulkanBackend::Setter setter;
|
|
||||||
|
|
||||||
bool wireframe = false;
|
|
||||||
bool normalMapping = true;
|
|
||||||
bool ToneMapping = true;
|
|
||||||
bool pbrEnabled = true;
|
|
||||||
|
|
||||||
struct stat
|
|
||||||
{
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
} info ;
|
GLFWwindow* window;
|
||||||
|
|
||||||
|
|
||||||
|
PlumageRender::Setter setter;
|
||||||
|
|
||||||
|
bool wireframe = false;
|
||||||
|
bool normalMapping = true;
|
||||||
|
bool ToneMapping = true;
|
||||||
|
bool pbrEnabled = true;
|
||||||
|
|
||||||
|
struct stat
|
||||||
|
{
|
||||||
|
|
||||||
|
} info;
|
||||||
|
|
||||||
|
struct Signal
|
||||||
|
{
|
||||||
|
bool imageSequenceOutputComplete = false;
|
||||||
|
bool imageSequenceToVideoComplete = false;
|
||||||
|
|
||||||
|
}signal;
|
||||||
|
|
||||||
struct Signal
|
|
||||||
{
|
|
||||||
bool imageSequenceOutputComplete = false;
|
|
||||||
bool imageSequenceToVideoComplete = false;
|
|
||||||
|
|
||||||
}signal;
|
|
||||||
|
|
||||||
|
|
||||||
|
glm::vec3 modelrot = glm::vec3(0.0f);
|
||||||
|
glm::vec3 modelPos = glm::vec3(0.0f);
|
||||||
|
|
||||||
struct Textures {
|
std::map<std::string, std::string> environments;
|
||||||
vks::TextureCubeMap environmentCube;
|
std::string selectedEnvironment = "papermill";
|
||||||
vks::Texture2D empty;
|
std::map<std::string, std::string> scenes;
|
||||||
vks::Texture2D lutBrdf;
|
std::string selectedScene = "DamagedHelmet";
|
||||||
vks::TextureCubeMap irradianceCube;
|
|
||||||
vks::TextureCubeMap prefilteredCube;
|
|
||||||
} textures;
|
|
||||||
|
|
||||||
struct ShaderData {
|
int32_t debugViewInputs = 0;
|
||||||
glm::vec4 lightDir;
|
int32_t debugViewEquation = 0;
|
||||||
float exposure = 4.5f;
|
|
||||||
float gamma = 2.2f;
|
|
||||||
float prefilteredCubeMipLevels;
|
|
||||||
float scaleIBLAmbient = 1.0f;
|
|
||||||
float debugViewInputs = 0;
|
|
||||||
float debugViewEquation = 0;
|
|
||||||
} shaderData;
|
|
||||||
struct ChinesesUI
|
|
||||||
{
|
|
||||||
const char * model = "模型";
|
|
||||||
|
|
||||||
const char* environmentMap = "环境贴图";
|
|
||||||
const char* environmentBackGround = "启用背景贴图";
|
|
||||||
const char* debugInput = "输入";
|
|
||||||
const char* debugPBREquation = "PBR计算参数";
|
|
||||||
const char* animation = "动画";
|
|
||||||
const char* pauseAnimation = "启用动画";
|
|
||||||
const char* animationSeq = "动画序列";
|
|
||||||
// menu item
|
|
||||||
const char* menuFile = "文件";
|
|
||||||
const char* menuOpenNewModel = "新模型..";
|
|
||||||
const char* menuEnvironment = "环境光照";
|
|
||||||
const char* menuEnvironmentConfig = "设置";
|
|
||||||
const char* menuAnimation = "动画";
|
|
||||||
const char* menuDebugFrameRate = "fps";
|
|
||||||
const char* menuDebugInput = "输入";
|
|
||||||
const char* menuAnimationNoAnimation = "当前模型没有动画!";
|
|
||||||
|
|
||||||
const char* menuAnimationActivation = "开关";
|
|
||||||
const char* menuAnimationAnimationSequence = "动画序列";
|
|
||||||
|
|
||||||
|
struct StagingBuffer {
|
||||||
|
VkBuffer buffer;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
} vertexStaging, indexStaging;
|
||||||
|
|
||||||
}chineseUI;
|
std::vector<VkCommandBuffer> commandBuffers;
|
||||||
struct UniformBufferSet {
|
|
||||||
Buffer scene;
|
uint32_t frameIndex = 0;
|
||||||
Buffer skybox;
|
|
||||||
Buffer params;
|
//VkImage swapChainImage;
|
||||||
|
|
||||||
|
int32_t animationIndex = 0;
|
||||||
|
float animationTimer = 0.0f;
|
||||||
|
bool animate = true;
|
||||||
|
|
||||||
|
bool displayBackground = true;
|
||||||
|
|
||||||
|
struct LightSource {
|
||||||
|
glm::vec3 color = glm::vec3(1.0f);
|
||||||
|
glm::vec3 rotation = glm::vec3(75.0f, 40.0f, 0.0f);
|
||||||
|
} lightSource;
|
||||||
|
|
||||||
|
//cube map generation
|
||||||
|
|
||||||
|
struct OffScreen
|
||||||
|
{
|
||||||
|
VkImage image;
|
||||||
|
VkImageView view;
|
||||||
|
VkDeviceMemory memory;
|
||||||
|
VkFramebuffer framebuffer;
|
||||||
|
} offscreen;
|
||||||
|
|
||||||
|
struct IrradiancePushBlock
|
||||||
|
{
|
||||||
|
glm::mat4 mvp;
|
||||||
|
// Sampling deltas
|
||||||
|
float deltaPhi = (2.0f * float(M_PI)) / 180.0f;
|
||||||
|
float deltaTheta = (0.5f * float(M_PI)) / 64.0f;
|
||||||
|
} irradiancePushBlock;
|
||||||
|
|
||||||
|
struct PrefilterPushBlock {
|
||||||
|
glm::mat4 mvp;
|
||||||
|
float roughness;
|
||||||
|
uint32_t numSamples = 32u;
|
||||||
|
} prefilterPushBlock;
|
||||||
|
|
||||||
|
UI* gui;
|
||||||
|
|
||||||
|
uint64_t savedFrameCounter = setter.settings.startFrameCount;
|
||||||
|
|
||||||
|
bool framebufferResized = false;
|
||||||
|
|
||||||
|
renderMain();
|
||||||
|
~renderMain()
|
||||||
|
{
|
||||||
|
// Clean up used Vulkan resources
|
||||||
|
// Note : Inherited destructor cleans up resources stored in base class
|
||||||
|
vkDestroyPipeline(device, pipelines.skybox, nullptr);
|
||||||
|
vkDestroyPipeline(device, pipelines.pbr, nullptr);
|
||||||
|
vkDestroyPipeline(device, pipelines.pbrAlphaBlend, nullptr);
|
||||||
|
|
||||||
|
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr);
|
||||||
|
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr);
|
||||||
|
|
||||||
|
models.scene.destroy(device);
|
||||||
|
models.skybox.destroy(device);
|
||||||
|
|
||||||
|
for (auto buffer : uniformBuffers) {
|
||||||
|
buffer.params.destroy();
|
||||||
|
buffer.scene.destroy();
|
||||||
|
buffer.skybox.destroy();
|
||||||
|
}
|
||||||
|
for (auto fence : waitFences) {
|
||||||
|
vkDestroyFence(device, fence, nullptr);
|
||||||
|
}
|
||||||
|
for (auto semaphore : renderCompleteSemaphores) {
|
||||||
|
vkDestroySemaphore(device, semaphore, nullptr);
|
||||||
|
}
|
||||||
|
for (auto semaphore : presentCompleteSemaphores) {
|
||||||
|
vkDestroySemaphore(device, semaphore, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
textures.environmentCube.destroy();
|
||||||
|
textures.irradianceCube.destroy();
|
||||||
|
textures.prefilteredCube.destroy();
|
||||||
|
textures.lutBrdf.destroy();
|
||||||
|
textures.empty.destroy();
|
||||||
|
delete gui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void initWindow(int width, int height);
|
||||||
|
|
||||||
|
static void framebufferResizeCallback(GLFWwindow* window, int width, int height);
|
||||||
|
void renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode);
|
||||||
|
void loadScene(std::string filename);
|
||||||
|
void loadEnvironment(std::string filename);
|
||||||
|
void buildCommandBuffers();
|
||||||
|
void loadAssets();
|
||||||
|
void setupNodeDescriptorSet(glTFModel::Node* node);
|
||||||
|
void setupDescriptors();
|
||||||
|
void preparePipelines();
|
||||||
|
// void tonemappingPipelin();
|
||||||
|
void generateCubemaps();
|
||||||
|
void generateBRDFLUT();
|
||||||
|
void prepareUniformBuffers();
|
||||||
|
void updateUniformBuffers();
|
||||||
|
void updateShaderData();
|
||||||
|
void windowResized();
|
||||||
|
void prepare();
|
||||||
|
void setupCamera();
|
||||||
|
void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue);
|
||||||
|
|
||||||
|
void writeImageToFile(std::string filePath);
|
||||||
|
void outputImageSequence();
|
||||||
|
void imageSequenceToVideo();
|
||||||
|
void removeImageSequence();
|
||||||
|
//void outputScreenShot();
|
||||||
|
//uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties);
|
||||||
|
virtual void render();
|
||||||
|
virtual void updateUIOverlay();
|
||||||
|
virtual void fileDropped(std::string filename);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct UBOMatrices {
|
}
|
||||||
glm::mat4 projection;
|
|
||||||
glm::mat4 model;
|
|
||||||
glm::mat4 view;
|
|
||||||
glm::vec3 camPos;
|
|
||||||
} shaderDataScene, shaderDataSkybox;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
glm::vec3 modelrot = glm::vec3(0.0f);
|
|
||||||
glm::vec3 modelPos = glm::vec3(0.0f);
|
|
||||||
|
|
||||||
|
|
||||||
enum PBRWorkflows { PBR_WORKFLOW_METALLIC_ROUGHNESS = 0, PBR_WORKFLOW_SPECULAR_GLOSINESS = 1 };
|
|
||||||
|
|
||||||
|
|
||||||
std::map<std::string, std::string> environments;
|
|
||||||
std::string selectedEnvironment = "papermill";
|
|
||||||
std::map<std::string, std::string> scenes;
|
|
||||||
std::string selectedScene = "DamagedHelmet";
|
|
||||||
|
|
||||||
int32_t debugViewInputs = 0;
|
|
||||||
int32_t debugViewEquation = 0;
|
|
||||||
|
|
||||||
struct StagingBuffer {
|
|
||||||
VkBuffer buffer;
|
|
||||||
VkDeviceMemory memory;
|
|
||||||
} vertexStaging, indexStaging;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct DescriptorSets {
|
|
||||||
VkDescriptorSet scene;
|
|
||||||
VkDescriptorSet skybox;
|
|
||||||
VkDescriptorSet tonemappingDescriptorSet = VK_NULL_HANDLE;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<DescriptorSets> descriptorSets;
|
|
||||||
|
|
||||||
std::vector<VkCommandBuffer> commandBuffers;
|
|
||||||
std::vector<UniformBufferSet> uniformBuffers;
|
|
||||||
|
|
||||||
std::vector<VkFence> waitFences;
|
|
||||||
std::vector<VkSemaphore> renderCompleteSemaphores;
|
|
||||||
std::vector<VkSemaphore> presentCompleteSemaphores;
|
|
||||||
|
|
||||||
const uint32_t renderAhead = 2;
|
|
||||||
uint32_t frameIndex = 0;
|
|
||||||
|
|
||||||
//VkImage swapChainImage;
|
|
||||||
|
|
||||||
int32_t animationIndex = 0;
|
|
||||||
float animationTimer = 0.0f;
|
|
||||||
bool animate = true;
|
|
||||||
|
|
||||||
bool displayBackground = true;
|
|
||||||
|
|
||||||
struct LightSource {
|
|
||||||
glm::vec3 color = glm::vec3(1.0f);
|
|
||||||
glm::vec3 rotation = glm::vec3(75.0f, 40.0f, 0.0f);
|
|
||||||
} lightSource;
|
|
||||||
|
|
||||||
|
|
||||||
//cube map generation
|
|
||||||
|
|
||||||
struct OffScreen
|
|
||||||
{
|
|
||||||
VkImage image;
|
|
||||||
VkImageView view;
|
|
||||||
VkDeviceMemory memory;
|
|
||||||
VkFramebuffer framebuffer;
|
|
||||||
} offscreen;
|
|
||||||
|
|
||||||
struct IrradiancePushBlock
|
|
||||||
{
|
|
||||||
glm::mat4 mvp;
|
|
||||||
// Sampling deltas
|
|
||||||
float deltaPhi = (2.0f * float(M_PI)) / 180.0f;
|
|
||||||
float deltaTheta = (0.5f * float(M_PI)) / 64.0f;
|
|
||||||
} irradiancePushBlock;
|
|
||||||
|
|
||||||
struct PrefilterPushBlock {
|
|
||||||
glm::mat4 mvp;
|
|
||||||
float roughness;
|
|
||||||
uint32_t numSamples = 32u;
|
|
||||||
} prefilterPushBlock;
|
|
||||||
|
|
||||||
UI* gui;
|
|
||||||
|
|
||||||
uint64_t savedFrameCounter = setter.settings.startFrameCount;
|
|
||||||
|
|
||||||
bool framebufferResized = false;
|
|
||||||
|
|
||||||
PlumageRender();
|
|
||||||
~PlumageRender()
|
|
||||||
{
|
|
||||||
// Clean up used Vulkan resources
|
|
||||||
// Note : Inherited destructor cleans up resources stored in base class
|
|
||||||
vkDestroyPipeline(device, pipelines.skybox, nullptr);
|
|
||||||
vkDestroyPipeline(device, pipelines.pbr, nullptr);
|
|
||||||
vkDestroyPipeline(device, pipelines.pbrAlphaBlend, nullptr);
|
|
||||||
|
|
||||||
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
|
|
||||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.scene, nullptr);
|
|
||||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.material, nullptr);
|
|
||||||
vkDestroyDescriptorSetLayout(device, descriptorSetLayouts.node, nullptr);
|
|
||||||
|
|
||||||
models.scene.destroy(device);
|
|
||||||
models.skybox.destroy(device);
|
|
||||||
|
|
||||||
for (auto buffer : uniformBuffers) {
|
|
||||||
buffer.params.destroy();
|
|
||||||
buffer.scene.destroy();
|
|
||||||
buffer.skybox.destroy();
|
|
||||||
}
|
|
||||||
for (auto fence : waitFences) {
|
|
||||||
vkDestroyFence(device, fence, nullptr);
|
|
||||||
}
|
|
||||||
for (auto semaphore : renderCompleteSemaphores) {
|
|
||||||
vkDestroySemaphore(device, semaphore, nullptr);
|
|
||||||
}
|
|
||||||
for (auto semaphore : presentCompleteSemaphores) {
|
|
||||||
vkDestroySemaphore(device, semaphore, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
textures.environmentCube.destroy();
|
|
||||||
textures.irradianceCube.destroy();
|
|
||||||
textures.prefilteredCube.destroy();
|
|
||||||
textures.lutBrdf.destroy();
|
|
||||||
textures.empty.destroy();
|
|
||||||
delete gui;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void initWindow(int width, int height);
|
|
||||||
|
|
||||||
static void framebufferResizeCallback(GLFWwindow* window, int width, int height);
|
|
||||||
void renderNode(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode);
|
|
||||||
void loadScene(std::string filename);
|
|
||||||
void loadEnvironment(std::string filename);
|
|
||||||
void buildCommandBuffers();
|
|
||||||
void loadAssets();
|
|
||||||
void setupNodeDescriptorSet(glTFModel::Node* node);
|
|
||||||
void setupDescriptors();
|
|
||||||
void preparePipelines();
|
|
||||||
// void tonemappingPipelin();
|
|
||||||
void generateCubemaps();
|
|
||||||
void generateBRDFLUT();
|
|
||||||
void prepareUniformBuffers();
|
|
||||||
void updateUniformBuffers();
|
|
||||||
void updateShaderData();
|
|
||||||
void windowResized();
|
|
||||||
void prepare();
|
|
||||||
void submitWork(VkCommandBuffer cmdBuffer, VkQueue queue);
|
|
||||||
|
|
||||||
void writeImageToFile(std::string filePath);
|
|
||||||
void outputImageSequence();
|
|
||||||
void imageSequenceToVideo();
|
|
||||||
void removeImageSequence();
|
|
||||||
//void outputScreenShot();
|
|
||||||
//uint32_t getMemoryTypeIndex(uint32_t typeBits, VkMemoryPropertyFlags properties);
|
|
||||||
virtual void render();
|
|
||||||
virtual void updateUIOverlay();
|
|
||||||
virtual void fileDropped(std::string filename);
|
|
||||||
};
|
|
|
@ -0,0 +1 @@
|
||||||
|
#pragma once
|
|
@ -1,6 +1,6 @@
|
||||||
#include "renderSetter.h"
|
#include "renderSetter.h"
|
||||||
|
|
||||||
void VulkanBackend::Setter::getSettingFromCommandLine()
|
void PlumageRender::Setter::getSettingFromCommandLine()
|
||||||
{
|
{
|
||||||
// 暂定
|
// 暂定
|
||||||
char* numConvPtr;
|
char* numConvPtr;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "VulkanTools.h"
|
#include "VulkanTools.h"
|
||||||
|
|
||||||
|
|
||||||
namespace VulkanBackend
|
namespace PlumageRender
|
||||||
{
|
{
|
||||||
class Setter
|
class Setter
|
||||||
{
|
{
|
||||||
|
@ -27,11 +27,13 @@ namespace VulkanBackend
|
||||||
bool fullscreen = false; // 全屏开关
|
bool fullscreen = false; // 全屏开关
|
||||||
bool vsync = false; // 垂直同步开关
|
bool vsync = false; // 垂直同步开关
|
||||||
bool multiSampling = true; // 多重采样
|
bool multiSampling = true; // 多重采样
|
||||||
|
bool displayBackground = true; // IBL显示背景图
|
||||||
bool rotateModel = true; // 模型自旋转(暂时失效)
|
bool rotateModel = true; // 模型自旋转(暂时失效)
|
||||||
bool headless = true; // 无头开关
|
bool headless = true; // 无头开关
|
||||||
bool outputPNGimage = false; // 输出图片序列格式为PNG(四通道)
|
bool outputPNGimage = false; // 输出图片序列格式为PNG(四通道)
|
||||||
bool enableSaveToImageSequeue = false; // 图片序列开关(暂时弃用)
|
bool enableSaveToImageSequeue = false; // 图片序列开关(暂时弃用)
|
||||||
uint32_t outputFrameCount = 75; // 图片序列结束帧
|
uint32_t MaxFrameInFlight = 2; // 最大并行渲染帧数(通常为2,此时CPU和GPU并行处理)
|
||||||
|
uint32_t endFrameIndex = 75; // 图片序列结束帧
|
||||||
bool enableIMGUI = false; // gui使用imgui
|
bool enableIMGUI = false; // gui使用imgui
|
||||||
uint32_t startFrameCount = 1; // 图片序列开始帧
|
uint32_t startFrameCount = 1; // 图片序列开始帧
|
||||||
uint32_t videoFrameRate = 25; // 视频帧率
|
uint32_t videoFrameRate = 25; // 视频帧率
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
#include "renderUI.h"
|
||||||
|
#include <codecvt>
|
||||||
|
|
||||||
|
|
||||||
|
void PlumageRender::PlumageGUI::updateUIOverlay()
|
||||||
|
{
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
ImVec2 lastDisplaySize = io.DisplaySize;
|
||||||
|
io.DisplaySize = ImVec2((float)setter.settings.width, (float)setter.settings.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;
|
||||||
|
bool boolTitleWindowShow = false;
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(10000, 10000));
|
||||||
|
//ImGui::SetNextWindowSize(ImVec2(200 * scale, (models.scene.animations.size() > 0 ? 440 : 360) * scale), ImGuiSetCond_Always);
|
||||||
|
ImGui::Begin("", nullptr, ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||||
|
|
||||||
|
ImGui::PushItemWidth(100.0f * scale);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (gui->beginMainMenuBar()) {
|
||||||
|
if (gui->beginMenu(chineseUI.menuFile))
|
||||||
|
{
|
||||||
|
if (gui->menuItem(chineseUI.menuOpenNewModel))
|
||||||
|
{
|
||||||
|
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/glb file to load";
|
||||||
|
ofn.Flags = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR;
|
||||||
|
if (GetOpenFileNameW(&ofn)) {
|
||||||
|
filename = buffer;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filename.empty()) {
|
||||||
|
vkDeviceWaitIdle(vulkanBasic.device);
|
||||||
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
|
||||||
|
std::string stringFilename = converter.to_bytes(filename);
|
||||||
|
loadScene(stringFilename);
|
||||||
|
setupDescriptors();
|
||||||
|
updateCBs = true;
|
||||||
|
signal.imageSequenceOutputComplete = false;
|
||||||
|
signal.imageSequenceToVideoComplete = false;
|
||||||
|
savedFrameCounter = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gui->endMenu();
|
||||||
|
}
|
||||||
|
if (gui->beginMenu(chineseUI.menuEnvironment))
|
||||||
|
{
|
||||||
|
if (gui->beginMenu(chineseUI.menuEnvironmentConfig))
|
||||||
|
{
|
||||||
|
if (gui->combo(chineseUI.environmentMap, selectedEnvironment, environments)) {
|
||||||
|
vkDeviceWaitIdle(device);
|
||||||
|
loadEnvironment(environments[selectedEnvironment]);
|
||||||
|
setupDescriptors();
|
||||||
|
updateCBs = true;
|
||||||
|
}
|
||||||
|
if (gui->checkbox(chineseUI.environmentBackGround, &setter.settings.displayBackground)) {
|
||||||
|
updateShaderParams = true;
|
||||||
|
}
|
||||||
|
if (gui->slider("Exposure", &shaderData.exposure, 0.1f, 10.0f)) {
|
||||||
|
updateShaderParams = true;
|
||||||
|
}
|
||||||
|
if (gui->slider("Gamma", &shaderData.gamma, 0.1f, 4.0f)) {
|
||||||
|
updateShaderParams = true;
|
||||||
|
}
|
||||||
|
if (gui->slider("IBL", &shaderData.scaleIBLAmbient, 0.0f, 1.0f)) {
|
||||||
|
updateShaderParams = true;
|
||||||
|
}
|
||||||
|
gui->endMenu();
|
||||||
|
}
|
||||||
|
gui->endMenu();
|
||||||
|
}
|
||||||
|
if (gui->beginMenu("debug"))
|
||||||
|
{
|
||||||
|
if (gui->beginMenu(chineseUI.menuDebugInput))
|
||||||
|
{
|
||||||
|
const std::vector<std::string> debugNamesInputs = {
|
||||||
|
"none", "Base color", "Normal", "Occlusion", "Emissive", "Metallic", "Roughness"
|
||||||
|
};
|
||||||
|
if (gui->combo(chineseUI.debugInput, &debugViewInputs, debugNamesInputs)) {
|
||||||
|
shaderData.debugViewInputs = 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(chineseUI.debugPBREquation, &debugViewEquation, debugNamesEquation)) {
|
||||||
|
shaderData.debugViewEquation = static_cast<float>(debugViewEquation);
|
||||||
|
updateShaderParams = true;
|
||||||
|
}
|
||||||
|
gui->endMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gui->beginMenu(chineseUI.menuDebugFrameRate))
|
||||||
|
{
|
||||||
|
gui->text("%.1d fps (%.2f ms)", lastFPS, (1000.0f / lastFPS));
|
||||||
|
gui->endMenu();
|
||||||
|
}
|
||||||
|
gui->endMenu();
|
||||||
|
}
|
||||||
|
if (gui->beginMenu(chineseUI.menuAnimation))
|
||||||
|
{
|
||||||
|
if (models.scene.animations.size() > 0)
|
||||||
|
{
|
||||||
|
if (gui->beginMenu(chineseUI.menuAnimationActivation))
|
||||||
|
{
|
||||||
|
gui->checkbox(chineseUI.pauseAnimation, &animate);
|
||||||
|
gui->endMenu();
|
||||||
|
}
|
||||||
|
if (gui->beginMenu(chineseUI.menuAnimationAnimationSequence))
|
||||||
|
{
|
||||||
|
std::vector<std::string> animationNames;
|
||||||
|
for (auto animation : models.scene.animations) {
|
||||||
|
animationNames.push_back(animation.name);
|
||||||
|
}
|
||||||
|
gui->combo(chineseUI.animationSeq, &animationIndex, animationNames);
|
||||||
|
gui->endMenu();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gui->text(chineseUI.menuAnimationNoAnimation);
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include "ui.hpp"
|
||||||
|
#include "VulkanDevice.hpp"
|
||||||
|
#include "vulkanFoundation.h"
|
||||||
|
#include "renderSetter.h"
|
||||||
|
|
||||||
|
namespace PlumageRender
|
||||||
|
{
|
||||||
|
class PlumageGUI
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PlumageGUI();
|
||||||
|
~PlumageGUI();
|
||||||
|
|
||||||
|
UI* gui;
|
||||||
|
|
||||||
|
void updateUIOverlay();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
PlumageRender::Setter setter;
|
||||||
|
VulkanBackend::VulkanFoundation vulkanBasic;
|
||||||
|
|
||||||
|
float frameTimer = 1.0f;
|
||||||
|
|
||||||
|
struct GamePadState {
|
||||||
|
glm::vec2 axisLeft = glm::vec2(0.0f);
|
||||||
|
glm::vec2 axisRight = glm::vec2(0.0f);
|
||||||
|
} gamePadState;
|
||||||
|
|
||||||
|
struct MouseButtons {
|
||||||
|
bool left = false;
|
||||||
|
bool right = false;
|
||||||
|
bool middle = false;
|
||||||
|
} mouseButtons;
|
||||||
|
|
||||||
|
glm::vec2 mousePos;
|
||||||
|
|
||||||
|
struct ChinesesUI
|
||||||
|
{
|
||||||
|
const char* model = "模型";
|
||||||
|
|
||||||
|
const char* environmentMap = "环境贴图";
|
||||||
|
const char* environmentBackGround = "启用背景贴图";
|
||||||
|
const char* debugInput = "输入";
|
||||||
|
const char* debugPBREquation = "PBR计算参数";
|
||||||
|
const char* animation = "动画";
|
||||||
|
const char* pauseAnimation = "启用动画";
|
||||||
|
const char* animationSeq = "动画序列";
|
||||||
|
// menu item
|
||||||
|
const char* menuFile = "文件";
|
||||||
|
const char* menuOpenNewModel = "新模型..";
|
||||||
|
const char* menuEnvironment = "环境光照";
|
||||||
|
const char* menuEnvironmentConfig = "设置";
|
||||||
|
const char* menuAnimation = "动画";
|
||||||
|
const char* menuDebugFrameRate = "fps";
|
||||||
|
const char* menuDebugInput = "输入";
|
||||||
|
const char* menuAnimationNoAnimation = "当前模型没有动画!";
|
||||||
|
|
||||||
|
const char* menuAnimationActivation = "开关";
|
||||||
|
const char* menuAnimationAnimationSequence = "动画序列";
|
||||||
|
|
||||||
|
|
||||||
|
}chineseUI;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
PlumageGUI::PlumageGUI()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
PlumageGUI::~PlumageGUI()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#include "vulkanFoundation.h"
|
#include "vulkanFoundation.h"
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::initVulkan()
|
void VulkanBackend::VulkanFoundation::initVulkan()
|
||||||
{
|
{
|
||||||
// 创建instance
|
// 创建instance
|
||||||
createInstance();
|
createInstance();
|
||||||
|
@ -42,11 +42,6 @@ void VulkanBackend::VulkanFondation::initVulkan()
|
||||||
// 创建命令缓冲池
|
// 创建命令缓冲池
|
||||||
createCommandPool();
|
createCommandPool();
|
||||||
|
|
||||||
// 创建顶点缓存
|
|
||||||
createVertexBuffer();
|
|
||||||
|
|
||||||
// 创建索引缓存区
|
|
||||||
createIndexBuffer();
|
|
||||||
|
|
||||||
// 创建统一缓存区
|
// 创建统一缓存区
|
||||||
createUniformBuffer();
|
createUniformBuffer();
|
||||||
|
@ -57,6 +52,9 @@ void VulkanBackend::VulkanFondation::initVulkan()
|
||||||
// 创建资源描述符集
|
// 创建资源描述符集
|
||||||
createDescriptorSets();
|
createDescriptorSets();
|
||||||
|
|
||||||
|
// 分配命令缓存区
|
||||||
|
allocateCommandBuffers();
|
||||||
|
|
||||||
// 创建命令缓存区
|
// 创建命令缓存区
|
||||||
createCommandBuffer();
|
createCommandBuffer();
|
||||||
}
|
}
|
||||||
|
@ -64,7 +62,7 @@ void VulkanBackend::VulkanFondation::initVulkan()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createInstance()
|
void VulkanBackend::VulkanFoundation::createInstance()
|
||||||
{
|
{
|
||||||
// check validation layers
|
// check validation layers
|
||||||
if (setter.settings.validation && !checkValidationLayerSupport())
|
if (setter.settings.validation && !checkValidationLayerSupport())
|
||||||
|
@ -115,7 +113,7 @@ void VulkanBackend::VulkanFondation::createInstance()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanBackend::VulkanFondation::checkValidationLayerSupport()
|
bool VulkanBackend::VulkanFoundation::checkValidationLayerSupport()
|
||||||
{
|
{
|
||||||
uint32_t layerCount;
|
uint32_t layerCount;
|
||||||
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
vkEnumerateInstanceLayerProperties(&layerCount, nullptr);
|
||||||
|
@ -142,7 +140,7 @@ bool VulkanBackend::VulkanFondation::checkValidationLayerSupport()
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<const char*> VulkanBackend::VulkanFondation::getRequiredExtensions()
|
std::vector<const char*> VulkanBackend::VulkanFoundation::getRequiredExtensions()
|
||||||
{
|
{
|
||||||
std::vector<const char*> extensions;
|
std::vector<const char*> extensions;
|
||||||
if (!setter.settings.headless)
|
if (!setter.settings.headless)
|
||||||
|
@ -165,7 +163,7 @@ std::vector<const char*> VulkanBackend::VulkanFondation::getRequiredExtensions()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::setupDebugMessager()
|
void VulkanBackend::VulkanFoundation::setupDebugMessager()
|
||||||
{
|
{
|
||||||
if (!setter.settings.validation)
|
if (!setter.settings.validation)
|
||||||
{
|
{
|
||||||
|
@ -182,7 +180,7 @@ void VulkanBackend::VulkanFondation::setupDebugMessager()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& debugCreateInfo)
|
void VulkanBackend::VulkanFoundation::populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& debugCreateInfo)
|
||||||
{
|
{
|
||||||
debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||||
|
|
||||||
|
@ -196,7 +194,7 @@ void VulkanBackend::VulkanFondation::populateDebugMessengerCreateInfo(VkDebugUti
|
||||||
debugCreateInfo.pUserData = nullptr;
|
debugCreateInfo.pUserData = nullptr;
|
||||||
}
|
}
|
||||||
// debugCallback用于校验层
|
// debugCallback用于校验层
|
||||||
VKAPI_ATTR VkBool32 VKAPI_CALL VulkanBackend::VulkanFondation::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
|
VKAPI_ATTR VkBool32 VKAPI_CALL VulkanBackend::VulkanFoundation::debugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageTypes, const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
|
std::cerr << "validation layer: " << pCallbackData->pMessage << std::endl;
|
||||||
|
@ -205,7 +203,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanBackend::VulkanFondation::debugCallback(VkD
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VkResult VulkanBackend::VulkanFondation::CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
|
VkResult VulkanBackend::VulkanFoundation::CreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger)
|
||||||
{
|
{
|
||||||
auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
auto func = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
|
||||||
|
|
||||||
|
@ -221,7 +219,7 @@ VkResult VulkanBackend::VulkanFondation::CreateDebugUtilsMessengerEXT(VkInstance
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createSurface()
|
void VulkanBackend::VulkanFoundation::createSurface()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (setter.settings.headless)
|
if (setter.settings.headless)
|
||||||
|
@ -237,7 +235,7 @@ void VulkanBackend::VulkanFondation::createSurface()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::pickPhysicalDevice()
|
void VulkanBackend::VulkanFoundation::pickPhysicalDevice()
|
||||||
{
|
{
|
||||||
|
|
||||||
uint32_t deviceCount = 0;
|
uint32_t deviceCount = 0;
|
||||||
|
@ -269,7 +267,7 @@ void VulkanBackend::VulkanFondation::pickPhysicalDevice()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanBackend::VulkanFondation::isDeviceSuitable(VkPhysicalDevice device)
|
bool VulkanBackend::VulkanFoundation::isDeviceSuitable(VkPhysicalDevice device)
|
||||||
{
|
{
|
||||||
if (setter.settings.headless)
|
if (setter.settings.headless)
|
||||||
{
|
{
|
||||||
|
@ -289,7 +287,7 @@ bool VulkanBackend::VulkanFondation::isDeviceSuitable(VkPhysicalDevice device)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanBackend::VulkanFondation::checkDeviceExtensionSupport(VkPhysicalDevice device)
|
bool VulkanBackend::VulkanFoundation::checkDeviceExtensionSupport(VkPhysicalDevice device)
|
||||||
{
|
{
|
||||||
uint32_t extensionCount;
|
uint32_t extensionCount;
|
||||||
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
|
vkEnumerateDeviceExtensionProperties(device, nullptr, &extensionCount, nullptr);
|
||||||
|
@ -308,7 +306,7 @@ bool VulkanBackend::VulkanFondation::checkDeviceExtensionSupport(VkPhysicalDevic
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanBackend::VulkanFondation::QueueFamilyIndices VulkanBackend::VulkanFondation::findQueueFamilies(VkPhysicalDevice device)
|
VulkanBackend::VulkanFoundation::QueueFamilyIndices VulkanBackend::VulkanFoundation::findQueueFamilies(VkPhysicalDevice device)
|
||||||
{
|
{
|
||||||
QueueFamilyIndices indices;
|
QueueFamilyIndices indices;
|
||||||
|
|
||||||
|
@ -357,7 +355,7 @@ VulkanBackend::VulkanFondation::QueueFamilyIndices VulkanBackend::VulkanFondatio
|
||||||
return indices;
|
return indices;
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanBackend::VulkanFondation::SwapChainSupportDetails VulkanBackend::VulkanFondation::querySwapChainSupport(VkPhysicalDevice device)
|
VulkanBackend::VulkanFoundation::SwapChainSupportDetails VulkanBackend::VulkanFoundation::querySwapChainSupport(VkPhysicalDevice device)
|
||||||
{
|
{
|
||||||
// 获得surface细节
|
// 获得surface细节
|
||||||
SwapChainSupportDetails details;
|
SwapChainSupportDetails details;
|
||||||
|
@ -387,7 +385,7 @@ VulkanBackend::VulkanFondation::SwapChainSupportDetails VulkanBackend::VulkanFon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createLogicalDevice()
|
void VulkanBackend::VulkanFoundation::createLogicalDevice()
|
||||||
{
|
{
|
||||||
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
|
QueueFamilyIndices indices = findQueueFamilies(physicalDevice);
|
||||||
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
|
||||||
|
@ -467,7 +465,7 @@ void VulkanBackend::VulkanFondation::createLogicalDevice()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createSwapChain()
|
void VulkanBackend::VulkanFoundation::createSwapChain()
|
||||||
{
|
{
|
||||||
|
|
||||||
if (setter.settings.headless)
|
if (setter.settings.headless)
|
||||||
|
@ -535,7 +533,7 @@ void VulkanBackend::VulkanFondation::createSwapChain()
|
||||||
swapChainExtent = extent;
|
swapChainExtent = extent;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkSurfaceFormatKHR VulkanBackend::VulkanFondation::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats)
|
VkSurfaceFormatKHR VulkanBackend::VulkanFoundation::chooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats)
|
||||||
{
|
{
|
||||||
for (const auto& availableFormat : availableFormats) {
|
for (const auto& availableFormat : availableFormats) {
|
||||||
if (availableFormat.format == VK_FORMAT_B8G8R8_SRGB && availableFormat.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR)
|
if (availableFormat.format == VK_FORMAT_B8G8R8_SRGB && availableFormat.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR)
|
||||||
|
@ -547,7 +545,7 @@ VkSurfaceFormatKHR VulkanBackend::VulkanFondation::chooseSwapSurfaceFormat(const
|
||||||
return availableFormats[0];
|
return availableFormats[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
VkPresentModeKHR VulkanBackend::VulkanFondation::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes)
|
VkPresentModeKHR VulkanBackend::VulkanFoundation::chooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes)
|
||||||
{
|
{
|
||||||
// Get available present modes
|
// Get available present modes
|
||||||
uint32_t presentModeCount;
|
uint32_t presentModeCount;
|
||||||
|
@ -580,7 +578,7 @@ VkPresentModeKHR VulkanBackend::VulkanFondation::chooseSwapPresentMode(const std
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VkExtent2D VulkanBackend::VulkanFondation::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities)
|
VkExtent2D VulkanBackend::VulkanFoundation::chooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities)
|
||||||
{
|
{
|
||||||
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
|
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max())
|
||||||
{
|
{
|
||||||
|
@ -604,8 +602,8 @@ VkExtent2D VulkanBackend::VulkanFondation::chooseSwapExtent(const VkSurfaceCapab
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// toDo 重写
|
|
||||||
void VulkanBackend::VulkanFondation::createImageView()
|
void VulkanBackend::VulkanFoundation::createImageView()
|
||||||
{
|
{
|
||||||
VkFormat colorAttachmentFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
VkFormat colorAttachmentFormat = VK_FORMAT_R8G8B8A8_UNORM;
|
||||||
VkFormat depthFormat = findDepthFormat();
|
VkFormat depthFormat = findDepthFormat();
|
||||||
|
@ -833,7 +831,7 @@ void VulkanBackend::VulkanFondation::createImageView()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createRenderPass()
|
void VulkanBackend::VulkanFoundation::createRenderPass()
|
||||||
{
|
{
|
||||||
VkFormat colorAttachmentFormat;
|
VkFormat colorAttachmentFormat;
|
||||||
VkFormat depthAttachmentFormat = findDepthFormat();
|
VkFormat depthAttachmentFormat = findDepthFormat();
|
||||||
|
@ -1013,7 +1011,7 @@ void VulkanBackend::VulkanFondation::createRenderPass()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFormat VulkanBackend::VulkanFondation::findDepthFormat()
|
VkFormat VulkanBackend::VulkanFoundation::findDepthFormat()
|
||||||
{
|
{
|
||||||
return findSupportedFormat(
|
return findSupportedFormat(
|
||||||
{ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
|
{ VK_FORMAT_D32_SFLOAT, VK_FORMAT_D32_SFLOAT_S8_UINT, VK_FORMAT_D24_UNORM_S8_UINT },
|
||||||
|
@ -1022,7 +1020,7 @@ VkFormat VulkanBackend::VulkanFondation::findDepthFormat()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
VkFormat VulkanBackend::VulkanFondation::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
|
VkFormat VulkanBackend::VulkanFoundation::findSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features)
|
||||||
{
|
{
|
||||||
for (VkFormat format : candidates)
|
for (VkFormat format : candidates)
|
||||||
{
|
{
|
||||||
|
@ -1041,14 +1039,14 @@ VkFormat VulkanBackend::VulkanFondation::findSupportedFormat(const std::vector<V
|
||||||
throw std::runtime_error("failed to find supported format");
|
throw std::runtime_error("failed to find supported format");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanBackend::VulkanFondation::hasStencilComponent(VkFormat format)
|
bool VulkanBackend::VulkanFoundation::hasStencilComponent(VkFormat format)
|
||||||
{
|
{
|
||||||
return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
|
return format == VK_FORMAT_D32_SFLOAT_S8_UINT || format == VK_FORMAT_D24_UNORM_S8_UINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createDescriptorSetLayout()
|
void VulkanBackend::VulkanFoundation::createDescriptorSetLayout()
|
||||||
{
|
{
|
||||||
// scene,场景的资源描述符
|
// scene,场景的资源描述符
|
||||||
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
|
||||||
|
@ -1091,7 +1089,7 @@ void VulkanBackend::VulkanFondation::createDescriptorSetLayout()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createPipelineCache()
|
void VulkanBackend::VulkanFoundation::createPipelineCache()
|
||||||
{
|
{
|
||||||
VkPipelineCacheCreateInfo pipelineCacheCreateInfo{};
|
VkPipelineCacheCreateInfo pipelineCacheCreateInfo{};
|
||||||
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
pipelineCacheCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
|
||||||
|
@ -1100,7 +1098,7 @@ void VulkanBackend::VulkanFondation::createPipelineCache()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createGraphicPipeline()
|
void VulkanBackend::VulkanFoundation::createGraphicPipeline()
|
||||||
{
|
{
|
||||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{};
|
VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCI{};
|
||||||
inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
inputAssemblyStateCI.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||||
|
@ -1248,43 +1246,50 @@ void VulkanBackend::VulkanFondation::createGraphicPipeline()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createFramebuffer()
|
void VulkanBackend::VulkanFoundation::createFramebuffer()
|
||||||
{
|
{
|
||||||
if (setter.settings.headless)
|
if (setter.settings.headless)
|
||||||
{
|
{
|
||||||
if (setter.settings.multiSampling)
|
if (setter.settings.multiSampling)
|
||||||
{
|
{
|
||||||
VkImageView attachments[4];
|
for (int i = 0; i < frameRange; i++)
|
||||||
attachments[0] = multisampleTarget.colorAttachment.view;
|
{
|
||||||
attachments[1] = multisampleTarget.depthAttachment.view;
|
VkImageView attachments[4];
|
||||||
attachments[2] = depthAttachment.view;
|
attachments[0] = multisampleTarget.colorAttachment.view;
|
||||||
attachments[3] = colorAttachment.view;
|
attachments[1] = multisampleTarget.depthAttachment.view;
|
||||||
|
attachments[2] = depthAttachment.view;
|
||||||
|
attachments[3] = colorAttachment.view;
|
||||||
|
|
||||||
VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo();
|
VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo();
|
||||||
framebufferCreateInfo.renderPass = renderPass;
|
framebufferCreateInfo.renderPass = renderPass;
|
||||||
framebufferCreateInfo.attachmentCount = 4;
|
framebufferCreateInfo.attachmentCount = 4;
|
||||||
framebufferCreateInfo.pAttachments = attachments;
|
framebufferCreateInfo.pAttachments = attachments;
|
||||||
framebufferCreateInfo.width = setter.settings.width;
|
framebufferCreateInfo.width = setter.settings.width;
|
||||||
framebufferCreateInfo.height = setter.settings.height;
|
framebufferCreateInfo.height = setter.settings.height;
|
||||||
framebufferCreateInfo.layers = 1;
|
framebufferCreateInfo.layers = 1;
|
||||||
|
|
||||||
VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffer));
|
VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffers[i]));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
VkImageView attachments[2];
|
for (int i = 0; i < frameRange; i++)
|
||||||
attachments[0] = colorAttachment.view;
|
{
|
||||||
attachments[1] = depthAttachment.view;
|
VkImageView attachments[2];
|
||||||
|
attachments[0] = colorAttachment.view;
|
||||||
|
attachments[1] = depthAttachment.view;
|
||||||
|
|
||||||
VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo();
|
VkFramebufferCreateInfo framebufferCreateInfo = vks::initializers::framebufferCreateInfo();
|
||||||
framebufferCreateInfo.renderPass = renderPass;
|
framebufferCreateInfo.renderPass = renderPass;
|
||||||
framebufferCreateInfo.attachmentCount = 2;
|
framebufferCreateInfo.attachmentCount = 2;
|
||||||
framebufferCreateInfo.pAttachments = attachments;
|
framebufferCreateInfo.pAttachments = attachments;
|
||||||
framebufferCreateInfo.width = setter.settings.width;
|
framebufferCreateInfo.width = setter.settings.width;
|
||||||
framebufferCreateInfo.height = setter.settings.height;
|
framebufferCreateInfo.height = setter.settings.height;
|
||||||
framebufferCreateInfo.layers = 1;
|
framebufferCreateInfo.layers = 1;
|
||||||
VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffer));
|
VK_CHECK_RESULT(vkCreateFramebuffer(device, &framebufferCreateInfo, nullptr, &framebuffers[i]));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1292,11 +1297,10 @@ void VulkanBackend::VulkanFondation::createFramebuffer()
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
createSwapChainFramebuffer();
|
createSwapChainFramebuffer();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createSwapChainFramebuffer()
|
void VulkanBackend::VulkanFoundation::createSwapChainFramebuffer()
|
||||||
{
|
{
|
||||||
uint32_t attachmentCount;
|
uint32_t attachmentCount;
|
||||||
VkImageView attachments[attachmentCount];
|
VkImageView attachments[attachmentCount];
|
||||||
|
@ -1326,7 +1330,7 @@ void VulkanBackend::VulkanFondation::createSwapChainFramebuffer()
|
||||||
|
|
||||||
|
|
||||||
// Create frame buffers for every swap chain image
|
// Create frame buffers for every swap chain image
|
||||||
swapChainFramebuffers.resize(swapChainImageViews.size());
|
framebuffers.resize(swapChainImageViews.size());
|
||||||
for (uint32_t i = 0; i < swapChainImageViews.size(); i++) {
|
for (uint32_t i = 0; i < swapChainImageViews.size(); i++) {
|
||||||
if (setter.settings.multiSampling) {
|
if (setter.settings.multiSampling) {
|
||||||
attachments[3] = swapChainImageViews[i];
|
attachments[3] = swapChainImageViews[i];
|
||||||
|
@ -1334,13 +1338,13 @@ void VulkanBackend::VulkanFondation::createSwapChainFramebuffer()
|
||||||
else {
|
else {
|
||||||
attachments[0] = swapChainImageViews[i];
|
attachments[0] = swapChainImageViews[i];
|
||||||
}
|
}
|
||||||
VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCI, nullptr, &swapChainFramebuffers[i]));
|
VK_CHECK_RESULT(vkCreateFramebuffer(device, &frameBufferCI, nullptr, &framebuffers[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createCommandPool()
|
void VulkanBackend::VulkanFoundation::createCommandPool()
|
||||||
{
|
{
|
||||||
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
|
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice);
|
||||||
|
|
||||||
|
@ -1355,39 +1359,59 @@ void VulkanBackend::VulkanFondation::createCommandPool()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createVertexBuffer()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void VulkanBackend::VulkanFoundation::createUniformBuffer()
|
||||||
{
|
{
|
||||||
VkDeviceSize bufferSize = sizeof(vertices[0]) * vertices.size();
|
if (setter.settings.headless)
|
||||||
VkBuffer stagingBuffer;
|
{
|
||||||
VkDeviceMemory stagingBufferMemory;
|
uniformBuffers.resize(frameRange);
|
||||||
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingBufferMemory);
|
}
|
||||||
|
else
|
||||||
void* data;
|
{
|
||||||
vkMapMemory(device, stagingBufferMemory, 0, bufferSize, 0, &data);
|
uniformBuffers.resize(swapChainImages.size());
|
||||||
memcpy(data, vertices.data(), (size_t)bufferSize);
|
}
|
||||||
vkUnmapMemory(device, stagingBufferMemory);
|
|
||||||
|
for (auto& uniformBuffer : uniformBuffers) {
|
||||||
// host visible buffer as temp buffer
|
uniformBuffer.scene.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataScene));
|
||||||
createBuffer(bufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, vertexBuffer, vertexBufferMemory);
|
uniformBuffer.skybox.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderDataSkybox));
|
||||||
|
uniformBuffer.params.create(vulkanDevice, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, sizeof(shaderData));
|
||||||
// move the vertex data to the device local buffer
|
}
|
||||||
copyBuffer(stagingBuffer, vertexBuffer, bufferSize);
|
updateUniformBuffers();
|
||||||
|
|
||||||
vkDestroyBuffer(device, stagingBuffer, nullptr);
|
|
||||||
vkFreeMemory(device, stagingBufferMemory, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createIndexBuffer()
|
void VulkanBackend::VulkanFoundation::updateUniformBuffers()
|
||||||
{
|
{
|
||||||
|
// Scene
|
||||||
|
shaderDataScene.projection = camera.matrices.perspective;
|
||||||
|
shaderDataScene.view = camera.matrices.view;
|
||||||
|
|
||||||
|
// Center and scale model
|
||||||
|
float scale = (1.0f / std::max(models.scene.aabb[0][0], std::max(models.scene.aabb[1][1], models.scene.aabb[2][2]))) * 0.5f;
|
||||||
|
glm::vec3 translate = -glm::vec3(models.scene.aabb[3][0], models.scene.aabb[3][1], models.scene.aabb[3][2]);
|
||||||
|
translate += -0.5f * glm::vec3(models.scene.aabb[0][0], models.scene.aabb[1][1], models.scene.aabb[2][2]);
|
||||||
|
|
||||||
|
shaderDataScene.model = glm::mat4(1.0f);
|
||||||
|
shaderDataScene.model[0][0] = scale;
|
||||||
|
shaderDataScene.model[1][1] = scale;
|
||||||
|
shaderDataScene.model[2][2] = scale;
|
||||||
|
shaderDataScene.model = glm::translate(shaderDataScene.model, translate);
|
||||||
|
|
||||||
|
shaderDataScene.camPos = glm::vec3(
|
||||||
|
-camera.position.z * sin(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x)),
|
||||||
|
-camera.position.z * sin(glm::radians(camera.rotation.x)),
|
||||||
|
camera.position.z * cos(glm::radians(camera.rotation.y)) * cos(glm::radians(camera.rotation.x))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Skybox
|
||||||
|
shaderDataSkybox.projection = camera.matrices.perspective;
|
||||||
|
shaderDataSkybox.view = camera.matrices.view;
|
||||||
|
shaderDataSkybox.model = glm::mat4(glm::mat3(camera.matrices.view));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createUniformBuffer()
|
void VulkanBackend::VulkanFoundation::createDescriptorPool()
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createDescriptorPool()
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Descriptor Pool
|
Descriptor Pool
|
||||||
|
@ -1424,14 +1448,430 @@ void VulkanBackend::VulkanFondation::createDescriptorPool()
|
||||||
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool));
|
VK_CHECK_RESULT(vkCreateDescriptorPool(device, &descriptorPoolCI, nullptr, &descriptorPool));
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createDescriptorSets()
|
void VulkanBackend::VulkanFoundation::createDescriptorSets()
|
||||||
{
|
{
|
||||||
|
if (setter.settings.headless)
|
||||||
|
{
|
||||||
|
descriptorSets.resize(frameRange);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
descriptorSets.resize(swapChainImages.size());
|
||||||
|
}
|
||||||
|
createSceneDescriptorSets();
|
||||||
|
createMaterialDescriptorSets();
|
||||||
|
createModelNodeDescriptorSets();
|
||||||
|
createSkyboxDescriptorSets();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanBackend::VulkanFondation::createCommandBuffer()
|
// Scene (matrices and environment maps)
|
||||||
|
void VulkanBackend::VulkanFoundation::createSceneDescriptorSets()
|
||||||
{
|
{
|
||||||
|
|
||||||
|
for (auto i = 0; i < descriptorSets.size(); i++)
|
||||||
|
{
|
||||||
|
|
||||||
|
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
||||||
|
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
|
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
||||||
|
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene;
|
||||||
|
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||||||
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].scene));
|
||||||
|
|
||||||
|
std::array<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].scene;
|
||||||
|
writeDescriptorSets[0].dstBinding = 0;
|
||||||
|
writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].scene.descriptor;
|
||||||
|
|
||||||
|
writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
writeDescriptorSets[1].descriptorCount = 1;
|
||||||
|
writeDescriptorSets[1].dstSet = descriptorSets[i].scene;
|
||||||
|
writeDescriptorSets[1].dstBinding = 1;
|
||||||
|
writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor;
|
||||||
|
|
||||||
|
writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
writeDescriptorSets[2].descriptorCount = 1;
|
||||||
|
writeDescriptorSets[2].dstSet = descriptorSets[i].scene;
|
||||||
|
writeDescriptorSets[2].dstBinding = 2;
|
||||||
|
writeDescriptorSets[2].pImageInfo = &pbrMaterial.textures.irradianceCube.descriptor;
|
||||||
|
|
||||||
|
writeDescriptorSets[3].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
writeDescriptorSets[3].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
writeDescriptorSets[3].descriptorCount = 1;
|
||||||
|
writeDescriptorSets[3].dstSet = descriptorSets[i].scene;
|
||||||
|
writeDescriptorSets[3].dstBinding = 3;
|
||||||
|
writeDescriptorSets[3].pImageInfo = &pbrMaterial.textures.prefilteredCube.descriptor;
|
||||||
|
|
||||||
|
writeDescriptorSets[4].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
writeDescriptorSets[4].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
writeDescriptorSets[4].descriptorCount = 1;
|
||||||
|
writeDescriptorSets[4].dstSet = descriptorSets[i].scene;
|
||||||
|
writeDescriptorSets[4].dstBinding = 4;
|
||||||
|
writeDescriptorSets[4].pImageInfo = &pbrMaterial.textures.lutBrdf.descriptor;
|
||||||
|
|
||||||
|
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, NULL);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-Material descriptor sets
|
||||||
|
void VulkanBackend::VulkanFoundation::createMaterialDescriptorSets()
|
||||||
|
{
|
||||||
|
|
||||||
|
for (auto& material : models.scene.materials)
|
||||||
|
{
|
||||||
|
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
||||||
|
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
|
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
||||||
|
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.material;
|
||||||
|
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||||||
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &material.descriptorSet));
|
||||||
|
|
||||||
|
std::vector<VkDescriptorImageInfo> imageDescriptors =
|
||||||
|
{
|
||||||
|
pbrMaterial.textures.empty.descriptor,
|
||||||
|
pbrMaterial.textures.empty.descriptor,
|
||||||
|
material.normalTexture ? material.normalTexture->descriptor : pbrMaterial.textures.empty.descriptor,
|
||||||
|
material.occlusionTexture ? material.occlusionTexture->descriptor : pbrMaterial.textures.empty.descriptor,
|
||||||
|
material.emissiveTexture ? material.emissiveTexture->descriptor : pbrMaterial.textures.empty.descriptor
|
||||||
|
};
|
||||||
|
|
||||||
|
if (material.pbrWorkflows.metallicRoughness)
|
||||||
|
{
|
||||||
|
if (material.baseColorTexture) {
|
||||||
|
imageDescriptors[0] = material.baseColorTexture->descriptor;
|
||||||
|
}
|
||||||
|
if (material.metallicRoughnessTexture) {
|
||||||
|
imageDescriptors[1] = material.metallicRoughnessTexture->descriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (material.pbrWorkflows.specularGlossiness)
|
||||||
|
{
|
||||||
|
if (material.extension.diffuseTexture)
|
||||||
|
{
|
||||||
|
imageDescriptors[0] = material.extension.diffuseTexture->descriptor;
|
||||||
|
}
|
||||||
|
if (material.extension.specularGlossinessTexture)
|
||||||
|
{
|
||||||
|
imageDescriptors[1] = material.extension.specularGlossinessTexture->descriptor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<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)
|
||||||
|
void VulkanBackend::VulkanFoundation::createModelNodeDescriptorSets()
|
||||||
|
{
|
||||||
|
|
||||||
|
// Per-Node descriptor set
|
||||||
|
for (auto& node : models.scene.nodes)
|
||||||
|
{
|
||||||
|
setupglTFNodeDescriptorSet(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// attention: gltf-spec
|
||||||
|
void VulkanBackend::VulkanFoundation::setupglTFNodeDescriptorSet(glTFModel::Node* node)
|
||||||
|
{
|
||||||
|
if (node->mesh) {
|
||||||
|
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
||||||
|
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
|
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
||||||
|
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.node;
|
||||||
|
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||||||
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &node->mesh->uniformBuffer.descriptorSet));
|
||||||
|
|
||||||
|
VkWriteDescriptorSet writeDescriptorSet{};
|
||||||
|
writeDescriptorSet.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
writeDescriptorSet.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
writeDescriptorSet.descriptorCount = 1;
|
||||||
|
writeDescriptorSet.dstSet = node->mesh->uniformBuffer.descriptorSet;
|
||||||
|
writeDescriptorSet.dstBinding = 0;
|
||||||
|
writeDescriptorSet.pBufferInfo = &node->mesh->uniformBuffer.descriptor;
|
||||||
|
|
||||||
|
vkUpdateDescriptorSets(device, 1, &writeDescriptorSet, 0, nullptr);
|
||||||
|
}
|
||||||
|
for (auto& child : node->children) {
|
||||||
|
setupglTFNodeDescriptorSet(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanBackend::VulkanFoundation::createSkyboxDescriptorSets()
|
||||||
|
{
|
||||||
|
// Skybox (fixed set)
|
||||||
|
for (auto i = 0; i < uniformBuffers.size(); i++) {
|
||||||
|
VkDescriptorSetAllocateInfo descriptorSetAllocInfo{};
|
||||||
|
descriptorSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||||
|
descriptorSetAllocInfo.descriptorPool = descriptorPool;
|
||||||
|
descriptorSetAllocInfo.pSetLayouts = &descriptorSetLayouts.scene;
|
||||||
|
descriptorSetAllocInfo.descriptorSetCount = 1;
|
||||||
|
VK_CHECK_RESULT(vkAllocateDescriptorSets(device, &descriptorSetAllocInfo, &descriptorSets[i].skybox));
|
||||||
|
|
||||||
|
std::array<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].skybox;
|
||||||
|
writeDescriptorSets[0].dstBinding = 0;
|
||||||
|
writeDescriptorSets[0].pBufferInfo = &uniformBuffers[i].skybox.descriptor;
|
||||||
|
|
||||||
|
writeDescriptorSets[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
writeDescriptorSets[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||||
|
writeDescriptorSets[1].descriptorCount = 1;
|
||||||
|
writeDescriptorSets[1].dstSet = descriptorSets[i].skybox;
|
||||||
|
writeDescriptorSets[1].dstBinding = 1;
|
||||||
|
writeDescriptorSets[1].pBufferInfo = &uniformBuffers[i].params.descriptor;
|
||||||
|
|
||||||
|
writeDescriptorSets[2].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||||
|
writeDescriptorSets[2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||||
|
writeDescriptorSets[2].descriptorCount = 1;
|
||||||
|
writeDescriptorSets[2].dstSet = descriptorSets[i].skybox;
|
||||||
|
writeDescriptorSets[2].dstBinding = 2;
|
||||||
|
writeDescriptorSets[2].pImageInfo = &pbrMaterial.textures.prefilteredCube.descriptor;
|
||||||
|
|
||||||
|
vkUpdateDescriptorSets(device, static_cast<uint32_t>(writeDescriptorSets.size()), writeDescriptorSets.data(), 0, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void VulkanBackend::VulkanFoundation::allocateCommandBuffers()
|
||||||
|
{
|
||||||
|
// resize
|
||||||
|
if (setter.settings.headless)
|
||||||
|
{
|
||||||
|
commandbuffers.resize(frameRange);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
commandbuffers.resize(swapChainImages.size());
|
||||||
|
}
|
||||||
|
// allocate
|
||||||
|
VkCommandBufferAllocateInfo cmdBufAllocateInfo{};
|
||||||
|
cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||||
|
cmdBufAllocateInfo.commandPool = commandPool;
|
||||||
|
cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||||
|
cmdBufAllocateInfo.commandBufferCount = static_cast<uint32_t>(commandbuffers.size());
|
||||||
|
VK_CHECK_RESULT(vkAllocateCommandBuffers(device, &cmdBufAllocateInfo, commandbuffers.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanBackend::VulkanFoundation::createCommandBuffer()
|
||||||
|
{
|
||||||
|
|
||||||
|
VkCommandBufferBeginInfo cmdBufferBeginInfo{};
|
||||||
|
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||||
|
|
||||||
|
VkClearValue clearValues[3];
|
||||||
|
if (setter.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 = setter.settings.width;
|
||||||
|
renderPassBeginInfo.renderArea.extent.height = setter.settings.height;
|
||||||
|
renderPassBeginInfo.clearValueCount = setter.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)setter.settings.width;
|
||||||
|
viewport.height = (float)setter.settings.height;
|
||||||
|
viewport.minDepth = 0.0f;
|
||||||
|
viewport.maxDepth = 1.0f;
|
||||||
|
vkCmdSetViewport(currentCB, 0, 1, &viewport);
|
||||||
|
|
||||||
|
VkRect2D scissor{};
|
||||||
|
scissor.extent = { setter.settings.width, setter.settings.height };
|
||||||
|
vkCmdSetScissor(currentCB, 0, 1, &scissor);
|
||||||
|
|
||||||
|
VkDeviceSize offsets[1] = { 0 };
|
||||||
|
|
||||||
|
if (setter.settings.displayBackground) {
|
||||||
|
vkCmdBindDescriptorSets(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 1, &descriptorSets[i].skybox, 0, nullptr);
|
||||||
|
vkCmdBindPipeline(currentCB, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skybox);
|
||||||
|
models.skybox.draw(currentCB);
|
||||||
|
}
|
||||||
|
|
||||||
|
glTFModel::Model& model = models.scene;
|
||||||
|
|
||||||
|
vkCmdBindVertexBuffers(currentCB, 0, 1, &model.vertices.buffer, offsets);
|
||||||
|
if (model.indices.buffer != VK_NULL_HANDLE) {
|
||||||
|
vkCmdBindIndexBuffer(currentCB, model.indices.buffer, 0, VK_INDEX_TYPE_UINT32);
|
||||||
|
}
|
||||||
|
|
||||||
|
boundPipeline = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
// Opaque primitives first
|
||||||
|
for (auto node : model.nodes) {
|
||||||
|
createglTFNodeCommandBuffer(node, i, glTFModel::Material::ALPHAMODE_OPAQUE);
|
||||||
|
}
|
||||||
|
// Alpha masked primitives
|
||||||
|
for (auto node : model.nodes) {
|
||||||
|
createglTFNodeCommandBuffer(node, i, glTFModel::Material::ALPHAMODE_MASK);
|
||||||
|
}
|
||||||
|
// Transparent primitives
|
||||||
|
// TODO: Correct depth sorting
|
||||||
|
for (auto node : model.nodes) {
|
||||||
|
createglTFNodeCommandBuffer(node, i, glTFModel::Material::ALPHAMODE_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
// User interface
|
||||||
|
if (!setter.settings.headless)
|
||||||
|
{
|
||||||
|
gui->draw(currentCB);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdEndRenderPass(currentCB);
|
||||||
|
VK_CHECK_RESULT(vkEndCommandBuffer(currentCB));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanBackend::VulkanFoundation::createglTFNodeCommandBuffer(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode)
|
||||||
|
{
|
||||||
|
if (node->mesh) {
|
||||||
|
// Render mesh primitives
|
||||||
|
for (glTFModel::Primitive* primitive : node->mesh->primitives) {
|
||||||
|
if (primitive->material.alphaMode == alphaMode) {
|
||||||
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
||||||
|
switch (alphaMode) {
|
||||||
|
case glTFModel::Material::ALPHAMODE_OPAQUE:
|
||||||
|
case glTFModel::Material::ALPHAMODE_MASK:
|
||||||
|
pipeline = primitive->material.doubleSided ? pipelines.pbrDoubleSided : pipelines.pbr;
|
||||||
|
break;
|
||||||
|
case glTFModel::Material::ALPHAMODE_BLEND:
|
||||||
|
pipeline = pipelines.pbrAlphaBlend;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pipeline != boundPipeline) {
|
||||||
|
vkCmdBindPipeline(commandbuffers[cbIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline);
|
||||||
|
boundPipeline = pipeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<VkDescriptorSet> descriptorsets = {
|
||||||
|
descriptorSets[cbIndex].scene,
|
||||||
|
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
|
||||||
|
PBR::Material::PushConstBlockMaterial pushConstBlockMaterial{};
|
||||||
|
pushConstBlockMaterial.emissiveFactor = primitive->material.emissiveFactor;
|
||||||
|
// To save push constant space, availabilty and texture coordiante set are combined
|
||||||
|
// -1 = texture not used for this material, >= 0 texture used and index of texture coordinate set
|
||||||
|
pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
|
||||||
|
pushConstBlockMaterial.normalTextureSet = primitive->material.normalTexture != nullptr ? primitive->material.texCoordSets.normal : -1;
|
||||||
|
pushConstBlockMaterial.occlusionTextureSet = primitive->material.occlusionTexture != nullptr ? primitive->material.texCoordSets.occlusion : -1;
|
||||||
|
pushConstBlockMaterial.emissiveTextureSet = primitive->material.emissiveTexture != nullptr ? primitive->material.texCoordSets.emissive : -1;
|
||||||
|
pushConstBlockMaterial.alphaMask = static_cast<float>(primitive->material.alphaMode == glTFModel::Material::ALPHAMODE_MASK);
|
||||||
|
pushConstBlockMaterial.alphaMaskCutoff = primitive->material.alphaCutoff;
|
||||||
|
|
||||||
|
// TODO: glTF specs states that metallic roughness should be preferred, even if specular glosiness is present
|
||||||
|
|
||||||
|
if (primitive->material.pbrWorkflows.metallicRoughness) {
|
||||||
|
// Metallic roughness workflow
|
||||||
|
pushConstBlockMaterial.workflow = static_cast<float>(PBR::Material::PBRWorkflows::PBR_WORKFLOW_METALLIC_ROUGHNESS);
|
||||||
|
pushConstBlockMaterial.baseColorFactor = primitive->material.baseColorFactor;
|
||||||
|
pushConstBlockMaterial.metallicFactor = primitive->material.metallicFactor;
|
||||||
|
pushConstBlockMaterial.roughnessFactor = primitive->material.roughnessFactor;
|
||||||
|
pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.metallicRoughnessTexture != nullptr ? primitive->material.texCoordSets.metallicRoughness : -1;
|
||||||
|
pushConstBlockMaterial.colorTextureSet = primitive->material.baseColorTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (primitive->material.pbrWorkflows.specularGlossiness) {
|
||||||
|
// Specular glossiness workflow
|
||||||
|
pushConstBlockMaterial.workflow = static_cast<float>(PBR::Material::PBRWorkflows::PBR_WORKFLOW_SPECULAR_GLOSINESS);
|
||||||
|
pushConstBlockMaterial.PhysicalDescriptorTextureSet = primitive->material.extension.specularGlossinessTexture != nullptr ? primitive->material.texCoordSets.specularGlossiness : -1;
|
||||||
|
pushConstBlockMaterial.colorTextureSet = primitive->material.extension.diffuseTexture != nullptr ? primitive->material.texCoordSets.baseColor : -1;
|
||||||
|
pushConstBlockMaterial.diffuseFactor = primitive->material.extension.diffuseFactor;
|
||||||
|
pushConstBlockMaterial.specularFactor = glm::vec4(primitive->material.extension.specularFactor, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
vkCmdPushConstants(commandbuffers[cbIndex], pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(PBR::Material::PushConstBlockMaterial), &pushConstBlockMaterial);
|
||||||
|
|
||||||
|
if (primitive->hasIndices) {
|
||||||
|
vkCmdDrawIndexed(commandbuffers[cbIndex], primitive->indexCount, 1, primitive->firstIndex, 0, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
vkCmdDraw(commandbuffers[cbIndex], primitive->vertexCount, 1, 0, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
for (auto child : node->children) {
|
||||||
|
createglTFNodeCommandBuffer(child, cbIndex, alphaMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VulkanBackend::VulkanFoundation::createFenceAndSemaphore()
|
||||||
|
{
|
||||||
|
waitFences.resize(setter.settings.MaxFrameInFlight);
|
||||||
|
presentCompleteSemaphores.resize(setter.settings.MaxFrameInFlight);
|
||||||
|
renderCompleteSemaphores.resize(setter.settings.MaxFrameInFlight);
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
}
|
||||||
|
if (!setter.settings.headless)
|
||||||
|
{
|
||||||
|
for (auto& semaphore : presentCompleteSemaphores) {
|
||||||
|
VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
|
||||||
|
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Queue ordering semaphores
|
||||||
|
|
||||||
|
for (auto& semaphore : renderCompleteSemaphores) {
|
||||||
|
VkSemaphoreCreateInfo semaphoreCI{ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, nullptr, 0 };
|
||||||
|
VK_CHECK_RESULT(vkCreateSemaphore(device, &semaphoreCI, nullptr, &semaphore));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -13,14 +13,16 @@
|
||||||
#include "VulkanUtils.hpp"
|
#include "VulkanUtils.hpp"
|
||||||
#include "PBR.h"
|
#include "PBR.h"
|
||||||
#include "VulkanDevice.hpp"
|
#include "VulkanDevice.hpp"
|
||||||
|
#include <camera.hpp>
|
||||||
|
#include "renderUI.h"
|
||||||
|
|
||||||
namespace VulkanBackend
|
namespace VulkanBackend
|
||||||
{
|
{
|
||||||
class VulkanFondation
|
class VulkanFoundation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VulkanFondation();
|
VulkanFoundation();
|
||||||
~VulkanFondation();
|
~VulkanFoundation();
|
||||||
|
|
||||||
const std::vector<const char*> validationLayers = {
|
const std::vector<const char*> validationLayers = {
|
||||||
"VK_LAYER_KHRONOS_validation"
|
"VK_LAYER_KHRONOS_validation"
|
||||||
|
@ -34,12 +36,15 @@ namespace VulkanBackend
|
||||||
|
|
||||||
const int MAX_FRAME_IN_FLIGHT = 2;
|
const int MAX_FRAME_IN_FLIGHT = 2;
|
||||||
|
|
||||||
|
const int frameRange = setter.settings.endFrameIndex - setter.settings.startFrameCount;
|
||||||
|
|
||||||
void initVulkan();
|
void initVulkan();
|
||||||
|
|
||||||
|
VkDevice device;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
VulkanBackend::Setter setter;
|
PlumageRender::Setter setter;
|
||||||
PBR::Material pbrMaterial;
|
PBR::Material pbrMaterial;
|
||||||
vks::VulkanDevice* vulkanDevice;
|
vks::VulkanDevice* vulkanDevice;
|
||||||
|
|
||||||
|
@ -70,7 +75,7 @@ namespace VulkanBackend
|
||||||
};
|
};
|
||||||
|
|
||||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE;
|
||||||
VkDevice device;
|
|
||||||
|
|
||||||
GLFWwindow* window;
|
GLFWwindow* window;
|
||||||
VkSurfaceKHR surface;
|
VkSurfaceKHR surface;
|
||||||
|
@ -96,8 +101,12 @@ namespace VulkanBackend
|
||||||
|
|
||||||
VkDescriptorPool descriptorPool;
|
VkDescriptorPool descriptorPool;
|
||||||
|
|
||||||
VkFramebuffer framebuffer;
|
Camera camera;
|
||||||
std::vector<VkFramebuffer> swapChainFramebuffers;
|
|
||||||
|
|
||||||
|
std::vector<VkFramebuffer> framebuffers;
|
||||||
|
|
||||||
|
std::vector<VkCommandBuffer> commandbuffers;
|
||||||
|
|
||||||
struct FrameBufferAttachment
|
struct FrameBufferAttachment
|
||||||
{
|
{
|
||||||
|
@ -123,6 +132,14 @@ namespace VulkanBackend
|
||||||
VkDescriptorSetLayout node;
|
VkDescriptorSetLayout node;
|
||||||
} descriptorSetLayouts;
|
} descriptorSetLayouts;
|
||||||
|
|
||||||
|
struct DescriptorSets {
|
||||||
|
VkDescriptorSet scene;
|
||||||
|
VkDescriptorSet skybox;
|
||||||
|
VkDescriptorSet tonemappingDescriptorSet = VK_NULL_HANDLE;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<DescriptorSets> descriptorSets;
|
||||||
|
|
||||||
struct Pipelines {
|
struct Pipelines {
|
||||||
VkPipeline skybox;
|
VkPipeline skybox;
|
||||||
VkPipeline pbr;
|
VkPipeline pbr;
|
||||||
|
@ -147,6 +164,35 @@ namespace VulkanBackend
|
||||||
|
|
||||||
VkPipelineCache pipelineCache;
|
VkPipelineCache pipelineCache;
|
||||||
|
|
||||||
|
struct UniformBufferSet {
|
||||||
|
Buffer scene;
|
||||||
|
Buffer skybox;
|
||||||
|
Buffer params;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UBOMatrices {
|
||||||
|
glm::mat4 projection;
|
||||||
|
glm::mat4 model;
|
||||||
|
glm::mat4 view;
|
||||||
|
glm::vec3 camPos;
|
||||||
|
} shaderDataScene, shaderDataSkybox;
|
||||||
|
|
||||||
|
struct ShaderData {
|
||||||
|
glm::vec4 lightDir;
|
||||||
|
float exposure = 4.5f;
|
||||||
|
float gamma = 2.2f;
|
||||||
|
float prefilteredCubeMipLevels;
|
||||||
|
float scaleIBLAmbient = 1.0f;
|
||||||
|
float debugViewInputs = 0;
|
||||||
|
float debugViewEquation = 0;
|
||||||
|
} shaderData;
|
||||||
|
|
||||||
|
std::vector<UniformBufferSet> uniformBuffers;
|
||||||
|
|
||||||
|
std::vector<VkFence> waitFences;
|
||||||
|
std::vector<VkSemaphore> renderCompleteSemaphores;
|
||||||
|
std::vector<VkSemaphore> presentCompleteSemaphores;
|
||||||
|
|
||||||
// 句柄创建,检查校验层支持和获取需要的扩展
|
// 句柄创建,检查校验层支持和获取需要的扩展
|
||||||
void createInstance();
|
void createInstance();
|
||||||
bool checkValidationLayerSupport();
|
bool checkValidationLayerSupport();
|
||||||
|
@ -169,8 +215,8 @@ namespace VulkanBackend
|
||||||
void pickPhysicalDevice();
|
void pickPhysicalDevice();
|
||||||
bool isDeviceSuitable(VkPhysicalDevice device);
|
bool isDeviceSuitable(VkPhysicalDevice device);
|
||||||
bool checkDeviceExtensionSupport(VkPhysicalDevice device);
|
bool checkDeviceExtensionSupport(VkPhysicalDevice device);
|
||||||
VulkanBackend::VulkanFondation::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
|
VulkanBackend::VulkanFoundation::QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device);
|
||||||
VulkanBackend::VulkanFondation::SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
|
VulkanBackend::VulkanFoundation::SwapChainSupportDetails querySwapChainSupport(VkPhysicalDevice device);
|
||||||
|
|
||||||
// 创建程序使用的逻辑设备
|
// 创建程序使用的逻辑设备
|
||||||
void createLogicalDevice();
|
void createLogicalDevice();
|
||||||
|
@ -206,31 +252,39 @@ namespace VulkanBackend
|
||||||
// 创建命令缓冲池
|
// 创建命令缓冲池
|
||||||
void createCommandPool();
|
void createCommandPool();
|
||||||
|
|
||||||
// 创建顶点缓存区
|
|
||||||
void createVertexBuffer();
|
|
||||||
|
|
||||||
// 创建索引缓存区
|
|
||||||
void createIndexBuffer();
|
|
||||||
|
|
||||||
// 创建统一缓冲区
|
// 创建统一缓冲区
|
||||||
void createUniformBuffer();
|
void createUniformBuffer();
|
||||||
|
|
||||||
|
void updateUniformBuffers();
|
||||||
|
|
||||||
// 创建描述符池
|
// 创建描述符池
|
||||||
void createDescriptorPool();
|
void createDescriptorPool();
|
||||||
|
|
||||||
// 创建描述符集合
|
// 创建描述符集合
|
||||||
void createDescriptorSets();
|
void createDescriptorSets();
|
||||||
|
|
||||||
|
void createSceneDescriptorSets();
|
||||||
|
void createMaterialDescriptorSets();
|
||||||
|
void createModelNodeDescriptorSets();
|
||||||
|
void setupglTFNodeDescriptorSet(glTFModel::Node* node);
|
||||||
|
void createSkyboxDescriptorSets();
|
||||||
|
|
||||||
// 创建命令缓存区
|
// 创建命令缓存区
|
||||||
|
void allocateCommandBuffers();
|
||||||
void createCommandBuffer();
|
void createCommandBuffer();
|
||||||
|
void createglTFNodeCommandBuffer(glTFModel::Node* node, uint32_t cbIndex, glTFModel::Material::AlphaMode alphaMode);
|
||||||
|
|
||||||
|
// 创建栅栏和信号量,用于多帧并行的同步
|
||||||
|
void createFenceAndSemaphore();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
VulkanFondation::VulkanFondation()
|
VulkanFoundation::VulkanFoundation()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
VulkanFondation::~VulkanFondation()
|
VulkanFoundation::~VulkanFoundation()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue