Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • vulkan2021/vkcv-framework
1 result
Select Git revision
Show changes
Showing
with 1451 additions and 32 deletions
#include "vkcv/scene/Mesh.hpp"
#include "vkcv/scene/Scene.hpp"
#include "vkcv/scene/Frustum.hpp"
namespace vkcv::scene {
Mesh::Mesh(Scene& scene) :
m_scene(scene) {}
static glm::mat4 arrayTo4x4Matrix(const std::array<float,16>& array){
glm::mat4 matrix;
for (int i = 0; i < 4; i++){
for (int j = 0; j < 4; j++){
matrix[i][j] = array[j * 4 + i];
}
}
return matrix;
}
void Mesh::load(const asset::Scene &scene, const asset::Mesh &mesh) {
m_parts.clear();
m_drawcalls.clear();
m_transform = arrayTo4x4Matrix(mesh.modelMatrix);
for (const auto& vertexGroupIndex : mesh.vertexGroups) {
if ((vertexGroupIndex < 0) || (vertexGroupIndex >= scene.vertexGroups.size())) {
continue;
}
MeshPart part (m_scene);
part.load(scene, scene.vertexGroups[vertexGroupIndex], m_drawcalls);
if (!part) {
continue;
}
auto bounds = transformBounds(m_transform, part.getBounds());
if (m_parts.empty()) {
m_bounds = bounds;
} else {
m_bounds.extend(bounds.getMin());
m_bounds.extend(bounds.getMax());
}
m_parts.push_back(part);
}
}
Mesh::~Mesh() {
m_drawcalls.clear();
m_parts.clear();
}
Mesh &Mesh::operator=(const Mesh &other) {
if (&other == this) {
return *this;
}
m_parts.resize(other.m_parts.size(), MeshPart(m_scene));
for (size_t i = 0; i < m_parts.size(); i++) {
m_parts[i] = other.m_parts[i];
}
m_drawcalls = std::vector<DrawcallInfo>(other.m_drawcalls);
m_transform = other.m_transform;
m_bounds = other.m_bounds;
return *this;
}
Mesh &Mesh::operator=(Mesh &&other) noexcept {
m_parts.resize(other.m_parts.size(), MeshPart(m_scene));
for (size_t i = 0; i < m_parts.size(); i++) {
m_parts[i] = std::move(other.m_parts[i]);
}
m_drawcalls = std::move(other.m_drawcalls);
m_transform = other.m_transform;
m_bounds = other.m_bounds;
return *this;
}
void Mesh::recordDrawcalls(const glm::mat4& viewProjection,
PushConstants& pushConstants,
std::vector<DrawcallInfo>& drawcalls,
const RecordMeshDrawcallFunction& record) {
const glm::mat4 transform = viewProjection * m_transform;
if (!checkFrustum(viewProjection, m_bounds)) {
return;
}
if (m_drawcalls.size() == 1) {
drawcalls.push_back(m_drawcalls[0]);
if (record) {
record(transform, m_transform, pushConstants, drawcalls.back());
}
} else {
for (size_t i = 0; i < m_parts.size(); i++) {
const MeshPart& part = m_parts[i];
if (!checkFrustum(transform, part.getBounds())) {
continue;
}
drawcalls.push_back(m_drawcalls[i]);
if (record) {
record(transform, m_transform, pushConstants, drawcalls.back());
}
}
}
}
size_t Mesh::getDrawcallCount() const {
return m_drawcalls.size();
}
const Bounds& Mesh::getBounds() const {
return m_bounds;
}
}
#include "vkcv/scene/MeshPart.hpp"
#include "vkcv/scene/Scene.hpp"
namespace vkcv::scene {
MeshPart::MeshPart(Scene& scene) :
m_scene(scene),
m_vertices(),
m_vertexBindings(),
m_indices(),
m_indexCount(0),
m_bounds(),
m_materialIndex(std::numeric_limits<size_t>::max()) {}
void MeshPart::load(const asset::Scene& scene,
const asset::VertexGroup &vertexGroup,
std::vector<DrawcallInfo>& drawcalls) {
Core& core = *(m_scene.m_core);
auto vertexBuffer = core.createBuffer<uint8_t>(
BufferType::VERTEX, vertexGroup.vertexBuffer.data.size()
);
vertexBuffer.fill(vertexGroup.vertexBuffer.data);
m_vertices = vertexBuffer.getHandle();
auto attributes = vertexGroup.vertexBuffer.attributes;
std::sort(attributes.begin(), attributes.end(), [](const vkcv::asset::VertexAttribute& x, const vkcv::asset::VertexAttribute& y) {
return static_cast<uint32_t>(x.type) < static_cast<uint32_t>(y.type);
});
for (const auto& attribute : attributes) {
m_vertexBindings.emplace_back(attribute.offset, vertexBuffer.getVulkanHandle());
}
auto indexBuffer = core.createBuffer<uint8_t>(
BufferType::INDEX, vertexGroup.indexBuffer.data.size()
);
indexBuffer.fill(vertexGroup.indexBuffer.data);
m_indices = indexBuffer.getHandle();
m_indexCount = vertexGroup.numIndices;
m_bounds.setMin(glm::vec3(
vertexGroup.min.x,
vertexGroup.min.y,
vertexGroup.min.z
));
m_bounds.setMax(glm::vec3(
vertexGroup.max.x,
vertexGroup.max.y,
vertexGroup.max.z
));
if ((vertexGroup.materialIndex >= 0) &&
(vertexGroup.materialIndex < scene.materials.size())) {
m_materialIndex = vertexGroup.materialIndex;
if (!getMaterial()) {
m_scene.loadMaterial(m_materialIndex, scene, scene.materials[vertexGroup.materialIndex]);
}
m_scene.increaseMaterialUsage(m_materialIndex);
} else {
m_materialIndex = std::numeric_limits<size_t>::max();
}
if (*this) {
const auto& material = getMaterial();
const auto& descriptorSet = core.getDescriptorSet(material.getDescriptorSet());
drawcalls.push_back(DrawcallInfo(
vkcv::Mesh(m_vertexBindings, indexBuffer.getVulkanHandle(), m_indexCount),
{ DescriptorSetUsage(0, descriptorSet.vulkanHandle) }
));
}
}
MeshPart::~MeshPart() {
m_scene.decreaseMaterialUsage(m_materialIndex);
}
MeshPart::MeshPart(const MeshPart &other) :
m_scene(other.m_scene),
m_vertices(other.m_vertices),
m_vertexBindings(other.m_vertexBindings),
m_indices(other.m_indices),
m_indexCount(other.m_indexCount),
m_bounds(other.m_bounds),
m_materialIndex(other.m_materialIndex) {
m_scene.increaseMaterialUsage(m_materialIndex);
}
MeshPart::MeshPart(MeshPart &&other) :
m_scene(other.m_scene),
m_vertices(other.m_vertices),
m_vertexBindings(other.m_vertexBindings),
m_indices(other.m_indices),
m_indexCount(other.m_indexCount),
m_bounds(other.m_bounds),
m_materialIndex(other.m_materialIndex) {
m_scene.increaseMaterialUsage(m_materialIndex);
}
MeshPart &MeshPart::operator=(const MeshPart &other) {
if (&other == this) {
return *this;
}
m_vertices = other.m_vertices;
m_vertexBindings = other.m_vertexBindings;
m_indices = other.m_indices;
m_indexCount = other.m_indexCount;
m_bounds = other.m_bounds;
m_materialIndex = other.m_materialIndex;
return *this;
}
MeshPart &MeshPart::operator=(MeshPart &&other) noexcept {
m_vertices = other.m_vertices;
m_vertexBindings = other.m_vertexBindings;
m_indices = other.m_indices;
m_indexCount = other.m_indexCount;
m_bounds = other.m_bounds;
m_materialIndex = other.m_materialIndex;
return *this;
}
const material::Material & MeshPart::getMaterial() const {
return m_scene.getMaterial(m_materialIndex);
}
MeshPart::operator bool() const {
return (
(getMaterial()) &&
(m_vertices) &&
(m_indices)
);
}
bool MeshPart::operator!() const {
return (
(!getMaterial()) ||
(!m_vertices) ||
(!m_indices)
);
}
const Bounds &MeshPart::getBounds() const {
return m_bounds;
}
}
#include "vkcv/scene/Node.hpp"
#include "vkcv/scene/Scene.hpp"
#include "vkcv/scene/Frustum.hpp"
#include <algorithm>
namespace vkcv::scene {
Node::Node(Scene& scene) :
m_scene(scene),
m_meshes(),
m_nodes(),
m_bounds() {}
Node::~Node() {
m_nodes.clear();
m_meshes.clear();
}
Node &Node::operator=(const Node &other) {
if (&other == this) {
return *this;
}
m_meshes.resize(other.m_meshes.size(), Mesh(m_scene));
for (size_t i = 0; i < m_meshes.size(); i++) {
m_meshes[i] = other.m_meshes[i];
}
m_nodes.resize(other.m_nodes.size(), Node(m_scene));
for (size_t i = 0; i < m_nodes.size(); i++) {
m_nodes[i] = other.m_nodes[i];
}
m_bounds = other.m_bounds;
return *this;
}
Node &Node::operator=(Node &&other) noexcept {
m_meshes.resize(other.m_meshes.size(), Mesh(m_scene));
for (size_t i = 0; i < m_meshes.size(); i++) {
m_meshes[i] = std::move(other.m_meshes[i]);
}
m_nodes.resize(other.m_nodes.size(), Node(m_scene));
for (size_t i = 0; i < m_nodes.size(); i++) {
m_nodes[i] = std::move(other.m_nodes[i]);
}
m_bounds = other.m_bounds;
return *this;
}
void Node::addMesh(const Mesh& mesh) {
if (m_meshes.empty()) {
m_bounds = mesh.getBounds();
} else {
m_bounds.extend(mesh.getBounds().getMin());
m_bounds.extend(mesh.getBounds().getMax());
}
m_meshes.push_back(mesh);
}
void Node::loadMesh(const asset::Scene &asset_scene, const asset::Mesh &asset_mesh) {
Mesh mesh (m_scene);
mesh.load(asset_scene, asset_mesh);
addMesh(mesh);
}
size_t Node::addNode() {
const Node node (m_scene);
const size_t index = m_nodes.size();
m_nodes.push_back(node);
return index;
}
Node& Node::getNode(size_t index) {
return m_nodes[index];
}
const Node& Node::getNode(size_t index) const {
return m_nodes[index];
}
void Node::recordDrawcalls(const glm::mat4& viewProjection,
PushConstants& pushConstants,
std::vector<DrawcallInfo>& drawcalls,
const RecordMeshDrawcallFunction& record) {
if (!checkFrustum(viewProjection, m_bounds)) {
return;
}
for (auto& mesh : m_meshes) {
mesh.recordDrawcalls(viewProjection, pushConstants, drawcalls, record);
}
for (auto& node : m_nodes) {
node.recordDrawcalls(viewProjection, pushConstants, drawcalls, record);
}
}
void Node::splitMeshesToSubNodes(size_t maxMeshesPerNode) {
if (m_meshes.size() <= maxMeshesPerNode) {
return;
}
const auto split = m_bounds.getCenter();
int axis = 0;
const auto size = m_bounds.getSize();
if (size[1] > size[0]) {
if (size[2] > size[1]) {
axis = 2;
} else {
axis = 1;
}
} else
if (size[2] > size[0]) {
axis = 2;
}
std::vector<size_t> left_meshes;
std::vector<size_t> right_meshes;
for (size_t i = 0; i < m_meshes.size(); i++) {
const auto& bounds = m_meshes[i].getBounds();
if (bounds.getMax()[axis] <= split[axis]) {
left_meshes.push_back(i);
} else
if (bounds.getMin()[axis] >= split[axis]) {
right_meshes.push_back(i);
}
}
if ((left_meshes.empty()) || (right_meshes.empty())) {
return;
}
const size_t left = addNode();
const size_t right = addNode();
for (size_t i : left_meshes) {
getNode(left).addMesh(m_meshes[i]);
}
for (size_t i : right_meshes) {
getNode(right).addMesh(m_meshes[i]);
left_meshes.push_back(i);
}
std::sort(left_meshes.begin(), left_meshes.end(), std::greater());
for (size_t i : left_meshes) {
m_meshes.erase(m_meshes.begin() + static_cast<long>(i));
}
getNode(left).splitMeshesToSubNodes(maxMeshesPerNode);
getNode(right).splitMeshesToSubNodes(maxMeshesPerNode);
}
size_t Node::getDrawcallCount() const {
size_t count = 0;
for (auto& mesh : m_meshes) {
count += mesh.getDrawcallCount();
}
for (auto& node : m_nodes) {
count += node.getDrawcallCount();
}
return count;
}
const Bounds& Node::getBounds() const {
return m_bounds;
}
}
#include "vkcv/scene/Scene.hpp"
#include <vkcv/Logger.hpp>
#include <vkcv/asset/asset_loader.hpp>
namespace vkcv::scene {
Scene::Scene(Core* core) :
m_core(core),
m_materials(),
m_nodes() {}
Scene::~Scene() {
m_nodes.clear();
m_materials.clear();
}
Scene::Scene(const Scene &other) :
m_core(other.m_core),
m_materials(other.m_materials),
m_nodes() {
m_nodes.resize(other.m_nodes.size(), Node(*this));
for (size_t i = 0; i < m_nodes.size(); i++) {
m_nodes[i] = other.m_nodes[i];
}
}
Scene::Scene(Scene &&other) noexcept :
m_core(other.m_core),
m_materials(other.m_materials),
m_nodes() {
m_nodes.resize(other.m_nodes.size(), Node(*this));
for (size_t i = 0; i < m_nodes.size(); i++) {
m_nodes[i] = std::move(other.m_nodes[i]);
}
}
Scene &Scene::operator=(const Scene &other) {
if (&other == this) {
return *this;
}
m_core = other.m_core;
m_materials = std::vector<Material>(other.m_materials);
m_nodes.resize(other.m_nodes.size(), Node(*this));
for (size_t i = 0; i < m_nodes.size(); i++) {
m_nodes[i] = other.m_nodes[i];
}
return *this;
}
Scene &Scene::operator=(Scene &&other) noexcept {
m_core = other.m_core;
m_materials = std::move(other.m_materials);
m_nodes.resize(other.m_nodes.size(), Node(*this));
for (size_t i = 0; i < m_nodes.size(); i++) {
m_nodes[i] = std::move(other.m_nodes[i]);
}
return *this;
}
size_t Scene::addNode() {
const Node node (*this);
const size_t index = m_nodes.size();
m_nodes.push_back(node);
return index;
}
Node& Scene::getNode(size_t index) {
return m_nodes[index];
}
const Node& Scene::getNode(size_t index) const {
return m_nodes[index];
}
void Scene::increaseMaterialUsage(size_t index) {
if (index < m_materials.size()) {
m_materials[index].m_usages++;
}
}
void Scene::decreaseMaterialUsage(size_t index) {
if ((index < m_materials.size()) && (m_materials[index].m_usages > 0)) {
m_materials[index].m_usages--;
}
}
size_t Scene::getMaterialCount() const {
return m_materials.size();
}
const material::Material & Scene::getMaterial(size_t index) const {
static material::Material noMaterial;
if (index >= m_materials.size()) {
return noMaterial;
}
return m_materials[index].m_data;
}
void Scene::recordDrawcalls(CommandStreamHandle &cmdStream,
const camera::Camera &camera,
const PassHandle &pass,
const PipelineHandle &pipeline,
size_t pushConstantsSizePerDrawcall,
const RecordMeshDrawcallFunction &record,
const std::vector<ImageHandle> &renderTargets) {
PushConstants pushConstants (pushConstantsSizePerDrawcall);
std::vector<DrawcallInfo> drawcalls;
size_t count = 0;
const glm::mat4 viewProjection = camera.getMVP();
for (auto& node : m_nodes) {
count += node.getDrawcallCount();
node.recordDrawcalls(viewProjection, pushConstants, drawcalls, record);
}
vkcv_log(LogLevel::RAW_INFO, "Frustum culling: %lu / %lu", drawcalls.size(), count);
m_core->recordDrawcallsToCmdStream(
cmdStream,
pass,
pipeline,
pushConstants,
drawcalls,
renderTargets
);
}
Scene Scene::create(Core& core) {
return Scene(&core);
}
static void loadImage(Core& core, const asset::Scene& asset_scene,
const asset::Texture& asset_texture,
const vk::Format& format,
ImageHandle& image, SamplerHandle& sampler) {
asset::Sampler* asset_sampler = nullptr;
if ((asset_texture.sampler >= 0) && (asset_texture.sampler < asset_scene.samplers.size())) {
//asset_sampler = &(asset_scene.samplers[asset_texture.sampler]); // TODO
}
Image img = core.createImage(format, asset_texture.w, asset_texture.h);
img.fill(asset_texture.data.data());
image = img.getHandle();
if (asset_sampler) {
//sampler = core.createSampler(asset_sampler) // TODO
} else {
sampler = core.createSampler(
vkcv::SamplerFilterType::LINEAR,
vkcv::SamplerFilterType::LINEAR,
vkcv::SamplerMipmapMode::LINEAR,
vkcv::SamplerAddressMode::REPEAT
);
}
}
void Scene::loadMaterial(size_t index, const asset::Scene& scene,
const asset::Material& material) {
if (index >= m_materials.size()) {
return;
}
ImageHandle diffuseImg;
SamplerHandle diffuseSmp;
if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
diffuseImg,diffuseSmp);
}
ImageHandle normalImg;
SamplerHandle normalSmp;
if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
diffuseImg,diffuseSmp);
}
ImageHandle metalRoughImg;
SamplerHandle metalRoughSmp;
if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
diffuseImg,diffuseSmp);
}
ImageHandle occlusionImg;
SamplerHandle occlusionSmp;
if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
diffuseImg,diffuseSmp);
}
ImageHandle emissionImg;
SamplerHandle emissionSmp;
if ((material.baseColor >= 0) && (material.baseColor < scene.textures.size())) {
loadImage(*m_core, scene, scene.textures[material.baseColor], vk::Format::eR8G8B8A8Srgb,
diffuseImg,diffuseSmp);
}
const float colorFactors [4] = {
material.baseColorFactor.r,
material.baseColorFactor.g,
material.baseColorFactor.b,
material.baseColorFactor.a
};
const float emissionFactors[4] = {
material.emissiveFactor.r,
material.emissiveFactor.g,
material.emissiveFactor.b
};
m_materials[index].m_data = material::Material::createPBR(
*m_core,
diffuseImg, diffuseSmp,
normalImg, normalSmp,
metalRoughImg, metalRoughSmp,
occlusionImg, occlusionSmp,
emissionImg, emissionSmp,
colorFactors,
material.normalScale,
material.metallicFactor,
material.roughnessFactor,
material.occlusionStrength,
emissionFactors
);
}
Scene Scene::load(Core& core, const std::filesystem::path &path) {
asset::Scene asset_scene;
if (!asset::loadScene(path.string(), asset_scene)) {
vkcv_log(LogLevel::ERROR, "Scene could not be loaded (%s)", path.c_str());
return create(core);
}
Scene scene = create(core);
for (const auto& material : asset_scene.materials) {
scene.m_materials.push_back({
0, material::Material()
});
}
const size_t root = scene.addNode();
for (const auto& mesh : asset_scene.meshes) {
scene.getNode(root).loadMesh(asset_scene, mesh);
}
scene.getNode(root).splitMeshesToSubNodes(128);
return scene;
}
}
......@@ -10,6 +10,9 @@ set(vkcv_shader_compiler_include ${PROJECT_SOURCE_DIR}/include)
# Add source and header files to the module
set(vkcv_shader_compiler_sources
${vkcv_shader_compiler_include}/vkcv/shader/Compiler.hpp
${vkcv_shader_compiler_source}/vkcv/shader/Compiler.cpp
${vkcv_shader_compiler_include}/vkcv/shader/GLSLCompiler.hpp
${vkcv_shader_compiler_source}/vkcv/shader/GLSLCompiler.cpp
)
......
#pragma once
#include <filesystem>
#include <string>
#include <unordered_map>
#include <vkcv/Event.hpp>
#include <vkcv/ShaderStage.hpp>
namespace vkcv::shader {
......@@ -8,10 +13,21 @@ namespace vkcv::shader {
class Compiler {
private:
protected:
std::unordered_map<std::string, std::string> m_defines;
public:
virtual bool compileSource(ShaderStage shaderStage, const char* shaderSource,
const ShaderCompiledFunction& compiled,
const std::filesystem::path& includePath) = 0;
virtual void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath,
const ShaderCompiledFunction& compiled, bool update = false) = 0;
const ShaderCompiledFunction& compiled,
const std::filesystem::path& includePath, bool update) = 0;
std::string getDefine(const std::string& name) const;
void setDefine(const std::string& name, const std::string& value);
};
}
......@@ -7,7 +7,7 @@
namespace vkcv::shader {
class GLSLCompiler {
class GLSLCompiler : public Compiler {
private:
public:
GLSLCompiler();
......@@ -20,8 +20,13 @@ namespace vkcv::shader {
GLSLCompiler& operator=(const GLSLCompiler& other);
GLSLCompiler& operator=(GLSLCompiler&& other) = default;
bool compileSource(ShaderStage shaderStage, const char* shaderSource,
const ShaderCompiledFunction& compiled,
const std::filesystem::path& includePath);
void compile(ShaderStage shaderStage, const std::filesystem::path& shaderPath,
const ShaderCompiledFunction& compiled, bool update = false);
const ShaderCompiledFunction& compiled,
const std::filesystem::path& includePath = "", bool update = false) override;
};
......
#include "vkcv/shader/Compiler.hpp"
namespace vkcv::shader {
std::string Compiler::getDefine(const std::string &name) const {
return m_defines.at(name);
}
void Compiler::setDefine(const std::string &name, const std::string &value) {
m_defines[name] = value;
}
}
......@@ -2,16 +2,18 @@
#include "vkcv/shader/GLSLCompiler.hpp"
#include <fstream>
#include <sstream>
#include <glslang/SPIRV/GlslangToSpv.h>
#include <glslang/StandAlone/DirStackFileIncluder.h>
#include <vkcv/File.hpp>
#include <vkcv/Logger.hpp>
namespace vkcv::shader {
static uint32_t s_CompilerCount = 0;
GLSLCompiler::GLSLCompiler() {
GLSLCompiler::GLSLCompiler() : Compiler() {
if (s_CompilerCount == 0) {
glslang::InitializeProcess();
}
......@@ -19,7 +21,7 @@ namespace vkcv::shader {
s_CompilerCount++;
}
GLSLCompiler::GLSLCompiler(const GLSLCompiler &other) {
GLSLCompiler::GLSLCompiler(const GLSLCompiler &other) : Compiler(other) {
s_CompilerCount++;
}
......@@ -50,6 +52,10 @@ namespace vkcv::shader {
return EShLangFragment;
case ShaderStage::COMPUTE:
return EShLangCompute;
case ShaderStage::TASK:
return EShLangTaskNV;
case ShaderStage::MESH:
return EShLangMeshNV;
default:
return EShLangCount;
}
......@@ -197,22 +203,45 @@ namespace vkcv::shader {
return true;
}
void GLSLCompiler::compile(ShaderStage shaderStage, const std::filesystem::path &shaderPath,
const ShaderCompiledFunction& compiled, bool update) {
bool GLSLCompiler::compileSource(ShaderStage shaderStage, const char* shaderSource,
const ShaderCompiledFunction &compiled,
const std::filesystem::path& includePath) {
const EShLanguage language = findShaderLanguage(shaderStage);
if (language == EShLangCount) {
vkcv_log(LogLevel::ERROR, "Shader stage not supported (%s)", shaderPath.string().c_str());
return;
vkcv_log(LogLevel::ERROR, "Shader stage not supported");
return false;
}
const std::vector<char> code = readShaderCode(shaderPath);
glslang::TShader shader (language);
glslang::TProgram program;
std::string source (shaderSource);
if (!m_defines.empty()) {
std::ostringstream defines;
for (const auto& define : m_defines) {
defines << "#define " << define.first << " " << define.second << std::endl;
}
size_t pos = source.find("#version") + 8;
if (pos >= source.length()) {
pos = 0;
}
const size_t epos = source.find_last_of("#extension", pos) + 10;
if (epos < source.length()) {
pos = epos;
}
const auto defines_str = defines.str();
pos = source.find('\n', pos) + 1;
source = source.insert(pos, defines_str);
}
const char *shaderStrings [1];
shaderStrings[0] = code.data();
shaderStrings[0] = source.c_str();
shader.setStrings(shaderStrings, 1);
......@@ -222,51 +251,53 @@ namespace vkcv::shader {
const auto messages = (EShMessages)(
EShMsgSpvRules |
EShMsgVulkanRules
);
);
std::string preprocessedGLSL;
DirStackFileIncluder includer;
includer.pushExternalLocalDirectory(shaderPath.parent_path().string());
includer.pushExternalLocalDirectory(includePath.string());
if (!shader.preprocess(&resources, 100, ENoProfile, false, false, messages, &preprocessedGLSL, includer)) {
vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n} (%s)",
shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str());
return;
if (!shader.preprocess(&resources, 100, ENoProfile,
false, false,
messages, &preprocessedGLSL, includer)) {
vkcv_log(LogLevel::ERROR, "Shader preprocessing failed {\n%s\n%s\n}",
shader.getInfoLog(), shader.getInfoDebugLog());
return false;
}
const char* preprocessedCString = preprocessedGLSL.c_str();
shader.setStrings(&preprocessedCString, 1);
if (!shader.parse(&resources, 100, false, messages)) {
vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n} (%s)",
shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str());
return;
vkcv_log(LogLevel::ERROR, "Shader parsing failed {\n%s\n%s\n}",
shader.getInfoLog(), shader.getInfoDebugLog());
return false;
}
program.addShader(&shader);
if (!program.link(messages)) {
vkcv_log(LogLevel::ERROR, "Shader linking failed {\n%s\n%s\n} (%s)",
shader.getInfoLog(), shader.getInfoDebugLog(), shaderPath.string().c_str());
return;
vkcv_log(LogLevel::ERROR, "Shader linking failed {\n%s\n%s\n}",
shader.getInfoLog(), shader.getInfoDebugLog());
return false;
}
const glslang::TIntermediate* intermediate = program.getIntermediate(language);
if (!intermediate) {
vkcv_log(LogLevel::ERROR, "No valid intermediate representation (%s)", shaderPath.string().c_str());
return;
vkcv_log(LogLevel::ERROR, "No valid intermediate representation");
return false;
}
std::vector<uint32_t> spirv;
glslang::GlslangToSpv(*intermediate, spirv);
const std::filesystem::path tmp_path (std::tmpnam(nullptr));
const std::filesystem::path tmp_path = generateTemporaryFilePath();
if (!writeSpirvCode(tmp_path, spirv)) {
vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk (%s)", shaderPath.string().c_str());
return;
vkcv_log(LogLevel::ERROR, "Spir-V could not be written to disk");
return false;
}
if (compiled) {
......@@ -274,6 +305,24 @@ namespace vkcv::shader {
}
std::filesystem::remove(tmp_path);
return true;
}
void GLSLCompiler::compile(ShaderStage shaderStage, const std::filesystem::path &shaderPath,
const ShaderCompiledFunction& compiled,
const std::filesystem::path& includePath, bool update) {
const std::vector<char> code = readShaderCode(shaderPath);
bool result;
if (!includePath.empty()) {
result = compileSource(shaderStage, code.data(), compiled, includePath);
} else {
result = compileSource(shaderStage, code.data(), compiled, shaderPath.parent_path());
}
if (!result) {
vkcv_log(LogLevel::ERROR, "Shader compilation failed: (%s)", shaderPath.string().c_str());
}
if (update) {
// TODO: Shader hot compilation during runtime
......
cmake_minimum_required(VERSION 3.16)
project(vkcv_upscaling)
# setting c++ standard for the project
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(vkcv_upscaling_source ${PROJECT_SOURCE_DIR}/src)
set(vkcv_upscaling_include ${PROJECT_SOURCE_DIR}/include)
set(vkcv_upscaling_sources
${vkcv_upscaling_include}/vkcv/upscaling/Upscaling.hpp
${vkcv_upscaling_source}/vkcv/upscaling/Upscaling.cpp
${vkcv_upscaling_include}/vkcv/upscaling/BilinearUpscaling.hpp
${vkcv_upscaling_source}/vkcv/upscaling/BilinearUpscaling.cpp
${vkcv_upscaling_include}/vkcv/upscaling/FSRUpscaling.hpp
${vkcv_upscaling_source}/vkcv/upscaling/FSRUpscaling.cpp
)
# Setup some path variables to load libraries
set(vkcv_upscaling_lib lib)
set(vkcv_upscaling_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_upscaling_lib})
# Check and load FidelityFX_FSR
include(config/FidelityFX_FSR.cmake)
# adding source files to the project
add_library(vkcv_upscaling STATIC ${vkcv_upscaling_sources})
# link the required libraries to the module
target_link_libraries(vkcv_upscaling ${vkcv_upscaling_libraries} vkcv vkcv_shader_compiler)
# including headers of dependencies and the VkCV framework
target_include_directories(vkcv_upscaling SYSTEM BEFORE PRIVATE ${vkcv_upscaling_includes} ${vkcv_include} ${vkcv_shader_compiler_include})
# add the own include directory for public headers
target_include_directories(vkcv_upscaling BEFORE PUBLIC ${vkcv_upscaling_include})
if (EXISTS "${vkcv_upscaling_lib_path}/FidelityFX-FSR")
include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/ffx-fsr/ffx_a.h ${vkcv_upscaling_include} ${vkcv_upscaling_source})
include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/ffx-fsr/ffx_fsr1.h ${vkcv_upscaling_include} ${vkcv_upscaling_source})
include_shader(${vkcv_upscaling_lib_path}/FidelityFX-FSR/sample/src/VK/FSR_Pass.glsl ${vkcv_upscaling_include} ${vkcv_upscaling_source})
list(APPEND vkcv_upscaling_includes ${vkcv_upscaling_lib}/FidelityFX-FSR/ffx-fsr)
list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/ffx_a.h.cxx)
list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/ffx_fsr1.h.cxx)
list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_source}/FSR_Pass.glsl.cxx)
list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/ffx_a.h.hxx)
list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/ffx_fsr1.h.hxx)
list(APPEND vkcv_upscaling_sources ${vkcv_upscaling_include}/FSR_Pass.glsl.hxx)
else()
message(WARNING "FidelityFX-FSR is required..! Update the submodules!")
endif ()
#pragma once
#include "Upscaling.hpp"
namespace vkcv::upscaling {
class BilinearUpscaling : public Upscaling {
private:
public:
BilinearUpscaling(Core& core);
void recordUpscaling(const CommandStreamHandle& cmdStream,
const ImageHandle& input,
const ImageHandle& output) override;
};
}
#pragma once
#include "Upscaling.hpp"
#include <vkcv/ShaderProgram.hpp>
namespace vkcv::upscaling {
enum class FSRQualityMode : int {
NONE = 0,
ULTRA_QUALITY = 1,
QUALITY = 2,
BALANCED = 3,
PERFORMANCE = 4
};
void getFSRResolution(FSRQualityMode mode,
uint32_t outputWidth, uint32_t outputHeight,
uint32_t &inputWidth, uint32_t &inputHeight);
float getFSRLodBias(FSRQualityMode mode);
struct FSRConstants {
uint32_t Const0 [4];
uint32_t Const1 [4];
uint32_t Const2 [4];
uint32_t Const3 [4];
uint32_t Sample [4];
};
class FSRUpscaling : public Upscaling {
private:
PipelineHandle m_easuPipeline;
PipelineHandle m_rcasPipeline;
DescriptorSetHandle m_easuDescriptorSet;
DescriptorSetHandle m_rcasDescriptorSet;
Buffer<FSRConstants> m_easuConstants;
Buffer<FSRConstants> m_rcasConstants;
ImageHandle m_intermediateImage;
SamplerHandle m_sampler;
bool m_hdr;
/**
* Sharpness will calculate the rcasAttenuation value
* which should be between 0.0f and 2.0f (default: 0.25f).
*
* rcasAttenuation = (1.0f - sharpness) * 2.0f
*
* So the default value for sharpness should be 0.875f.
*
* Beware that 0.0f or any negative value of sharpness will
* disable the rcas pass completely.
*/
float m_sharpness;
public:
explicit FSRUpscaling(Core& core);
void recordUpscaling(const CommandStreamHandle& cmdStream,
const ImageHandle& input,
const ImageHandle& output) override;
[[nodiscard]]
bool isHdrEnabled() const;
void setHdrEnabled(bool enabled);
[[nodiscard]]
float getSharpness() const;
void setSharpness(float sharpness);
};
}
#pragma once
#include <vkcv/Core.hpp>
#include <vkcv/Handles.hpp>
namespace vkcv::upscaling {
class Upscaling {
protected:
Core& m_core;
public:
Upscaling(Core& core);
~Upscaling() = default;
virtual void recordUpscaling(const CommandStreamHandle& cmdStream,
const ImageHandle& input,
const ImageHandle& output) = 0;
};
}
Subproject commit bcffc8171efb80e265991301a49670ed755088dd
#include "vkcv/upscaling/BilinearUpscaling.hpp"
namespace vkcv::upscaling {
BilinearUpscaling::BilinearUpscaling(Core &core) : Upscaling(core) {}
void BilinearUpscaling::recordUpscaling(const CommandStreamHandle &cmdStream, const ImageHandle &input,
const ImageHandle &output) {
m_core.recordBlitImage(cmdStream, input, output, SamplerFilterType::LINEAR);
}
}
#include "vkcv/upscaling/FSRUpscaling.hpp"
#include <stdint.h>
#include <math.h>
#define A_CPU 1
#include <ffx_a.h>
#include <ffx_fsr1.h>
#include "ffx_a.h.hxx"
#include "ffx_fsr1.h.hxx"
#include "FSR_Pass.glsl.hxx"
#include <vkcv/File.hpp>
#include <vkcv/Logger.hpp>
#include <vkcv/shader/GLSLCompiler.hpp>
namespace vkcv::upscaling {
void getFSRResolution(FSRQualityMode mode,
uint32_t outputWidth, uint32_t outputHeight,
uint32_t &inputWidth, uint32_t &inputHeight) {
float scale;
switch (mode) {
case FSRQualityMode::ULTRA_QUALITY:
scale = 1.3f;
break;
case FSRQualityMode::QUALITY:
scale = 1.5f;
break;
case FSRQualityMode::BALANCED:
scale = 1.7f;
break;
case FSRQualityMode::PERFORMANCE:
scale = 2.0f;
break;
default:
scale = 1.0f;
break;
}
inputWidth = static_cast<uint32_t>(
std::round(static_cast<float>(outputWidth) / scale)
);
inputHeight = static_cast<uint32_t>(
std::round(static_cast<float>(outputHeight) / scale)
);
}
float getFSRLodBias(FSRQualityMode mode) {
switch (mode) {
case FSRQualityMode::ULTRA_QUALITY:
return -0.38f;
case FSRQualityMode::QUALITY:
return -0.58f;
case FSRQualityMode::BALANCED:
return -0.79f;
case FSRQualityMode::PERFORMANCE:
return -1.0f;
default:
return 0.0f;
}
}
static std::vector<DescriptorBinding> getDescriptorBindings() {
return std::vector<DescriptorBinding>({
DescriptorBinding(
0, DescriptorType::UNIFORM_BUFFER_DYNAMIC,
1, ShaderStage::COMPUTE
),
DescriptorBinding(
1, DescriptorType::IMAGE_SAMPLED,
1, ShaderStage::COMPUTE
),
DescriptorBinding(
2, DescriptorType::IMAGE_STORAGE,
1, ShaderStage::COMPUTE
),
DescriptorBinding(
3, DescriptorType::SAMPLER,
1, ShaderStage::COMPUTE
)
});
}
template<typename T>
bool checkFeatures(const vk::BaseInStructure* base, vk::StructureType type, bool (*check)(const T& features)) {
if (base->sType == type) {
return check(*reinterpret_cast<const T*>(base));
} else
if (base->pNext) {
return checkFeatures<T>(base->pNext, type, check);
} else {
return false;
}
}
static bool checkFloat16(const vk::PhysicalDeviceFloat16Int8FeaturesKHR& features) {
return features.shaderFloat16;
}
static bool check16Storage(const vk::PhysicalDevice16BitStorageFeaturesKHR& features) {
return features.storageBuffer16BitAccess;
}
static bool writeShaderCode(const std::filesystem::path &shaderPath, const std::string& code) {
std::ofstream file (shaderPath.string(), std::ios::out);
if (!file.is_open()) {
vkcv_log(LogLevel::ERROR, "The file could not be opened (%s)", shaderPath.string().c_str());
return false;
}
file.seekp(0);
file.write(code.c_str(), static_cast<std::streamsize>(code.length()));
file.close();
return true;
}
static bool compileFSRShader(vkcv::shader::GLSLCompiler& compiler,
const shader::ShaderCompiledFunction& compiled) {
std::filesystem::path directory = generateTemporaryDirectoryPath();
if (!std::filesystem::create_directory(directory)) {
vkcv_log(LogLevel::ERROR, "The directory could not be created (%s)", directory.string().c_str());
return false;
}
if (!writeShaderCode(directory / "ffx_a.h", FFX_A_H_SHADER)) {
return false;
}
if (!writeShaderCode(directory / "ffx_fsr1.h", FFX_FSR1_H_SHADER)) {
return false;
}
return compiler.compileSource(vkcv::ShaderStage::COMPUTE,
FSR_PASS_GLSL_SHADER.c_str(),
[&directory, &compiled] (vkcv::ShaderStage shaderStage,
const std::filesystem::path& path) {
if (compiled) {
compiled(shaderStage, path);
}
std::filesystem::remove_all(directory);
}, directory
);
}
FSRUpscaling::FSRUpscaling(Core& core) :
Upscaling(core),
m_easuPipeline(),
m_rcasPipeline(),
m_easuDescriptorSet(m_core.createDescriptorSet(getDescriptorBindings())),
m_rcasDescriptorSet(m_core.createDescriptorSet(getDescriptorBindings())),
m_easuConstants(m_core.createBuffer<FSRConstants>(
BufferType::UNIFORM,1,
BufferMemoryType::HOST_VISIBLE
)),
m_rcasConstants(m_core.createBuffer<FSRConstants>(
BufferType::UNIFORM,1,
BufferMemoryType::HOST_VISIBLE
)),
m_intermediateImage(),
m_sampler(m_core.createSampler(
SamplerFilterType::LINEAR,
SamplerFilterType::LINEAR,
SamplerMipmapMode::NEAREST,
SamplerAddressMode::CLAMP_TO_EDGE
)),
m_hdr(false),
m_sharpness(0.875f) {
vkcv::shader::GLSLCompiler easuCompiler;
vkcv::shader::GLSLCompiler rcasCompiler;
const auto& features = m_core.getContext().getPhysicalDevice().getFeatures2();
const bool float16Support = (
checkFeatures<vk::PhysicalDeviceFloat16Int8FeaturesKHR>(
reinterpret_cast<const vk::BaseInStructure*>(&features),
vk::StructureType::ePhysicalDeviceShaderFloat16Int8FeaturesKHR,
checkFloat16
) &&
checkFeatures<vk::PhysicalDevice16BitStorageFeaturesKHR>(
reinterpret_cast<const vk::BaseInStructure*>(&features),
vk::StructureType::ePhysicalDevice16BitStorageFeaturesKHR,
check16Storage
)
) || (true); // check doesn't work because chain is empty
if (!float16Support) {
easuCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1");
rcasCompiler.setDefine("SAMPLE_SLOW_FALLBACK", "1");
}
easuCompiler.setDefine("SAMPLE_EASU", "1");
rcasCompiler.setDefine("SAMPLE_RCAS", "1");
{
ShaderProgram program;
compileFSRShader(easuCompiler, [&program](vkcv::ShaderStage shaderStage,
const std::filesystem::path& path) {
program.addShader(shaderStage, path);
});
m_easuPipeline = m_core.createComputePipeline(program, {
m_core.getDescriptorSet(m_easuDescriptorSet).layout
});
DescriptorWrites writes;
writes.uniformBufferWrites.emplace_back(
0, m_easuConstants.getHandle(),true
);
writes.samplerWrites.emplace_back(3, m_sampler);
m_core.writeDescriptorSet(m_easuDescriptorSet, writes);
}
{
ShaderProgram program;
compileFSRShader(rcasCompiler, [&program](vkcv::ShaderStage shaderStage,
const std::filesystem::path& path) {
program.addShader(shaderStage, path);
});
m_rcasPipeline = m_core.createComputePipeline(program, {
m_core.getDescriptorSet(m_rcasDescriptorSet).layout
});
DescriptorWrites writes;
writes.uniformBufferWrites.emplace_back(
0, m_rcasConstants.getHandle(),true
);
writes.samplerWrites.emplace_back(3, m_sampler);
m_core.writeDescriptorSet(m_rcasDescriptorSet, writes);
}
}
void FSRUpscaling::recordUpscaling(const CommandStreamHandle& cmdStream,
const ImageHandle& input,
const ImageHandle& output) {
const uint32_t inputWidth = m_core.getImageWidth(input);
const uint32_t inputHeight = m_core.getImageHeight(input);
const uint32_t outputWidth = m_core.getImageWidth(output);
const uint32_t outputHeight = m_core.getImageHeight(output);
if ((!m_intermediateImage) ||
(outputWidth != m_core.getImageWidth(m_intermediateImage)) ||
(outputHeight != m_core.getImageHeight(m_intermediateImage))) {
m_intermediateImage = m_core.createImage(
m_core.getImageFormat(output),
outputWidth, outputHeight,1,
false,
true
).getHandle();
m_core.prepareImageForStorage(cmdStream, m_intermediateImage);
}
const bool rcasEnabled = (
(m_sharpness > +0.0f) &&
((inputWidth < outputWidth) || (inputHeight < outputHeight))
);
{
FSRConstants consts = {};
FsrEasuCon(
consts.Const0, consts.Const1, consts.Const2, consts.Const3,
static_cast<AF1>(inputWidth), static_cast<AF1>(inputHeight),
static_cast<AF1>(inputWidth), static_cast<AF1>(inputHeight),
static_cast<AF1>(outputWidth), static_cast<AF1>(outputHeight)
);
consts.Sample[0] = (((m_hdr) && (!rcasEnabled)) ? 1 : 0);
m_easuConstants.fill(&consts);
}
static const uint32_t threadGroupWorkRegionDim = 16;
uint32_t dispatch[3];
dispatch[0] = (outputWidth + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
dispatch[1] = (outputHeight + (threadGroupWorkRegionDim - 1)) / threadGroupWorkRegionDim;
dispatch[2] = 1;
m_core.recordBufferMemoryBarrier(cmdStream, m_easuConstants.getHandle());
if (rcasEnabled) {
{
DescriptorWrites writes;
writes.sampledImageWrites.emplace_back(1, input);
writes.storageImageWrites.emplace_back(2, m_intermediateImage);
m_core.writeDescriptorSet(m_easuDescriptorSet, writes);
}
{
DescriptorWrites writes;
writes.sampledImageWrites.emplace_back(1, m_intermediateImage);
writes.storageImageWrites.emplace_back(2, output);
m_core.writeDescriptorSet(m_rcasDescriptorSet, writes);
}
m_core.recordComputeDispatchToCmdStream(
cmdStream,
m_easuPipeline,
dispatch,
{DescriptorSetUsage(0, m_core.getDescriptorSet(
m_easuDescriptorSet
).vulkanHandle, { 0 })},
PushConstants(0)
);
{
FSRConstants consts = {};
FsrRcasCon(consts.Const0, (1.0f - m_sharpness) * 2.0f);
consts.Sample[0] = (m_hdr ? 1 : 0);
m_rcasConstants.fill(&consts);
}
m_core.recordBufferMemoryBarrier(cmdStream, m_rcasConstants.getHandle());
m_core.prepareImageForSampling(cmdStream, m_intermediateImage);
m_core.recordComputeDispatchToCmdStream(
cmdStream,
m_rcasPipeline,
dispatch,
{DescriptorSetUsage(0, m_core.getDescriptorSet(
m_rcasDescriptorSet
).vulkanHandle, { 0 })},
PushConstants(0)
);
m_core.prepareImageForStorage(cmdStream, m_intermediateImage);
} else {
{
DescriptorWrites writes;
writes.sampledImageWrites.emplace_back(1, input);
writes.storageImageWrites.emplace_back(2, output);
m_core.writeDescriptorSet(m_easuDescriptorSet, writes);
}
m_core.recordComputeDispatchToCmdStream(
cmdStream,
m_easuPipeline,
dispatch,
{DescriptorSetUsage(0, m_core.getDescriptorSet(
m_easuDescriptorSet
).vulkanHandle, { 0 })},
PushConstants(0)
);
}
}
bool FSRUpscaling::isHdrEnabled() const {
return m_hdr;
}
void FSRUpscaling::setHdrEnabled(bool enabled) {
m_hdr = enabled;
}
float FSRUpscaling::getSharpness() const {
return m_sharpness;
}
void FSRUpscaling::setSharpness(float sharpness) {
m_sharpness = (sharpness < 0.0f ? 0.0f : (sharpness > 1.0f ? 1.0f : sharpness));
}
}
#include "vkcv/upscaling/Upscaling.hpp"
namespace vkcv::upscaling {
Upscaling::Upscaling(Core &core) : m_core(core) {}
}
# Add new projects/examples here:
add_subdirectory(bloom)
add_subdirectory(first_triangle)
add_subdirectory(first_mesh)
add_subdirectory(particle_simulation)
add_subdirectory(first_scene)
add_subdirectory(particle_simulation)
add_subdirectory(voxelization)
add_subdirectory(mesh_shader)
add_subdirectory(indirect_dispatch)
bloom
\ No newline at end of file