Renderer: Replace ShaderStage by ShaderModule (a module can handle multiple stages)

This commit is contained in:
Jérôme Leclercq
2021-03-31 11:13:37 +02:00
parent c1d1838336
commit e4aabf309e
25 changed files with 235 additions and 198 deletions

View File

@@ -51,7 +51,7 @@ namespace Nz
for (const auto& shaderEntry : m_pipelineInfo.shaders)
{
if (shaderEntry.uberShader)
renderPipelineInfo.shaderStages.push_back(shaderEntry.uberShader->Get(shaderEntry.enabledConditions));
renderPipelineInfo.shaderModules.push_back(shaderEntry.uberShader->Get(shaderEntry.enabledConditions));
}
renderPipelineInfo.vertexBuffers = vertexBuffers;

View File

@@ -11,8 +11,9 @@
namespace Nz
{
UberShader::UberShader(ShaderAst::StatementPtr shaderAst) :
m_shaderAst(std::move(shaderAst))
UberShader::UberShader(ShaderStageType shaderStage, ShaderAst::StatementPtr shaderAst) :
m_shaderAst(std::move(shaderAst)),
m_shaderStage(shaderStage)
{
//std::size_t conditionCount = m_shaderAst.GetConditionCount();
std::size_t conditionCount = 0;
@@ -34,7 +35,7 @@ namespace Nz
return 0;
}
const std::shared_ptr<ShaderStage>& UberShader::Get(UInt64 combination)
const std::shared_ptr<ShaderModule>& UberShader::Get(UInt64 combination)
{
combination &= m_combinationMask;
@@ -44,7 +45,7 @@ namespace Nz
ShaderWriter::States states;
states.enabledConditions = combination;
std::shared_ptr<ShaderStage> stage = Graphics::Instance()->GetRenderDevice().InstantiateShaderStage(m_shaderAst, std::move(states));
std::shared_ptr<ShaderModule> stage = Graphics::Instance()->GetRenderDevice().InstantiateShaderModule(m_shaderStage, m_shaderAst, std::move(states));
it = m_combinations.emplace(combination, std::move(stage)).first;
}

View File

@@ -10,7 +10,7 @@
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipeline.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
#include <Nazara/OpenGLRenderer/OpenGLShaderStage.hpp>
#include <Nazara/OpenGLRenderer/OpenGLShaderModule.hpp>
#include <Nazara/OpenGLRenderer/OpenGLTexture.hpp>
#include <Nazara/OpenGLRenderer/OpenGLTextureSampler.hpp>
#include <Nazara/OpenGLRenderer/Wrapper/Loader.hpp>
@@ -80,14 +80,14 @@ namespace Nz
return std::make_shared<OpenGLRenderPipelineLayout>(std::move(pipelineLayoutInfo));
}
std::shared_ptr<ShaderStage> OpenGLDevice::InstantiateShaderStage(const ShaderAst& shaderAst, const ShaderWriter::States& states)
std::shared_ptr<ShaderModule> OpenGLDevice::InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states)
{
return std::make_shared<OpenGLShaderStage>(*this, shaderAst, states);
return std::make_shared<OpenGLShaderModule>(*this, shaderStages, shaderAst, states);
}
std::shared_ptr<ShaderStage> OpenGLDevice::InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize)
std::shared_ptr<ShaderModule> OpenGLDevice::InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize)
{
return std::make_shared<OpenGLShaderStage>(*this, type, lang, source, sourceSize);
return std::make_shared<OpenGLShaderModule>(*this, shaderStages, lang, source, sourceSize);
}
std::shared_ptr<Texture> OpenGLDevice::InstantiateTexture(const TextureInfo& params)

View File

@@ -6,7 +6,7 @@
#include <Nazara/Core/ErrorFlags.hpp>
#include <Nazara/OpenGLRenderer/Utils.hpp>
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
#include <Nazara/OpenGLRenderer/OpenGLShaderStage.hpp>
#include <Nazara/OpenGLRenderer/OpenGLShaderModule.hpp>
#include <Nazara/Shader/GlslWriter.hpp>
#include <cassert>
#include <stdexcept>
@@ -21,10 +21,11 @@ namespace Nz
if (!m_program.Create(device))
throw std::runtime_error("failed to create program");
for (const auto& shaderStagePtr : m_pipelineInfo.shaderStages)
for (const auto& shaderModulePtr : m_pipelineInfo.shaderModules)
{
OpenGLShaderStage& shaderStage = static_cast<OpenGLShaderStage&>(*shaderStagePtr);
m_program.AttachShader(shaderStage.GetShader().GetObjectId());
OpenGLShaderModule& shaderModule = static_cast<OpenGLShaderModule&>(*shaderModulePtr);
for (const GL::Shader& shader : shaderModule.GetShaders())
m_program.AttachShader(shader.GetObjectId());
}
m_program.Link();

View File

