stk-code_catmod/lib/graphics_engine/src/ge_vulkan_shader_manager.cpp
2022-07-21 12:26:06 +08:00

245 lines
8.2 KiB
C++

#include "ge_vulkan_shader_manager.hpp"
#include "ge_main.hpp"
#include "ge_vulkan_driver.hpp"
#include "ge_vulkan_features.hpp"
#include <algorithm>
#include <map>
#include <sstream>
#include <string>
#include <stdexcept>
#include "IFileSystem.h"
namespace GE
{
namespace GEVulkanShaderManager
{
// ============================================================================
GEVulkanDriver* g_vk = NULL;
irr::io::IFileSystem* g_file_system = NULL;
std::string g_predefines = "";
// More when PBR is used later
uint32_t g_mesh_texture_layer = 2;
uint32_t g_sampler_size = 256;
std::map<std::string, VkShaderModule> g_shaders;
} // GEVulkanShaderManager
// ============================================================================
shaderc_include_result* showError(const char* message)
{
shaderc_include_result* err = new shaderc_include_result;
err->source_name = "";
err->source_name_length = 0;
err->content = message;
err->content_length = strlen(message);
err->user_data = NULL;
return err;
} // showError
// ============================================================================
class FileIncluder : public shaderc::CompileOptions::IncluderInterface
{
private:
std::vector<std::string> m_shader_fullpath;
std::vector<std::string> m_shader_data;
public:
// ------------------------------------------------------------------------
shaderc_include_result* GetInclude(const char* requested_source,
shaderc_include_type type,
const char* requesting_source,
size_t include_depth)
{
if (type != shaderc_include_type_relative)
return showError("Only relateive included supported");
std::string path = requesting_source;
size_t pos = path.find_last_of('/');
if (pos == std::string::npos)
pos = path.find_last_of('\\');
if (pos == std::string::npos)
throw std::runtime_error(std::string("Invalid path: ") + path);
m_shader_fullpath.push_back(std::string());
std::string& shader_fullpath = m_shader_fullpath.back();
shader_fullpath = path.substr(0, pos) + '/' + requested_source;
irr::io::IReadFile* r = GEVulkanShaderManager::
g_file_system->createAndOpenFile(shader_fullpath.c_str());
if (!r)
{
throw std::runtime_error(std::string("File ") + shader_fullpath +
" is missing");
}
m_shader_data.push_back(std::string());
std::string& shader_data = m_shader_data.back();
shader_data.resize(r->getSize());
int nb_read = 0;
if ((nb_read = r->read(&shader_data[0], r->getSize())) != r->getSize())
{
r->drop();
throw std::runtime_error(std::string("File ") +
requested_source + " failed to be read");
}
r->drop();
shaderc_include_result* result = new shaderc_include_result;
result->source_name = shader_fullpath.c_str();
result->source_name_length = shader_fullpath.size();
result->content = shader_data.c_str();
result->content_length = shader_data.size();
result->user_data = NULL;
return result;
}
// ------------------------------------------------------------------------
void ReleaseInclude(shaderc_include_result* include_result)
{
delete include_result;
}
}; // FileIncluder
// ============================================================================
void GEVulkanShaderManager::init(GEVulkanDriver* vk)
{
g_vk = vk;
g_file_system = vk->getFileSystem();
std::ostringstream oss;
oss << "#version 450\n";
oss << "#define SAMPLER_SIZE " << g_sampler_size << "\n";
oss << "#define TOTAL_MESH_TEXTURE_LAYER " << g_mesh_texture_layer << "\n";
if (GEVulkanFeatures::supportsBindTexturesAtOnce())
oss << "#define BIND_TEXTURES_AT_ONCE\n";
if (GEVulkanFeatures::supportsBindMeshTexturesAtOnce())
oss << "#define BIND_MESH_TEXTURES_AT_ONCE\n";
if (GEVulkanFeatures::supportsDifferentTexturePerDraw())
{
oss << "#extension GL_EXT_nonuniform_qualifier : enable\n";
oss << "#define GE_SAMPLE_TEX_INDEX nonuniformEXT\n";
}
else
oss << "#define GE_SAMPLE_TEX_INDEX int\n";
g_predefines = oss.str();
loadAllShaders();
} // init
// ----------------------------------------------------------------------------
void GEVulkanShaderManager::destroy()
{
if (!g_vk)
return;
for (auto& p : g_shaders)
vkDestroyShaderModule(g_vk->getDevice(), p.second, NULL);
g_shaders.clear();
} // destroy
// ----------------------------------------------------------------------------
void GEVulkanShaderManager::loadAllShaders()
{
irr::io::IFileList* files = g_file_system->createFileList(
getShaderFolder().c_str());
for (unsigned i = 0; i < files->getFileCount(); i++)
{
if (files->isDirectory(i))
continue;
std::string filename = files->getFileName(i).c_str();
std::string ext = filename.substr(filename.find_last_of(".") + 1);
shaderc_shader_kind kind;
if (ext == "vert")
kind = shaderc_vertex_shader;
else if (ext == "frag")
kind = shaderc_fragment_shader;
else if (ext == "comp")
kind = shaderc_compute_shader;
else if (ext == "tesc")
kind = shaderc_tess_control_shader;
else if (ext == "tese")
kind = shaderc_tess_evaluation_shader;
else
continue;
g_shaders[filename] = loadShader(kind, filename);
}
files->drop();
} // loadAllShaders
// ----------------------------------------------------------------------------
VkShaderModule GEVulkanShaderManager::loadShader(shaderc_shader_kind kind,
const std::string& name)
{
std::string shader_fullpath = getShaderFolder() + name;
irr::io::IReadFile* r =
g_file_system->createAndOpenFile(shader_fullpath.c_str());
if (!r)
{
throw std::runtime_error(std::string("File ") + shader_fullpath +
" is missing");
}
std::string shader_data;
shader_data.resize(r->getSize());
int nb_read = 0;
if ((nb_read = r->read(&shader_data[0], r->getSize())) != r->getSize())
{
r->drop();
throw std::runtime_error(
std::string("File ") + name + " failed to be read");
}
r->drop();
shader_data = g_predefines + shader_data;
shaderc::Compiler compiler;
FileIncluder* fi = new FileIncluder;
shaderc::CompileOptions options;
options.SetIncluder(std::unique_ptr<
shaderc::CompileOptions::IncluderInterface>(fi));
shaderc::SpvCompilationResult module = compiler.CompileGlslToSpv(
shader_data, kind, shader_fullpath.c_str(), options);
if (module.GetCompilationStatus() != shaderc_compilation_status_success)
throw std::runtime_error(module.GetErrorMessage());
std::vector<uint32_t> shader_bytecode(module.cbegin(), module.cend());
VkShaderModuleCreateInfo create_info = {};
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
create_info.pNext = NULL;
create_info.codeSize = shader_bytecode.size() * sizeof(uint32_t);
create_info.pCode = shader_bytecode.data();
VkShaderModule shader_module;
if (vkCreateShaderModule(g_vk->getDevice(), &create_info, NULL,
&shader_module) != VK_SUCCESS)
{
throw std::runtime_error(
std::string("vkCreateShaderModule failed for ") + name);
}
return shader_module;
} // loadShader
// ----------------------------------------------------------------------------
unsigned GEVulkanShaderManager::getSamplerSize()
{
return g_sampler_size;
} // getSamplerSize
// ----------------------------------------------------------------------------
unsigned GEVulkanShaderManager::getMeshTextureLayer()
{
return g_mesh_texture_layer;
} // getMeshTextureLayer
// ----------------------------------------------------------------------------
VkShaderModule GEVulkanShaderManager::getShader(const std::string& filename)
{
return g_shaders.at(filename);
} // getShader
}