@@ -0,0 +1,136 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/OpenGLShaderModule.hpp>
#include <Nazara/Core/MemoryView.hpp>
#include <Nazara/OpenGLRenderer/Utils.hpp>
#include <Nazara/Shader/GlslWriter.hpp>
#include <Nazara/Shader/ShaderAstSerializer.hpp>
#include <Nazara/Shader/ShaderLangLexer.hpp>
#include <Nazara/Shader/ShaderLangParser.hpp>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states)
{
NazaraAssert(shaderStages != 0, "at least one shader stage must be specified");
Create(device, shaderStages, shaderAst, states);
}
OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize)
{
NazaraAssert(shaderStages != 0, "at least one shader stage must be specified");
switch (lang)
{
case ShaderLanguage::GLSL:
{
for (std::size_t i = 0; i < ShaderStageTypeCount; ++i)
{
ShaderStageType shaderStage = static_cast<ShaderStageType>(i);
if (shaderStages.Test(shaderStage))
{
NazaraAssert(shaderStages == shaderStage, "when supplying GLSL, only one shader stage type can be specified");
GL::Shader shader;
if (!shader.Create(device, ToOpenGL(shaderStage)))
throw std::runtime_error("failed to create shader"); //< TODO: Handle error message
shader.SetSource(reinterpret_cast<const char*>(source), GLint(sourceSize));
shader.Compile();
CheckCompilationStatus(shader);
m_shaders.emplace_back(std::move(shader));
break;
}
}
break;
}
case ShaderLanguage::NazaraBinary:
{
auto shader = ShaderAst::UnserializeShader(source, sourceSize);
Create(device, shaderStages, shader, {});
break;
}
case ShaderLanguage::NazaraShader:
{
std::vector<Nz::ShaderLang::Token> tokens = Nz::ShaderLang::Tokenize(std::string_view(static_cast<const char*>(source), sourceSize));
Nz::ShaderLang::Parser parser;
Nz::ShaderAst::StatementPtr shaderAst = parser.Parse(tokens);
Create(device, shaderStages, shaderAst, {});
break;
}
case ShaderLanguage::SpirV:
{
throw std::runtime_error("TODO");
// TODO: Parse SpirV to extract entry points?
/*if (!device.GetReferenceContext().IsExtensionSupported(GL::Extension::SpirV))
throw std::runtime_error("SpirV is not supported by this OpenGL implementation");
m_shader.SetBinarySource(GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, source, GLsizei(sourceSize));
m_shader.SpecializeShader("main", 0U, nullptr, nullptr);*/
break;
}
default:
throw std::runtime_error("Unsupported shader language");
}
}
void OpenGLShaderModule::CheckCompilationStatus(GL::Shader& shader)
{
std::string errorLog;
if (!shader.GetCompilationStatus(&errorLog))
throw std::runtime_error("Failed to compile shader: " + errorLog);
}
void OpenGLShaderModule::Create(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::StatementPtr& shaderAst, const ShaderWriter::States& states)
{
const auto& context = device.GetReferenceContext();
const auto& contextParams = context.GetParams();
GlslWriter::Environment env;
env.glES = (contextParams.type == GL::ContextType::OpenGL_ES);
env.glMajorVersion = contextParams.glMajorVersion;
env.glMinorVersion = contextParams.glMinorVersion;
env.extCallback = [&](const std::string_view& ext)
{
return context.IsExtensionSupported(std::string(ext));
};
env.flipYPosition = true;
GlslWriter writer;
writer.SetEnv(env);
for (std::size_t i = 0; i < ShaderStageTypeCount; ++i)
{
ShaderStageType shaderStage = static_cast<ShaderStageType>(i);
if (shaderStages.Test(shaderStage))
{
GL::Shader shader;
if (!shader.Create(device, ToOpenGL(shaderStage)))
throw std::runtime_error("failed to create shader"); //< TODO: Handle error message
std::string code = writer.Generate(shaderStage, shaderAst, states);
shader.SetSource(code.data(), code.size());
shader.Compile();
CheckCompilationStatus(shader);
m_shaders.emplace_back(std::move(shader));
}
}
}
}

View File

@@ -1,94 +0,0 @@
// Copyright (C) 2020 Jérôme Leclercq
// This file is part of the "Nazara Engine - OpenGL Renderer"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/OpenGLRenderer/OpenGLShaderStage.hpp>
#include <Nazara/Core/MemoryView.hpp>
#include <Nazara/OpenGLRenderer/Utils.hpp>
#include <Nazara/Shader/GlslWriter.hpp>
#include <Nazara/Shader/ShaderAst.hpp>
#include <Nazara/Shader/ShaderAstSerializer.hpp>
#include <stdexcept>
#include <Nazara/OpenGLRenderer/Debug.hpp>
namespace Nz
{
OpenGLShaderStage::OpenGLShaderStage(OpenGLDevice& device, const ShaderAst& shaderAst, const ShaderWriter::States& states)
{
if (!m_shader.Create(device, ToOpenGL(shaderAst.GetStage())))
throw std::runtime_error("failed to create shader"); //< TODO: Handle error message
Create(device, shaderAst, states);
CheckCompilationStatus();
}
OpenGLShaderStage::OpenGLShaderStage(OpenGLDevice& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize)
{
if (!m_shader.Create(device, ToOpenGL(type)))
throw std::runtime_error("failed to create shader"); //< TODO: Handle error message
switch (lang)
{
case ShaderLanguage::GLSL:
m_shader.SetSource(reinterpret_cast<const char*>(source), GLint(sourceSize));
m_shader.Compile();
break;
case ShaderLanguage::NazaraBinary:
{
auto shader = UnserializeShader(source, sourceSize);
if (shader.GetStage() != type)
throw std::runtime_error("incompatible shader stage");
Create(device, shader, {});
break;
}
case ShaderLanguage::SpirV:
{
if (!device.GetReferenceContext().IsExtensionSupported(GL::Extension::SpirV))
throw std::runtime_error("SpirV is not supported by this OpenGL implementation");
m_shader.SetBinarySource(GL_SHADER_BINARY_FORMAT_SPIR_V_ARB, source, GLsizei(sourceSize));
m_shader.SpecializeShader("main", 0U, nullptr, nullptr);
break;
}
default:
throw std::runtime_error("Unsupported shader language");
}
CheckCompilationStatus();
}
void OpenGLShaderStage::CheckCompilationStatus()
{
std::string errorLog;
if (!m_shader.GetCompilationStatus(&errorLog))
throw std::runtime_error("Failed to compile shader: " + errorLog);
}
void OpenGLShaderStage::Create(OpenGLDevice& device, const ShaderAst& shaderAst, const ShaderWriter::States& states)
{
const auto& context = device.GetReferenceContext();
const auto& contextParams = context.GetParams();
GlslWriter::Environment env;
env.glES = (contextParams.type == GL::ContextType::OpenGL_ES);
env.glMajorVersion = contextParams.glMajorVersion;
env.glMinorVersion = contextParams.glMinorVersion;
env.extCallback = [&](const std::string_view& ext)
{
return context.IsExtensionSupported(std::string(ext));
};
env.flipYPosition = true;
GlslWriter writer;
writer.SetEnv(env);
std::string code = writer.Generate(shaderAst, states);
m_shader.SetSource(code.data(), code.size());
m_shader.Compile();
}
}

View File

@@ -11,7 +11,7 @@ namespace Nz
{
RenderDevice::~RenderDevice() = default;
std::shared_ptr<ShaderStage> RenderDevice::InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const std::filesystem::path& sourcePath)
std::shared_ptr<ShaderModule> RenderDevice::InstantiateShaderModule(ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const std::filesystem::path& sourcePath)
{
File file(sourcePath);
if (!file.Open(OpenMode_ReadOnly | OpenMode_Text))
@@ -29,6 +29,6 @@ namespace Nz
return {};
}
return InstantiateShaderStage(type, lang, source.data(), source.size());
return InstantiateShaderModule(shaderStages, lang, source.data(), source.size());
}
}

View File

@@ -2,10 +2,10 @@
// This file is part of the "Nazara Engine - Renderer module"
// For conditions of distribution and use, see copyright notice in Config.hpp
#include <Nazara/Renderer/ShaderStage.hpp>
#include <Nazara/Renderer/ShaderModule.hpp>
#include <Nazara/Renderer/Debug.hpp>
namespace Nz
{
ShaderStage::~ShaderStage() = default;
ShaderModule::~ShaderModule() = default;
}

View File

@@ -51,7 +51,7 @@ namespace Nz
return pipelineLayout;
}
std::shared_ptr<ShaderStage> VulkanDevice::InstantiateShaderStage(const ShaderAst& shaderAst, const ShaderWriter::States& states)
std::shared_ptr<ShaderModule> VulkanDevice::InstantiateShaderModule(const ShaderAst& shaderAst, const ShaderWriter::States& states)
{
auto stage = std::make_shared<VulkanShaderStage>();
if (!stage->Create(*this, shaderAst, states))
@@ -60,7 +60,7 @@ namespace Nz
return stage;
}
std::shared_ptr<ShaderStage> VulkanDevice::InstantiateShaderStage(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize)
std::shared_ptr<ShaderModule> VulkanDevice::InstantiateShaderModule(ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize)
{
auto stage = std::make_shared<VulkanShaderStage>();
if (!stage->Create(*this, type, lang, source, sourceSize))

View File

@@ -164,7 +164,7 @@ namespace Nz
{
std::vector<VkPipelineShaderStageCreateInfo> shaderStageCreateInfos;
for (auto&& stagePtr : pipelineInfo.shaderStages)
for (auto&& stagePtr : pipelineInfo.shaderModules)
{
Nz::VulkanShaderStage& vulkanStage = *static_cast<Nz::VulkanShaderStage*>(stagePtr.get());

View File

@@ -20,14 +20,7 @@ namespace Nz
writer.SetEnv(env);
std::vector<UInt32> code = writer.Generate(shader, states);
if (!m_shaderModule.Create(device, code.data(), code.size() * sizeof(UInt32)))
{
NazaraError("Failed to create shader module");
return false;
}
return true;
return Create(device, m_stage, ShaderLanguage::SpirV, code.data(), code.size() * sizeof(UInt32));
}
bool VulkanShaderStage::Create(Vk::Device& device, ShaderStageType type, ShaderLanguage lang, const void* source, std::size_t sourceSize)