Add initial support for shader binding sets (WIP)
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <Nazara/Core/StackArray.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLCommandPool.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPass.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLRenderPipelineLayout.hpp>
|
||||
#include <Nazara/OpenGLRenderer/OpenGLVaoCache.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/Context.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Wrapper/VertexArray.hpp>
|
||||
@@ -242,7 +243,10 @@ namespace Nz
|
||||
void OpenGLCommandBuffer::ApplyStates(const GL::Context& context, const DrawStates& states)
|
||||
{
|
||||
states.pipeline->Apply(context, states.shouldFlipY);
|
||||
states.shaderBindings->Apply(context);
|
||||
|
||||
unsigned int setIndex = 0;
|
||||
for (const auto& [pipelineLayout, shaderBinding] : states.shaderBindings)
|
||||
shaderBinding->Apply(*pipelineLayout, setIndex++, context);
|
||||
|
||||
if (states.scissorRegion)
|
||||
context.SetScissorBox(states.scissorRegion->x, states.scissorRegion->y, states.scissorRegion->width, states.scissorRegion->height);
|
||||
|
||||
@@ -38,11 +38,19 @@ namespace Nz
|
||||
m_commandBuffer.BindPipeline(&glPipeline);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::BindShaderBinding(const ShaderBinding& binding)
|
||||
void OpenGLCommandBufferBuilder::BindShaderBinding(UInt32 set, const ShaderBinding& binding)
|
||||
{
|
||||
const OpenGLShaderBinding& glBinding = static_cast<const OpenGLShaderBinding&>(binding);
|
||||
|
||||
m_commandBuffer.BindShaderBinding(&glBinding);
|
||||
m_commandBuffer.BindShaderBinding(glBinding.GetOwner(), set, &glBinding);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::BindShaderBinding(const RenderPipelineLayout& pipelineLayout, UInt32 set, const ShaderBinding& binding)
|
||||
{
|
||||
const OpenGLRenderPipelineLayout& glPipelineLayout = static_cast<const OpenGLRenderPipelineLayout&>(pipelineLayout);
|
||||
const OpenGLShaderBinding& glBinding = static_cast<const OpenGLShaderBinding&>(binding);
|
||||
|
||||
m_commandBuffer.BindShaderBinding(glPipelineLayout, set, &glBinding);
|
||||
}
|
||||
|
||||
void OpenGLCommandBufferBuilder::BindVertexBuffer(UInt32 binding, Nz::AbstractBuffer* vertexBuffer, UInt64 offset)
|
||||
|
||||
@@ -19,19 +19,23 @@ namespace Nz
|
||||
m_pipelineInfo(std::move(pipelineInfo)),
|
||||
m_isViewportFlipped(false)
|
||||
{
|
||||
OpenGLRenderPipelineLayout& pipelineLayout = static_cast<OpenGLRenderPipelineLayout&>(*m_pipelineInfo.pipelineLayout);
|
||||
|
||||
if (!m_program.Create(device))
|
||||
throw std::runtime_error("failed to create program");
|
||||
|
||||
const GL::Context* activeContext = GL::Context::GetCurrentContext();
|
||||
assert(activeContext);
|
||||
|
||||
// Enable pipeline states before compiling and linking the program, for drivers which embed some pipeline states into the shader binary (to avoid recompilation later)
|
||||
activeContext->UpdateStates(m_pipelineInfo, false);
|
||||
|
||||
ShaderStageTypeFlags stageFlags;
|
||||
|
||||
for (const auto& shaderModulePtr : m_pipelineInfo.shaderModules)
|
||||
{
|
||||
OpenGLShaderModule& shaderModule = static_cast<OpenGLShaderModule&>(*shaderModulePtr);
|
||||
for (const auto& shaderEntry : shaderModule.GetShaders())
|
||||
{
|
||||
m_program.AttachShader(shaderEntry.shader.GetObjectId());
|
||||
stageFlags |= shaderEntry.stage;
|
||||
}
|
||||
stageFlags |= shaderModule.Attach(m_program, pipelineLayout.GetBindingMapping());
|
||||
}
|
||||
|
||||
// OpenGL ES programs must have both vertex and fragment shaders or a compute shader or a mesh and fragment shader.
|
||||
@@ -42,12 +46,8 @@ namespace Nz
|
||||
if (!stageFlags.Test(stage))
|
||||
{
|
||||
ShaderAst::StatementPtr dummyAst = ShaderBuilder::DeclareFunction(stage, "main", {}, {});
|
||||
OpenGLShaderModule shaderModule(device, stage, dummyAst);
|
||||
for (const auto& shaderEntry : shaderModule.GetShaders())
|
||||
{
|
||||
m_program.AttachShader(shaderEntry.shader.GetObjectId());
|
||||
stageFlags |= shaderEntry.stage;
|
||||
}
|
||||
OpenGLShaderModule shaderModule(device, stage, *dummyAst);
|
||||
stageFlags |= shaderModule.Attach(m_program, pipelineLayout.GetBindingMapping());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Nz
|
||||
{
|
||||
context.UpdateStates(m_pipelineInfo, flipViewport);
|
||||
context.BindProgram(m_program.GetObjectId()); //< Bind program after states (for shader caching)
|
||||
if (m_isViewportFlipped != flipViewport)
|
||||
if (m_flipYUniformLocation != -1 && m_isViewportFlipped != flipViewport)
|
||||
{
|
||||
m_program.Uniform(m_flipYUniformLocation, (flipViewport) ? -1.f : 1.f);
|
||||
m_isViewportFlipped = flipViewport;
|
||||
|
||||
@@ -14,25 +14,17 @@
|
||||
namespace Nz
|
||||
{
|
||||
OpenGLRenderPipelineLayout::OpenGLRenderPipelineLayout(RenderPipelineLayoutInfo layoutInfo) :
|
||||
m_textureDescriptorCount(0),
|
||||
m_uniformBufferDescriptorCount(0),
|
||||
m_maxDescriptorCount(0),
|
||||
m_layoutInfo(std::move(layoutInfo))
|
||||
{
|
||||
for (const auto& bindingInfo : m_layoutInfo.bindings)
|
||||
// Build binding mapping (vulkan-like set | binding => GL binding) and register max descriptor count
|
||||
unsigned int bindingIndex = 0;
|
||||
for (const auto& binding : m_layoutInfo.bindings)
|
||||
{
|
||||
switch (bindingInfo.type)
|
||||
{
|
||||
case ShaderBindingType::Texture:
|
||||
m_textureDescriptorCount++;
|
||||
break;
|
||||
UInt64 bindingKey = UInt64(binding.setIndex) << 32 | UInt64(binding.bindingIndex);
|
||||
|
||||
case ShaderBindingType::UniformBuffer:
|
||||
m_uniformBufferDescriptorCount++;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("unknown binding type 0x" + NumberToString(UnderlyingCast(bindingInfo.type), 16));
|
||||
}
|
||||
m_bindingMapping[bindingKey] = bindingIndex++;
|
||||
m_maxDescriptorCount = std::max<std::size_t>(m_maxDescriptorCount, binding.bindingIndex + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,11 +37,11 @@ namespace Nz
|
||||
}
|
||||
}
|
||||
|
||||
ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateShaderBinding()
|
||||
ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateShaderBinding(UInt32 setIndex)
|
||||
{
|
||||
for (std::size_t i = 0; i < m_descriptorPools.size(); ++i)
|
||||
{
|
||||
ShaderBindingPtr bindingPtr = AllocateFromPool(i);
|
||||
ShaderBindingPtr bindingPtr = AllocateFromPool(i, setIndex);
|
||||
if (!bindingPtr)
|
||||
continue;
|
||||
|
||||
@@ -60,7 +52,7 @@ namespace Nz
|
||||
std::size_t newPoolIndex = m_descriptorPools.size();
|
||||
AllocatePool();
|
||||
|
||||
ShaderBindingPtr bindingPtr = AllocateFromPool(newPoolIndex);
|
||||
ShaderBindingPtr bindingPtr = AllocateFromPool(newPoolIndex, setIndex);
|
||||
if (!bindingPtr)
|
||||
throw std::runtime_error("Failed to allocate shader binding");
|
||||
|
||||
@@ -74,14 +66,15 @@ namespace Nz
|
||||
DescriptorPool pool;
|
||||
pool.freeBindings.Resize(MaxSet, true);
|
||||
pool.storage = std::make_unique<DescriptorPool::BindingStorage[]>(MaxSet);
|
||||
pool.textureDescriptor.resize(m_textureDescriptorCount * MaxSet);
|
||||
pool.uniformBufferDescriptor.resize(m_uniformBufferDescriptorCount * MaxSet);
|
||||
pool.descriptors.resize(m_maxDescriptorCount * MaxSet);
|
||||
|
||||
return m_descriptorPools.emplace_back(std::move(pool));
|
||||
}
|
||||
|
||||
ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateFromPool(std::size_t poolIndex)
|
||||
ShaderBindingPtr OpenGLRenderPipelineLayout::AllocateFromPool(std::size_t poolIndex, UInt32 /*setIndex*/)
|
||||
{
|
||||
//FIXME: Make use of set index to use less memory
|
||||
|
||||
auto& pool = m_descriptorPools[poolIndex];
|
||||
|
||||
std::size_t freeBindingId = pool.freeBindings.FindFirst();
|
||||
@@ -94,24 +87,24 @@ namespace Nz
|
||||
return ShaderBindingPtr(PlacementNew(freeBindingMemory, *this, poolIndex, freeBindingId));
|
||||
}
|
||||
|
||||
auto OpenGLRenderPipelineLayout::GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t textureIndex) -> TextureDescriptor&
|
||||
auto OpenGLRenderPipelineLayout::GetTextureDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> TextureDescriptor&
|
||||
{
|
||||
assert(poolIndex < m_descriptorPools.size());
|
||||
auto& pool = m_descriptorPools[poolIndex];
|
||||
assert(!pool.freeBindings.Test(bindingIndex));
|
||||
assert(textureIndex < m_textureDescriptorCount);
|
||||
assert(descriptorIndex < m_maxDescriptorCount);
|
||||
|
||||
return pool.textureDescriptor[bindingIndex * m_textureDescriptorCount + textureIndex];
|
||||
return pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex].emplace<TextureDescriptor>();
|
||||
}
|
||||
|
||||
auto OpenGLRenderPipelineLayout::GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t uniformBufferIndex) -> UniformBufferDescriptor&
|
||||
auto OpenGLRenderPipelineLayout::GetUniformBufferDescriptor(std::size_t poolIndex, std::size_t bindingIndex, std::size_t descriptorIndex) -> UniformBufferDescriptor&
|
||||
{
|
||||
assert(poolIndex < m_descriptorPools.size());
|
||||
auto& pool = m_descriptorPools[poolIndex];
|
||||
assert(!pool.freeBindings.Test(bindingIndex));
|
||||
assert(uniformBufferIndex < m_uniformBufferDescriptorCount);
|
||||
assert(descriptorIndex < m_maxDescriptorCount);
|
||||
|
||||
return pool.uniformBufferDescriptor[bindingIndex * m_uniformBufferDescriptorCount + uniformBufferIndex];
|
||||
return pool.descriptors[bindingIndex * m_maxDescriptorCount + descriptorIndex].emplace<UniformBufferDescriptor>();
|
||||
}
|
||||
|
||||
void OpenGLRenderPipelineLayout::Release(ShaderBinding& binding)
|
||||
|
||||
@@ -13,68 +13,66 @@
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
void OpenGLShaderBinding::Apply(const GL::Context& context) const
|
||||
void OpenGLShaderBinding::Apply(const OpenGLRenderPipelineLayout& pipelineLayout, UInt32 setIndex, const GL::Context& context) const
|
||||
{
|
||||
for (std::size_t i = 0; i < m_owner.GetTextureDescriptorCount(); ++i)
|
||||
//TODO: Check layout compaitiblity
|
||||
const auto& bindingMapping = pipelineLayout.GetBindingMapping();
|
||||
const auto& layoutInfo = pipelineLayout.GetLayoutInfo();
|
||||
|
||||
m_owner.ForEachDescriptor(m_poolIndex, m_bindingIndex, [&](UInt32 bindingIndex, auto&& descriptor)
|
||||
{
|
||||
const auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, i);
|
||||
using DescriptorType = std::decay_t<decltype(descriptor)>;
|
||||
|
||||
UInt32 textureIndex = textureDescriptor.bindingIndex;
|
||||
if (textureIndex == OpenGLRenderPipelineLayout::InvalidIndex)
|
||||
continue;
|
||||
auto bindingIt = std::find_if(layoutInfo.bindings.begin(), layoutInfo.bindings.end(), [&](const auto& binding) { return binding.setIndex == setIndex && binding.bindingIndex == bindingIndex; });
|
||||
if (bindingIt == layoutInfo.bindings.end())
|
||||
throw std::runtime_error("invalid binding index");
|
||||
|
||||
context.BindSampler(textureIndex, textureDescriptor.sampler);
|
||||
context.BindTexture(textureIndex, textureDescriptor.textureTarget, textureDescriptor.texture);
|
||||
}
|
||||
const auto& bindingInfo = *bindingIt;
|
||||
|
||||
for (std::size_t i = 0; i < m_owner.GetUniformBufferDescriptorCount(); ++i)
|
||||
{
|
||||
const auto& uboDescriptor = m_owner.GetUniformBufferDescriptor(m_poolIndex, m_bindingIndex, i);
|
||||
auto bindingMappingIt = bindingMapping.find(UInt64(setIndex) << 32 | bindingIndex);
|
||||
assert(bindingMappingIt != bindingMapping.end());
|
||||
|
||||
UInt32 uboIndex = uboDescriptor.bindingIndex;
|
||||
if (uboIndex == OpenGLRenderPipelineLayout::InvalidIndex)
|
||||
continue;
|
||||
UInt32 bindingPoint = bindingMappingIt->second;
|
||||
|
||||
context.BindUniformBuffer(uboDescriptor.bindingIndex, uboDescriptor.buffer, uboDescriptor.offset, uboDescriptor.size);
|
||||
}
|
||||
if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::TextureDescriptor>)
|
||||
{
|
||||
if (bindingInfo.type != ShaderBindingType::Texture)
|
||||
throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not a texture");
|
||||
|
||||
context.BindSampler(bindingPoint, descriptor.sampler);
|
||||
context.BindTexture(bindingPoint, descriptor.textureTarget, descriptor.texture);
|
||||
}
|
||||
else if constexpr (std::is_same_v<DescriptorType, OpenGLRenderPipelineLayout::UniformBufferDescriptor>)
|
||||
{
|
||||
if (bindingInfo.type != ShaderBindingType::UniformBuffer)
|
||||
throw std::runtime_error("descriptor (set=" + std::to_string(setIndex) + ", binding=" + std::to_string(bindingIndex) + ") is not an uniform buffer");
|
||||
|
||||
context.BindUniformBuffer(bindingPoint, descriptor.buffer, descriptor.offset, descriptor.size);
|
||||
}
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLShaderBinding::Update(const Binding* bindings, std::size_t bindingCount)
|
||||
{
|
||||
const auto& layoutInfo = m_owner.GetLayoutInfo();
|
||||
|
||||
for (std::size_t i = 0; i < bindingCount; ++i)
|
||||
{
|
||||
const Binding& binding = bindings[i];
|
||||
|
||||
assert(binding.bindingIndex < layoutInfo.bindings.size());
|
||||
const auto& bindingDesc = layoutInfo.bindings[binding.bindingIndex];
|
||||
|
||||
std::size_t resourceIndex = 0;
|
||||
for (std::size_t j = binding.bindingIndex; j > 0; --j)
|
||||
|
||||
std::visit([&](auto&& arg)
|
||||
{
|
||||
// Use j-1 to prevent underflow in for loop
|
||||
if (layoutInfo.bindings[j - 1].type == bindingDesc.type)
|
||||
resourceIndex++;
|
||||
}
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
switch (bindingDesc.type)
|
||||
{
|
||||
case ShaderBindingType::Texture:
|
||||
if constexpr (std::is_same_v<T, TextureBinding>)
|
||||
{
|
||||
if (!std::holds_alternative<TextureBinding>(binding.content))
|
||||
throw std::runtime_error("binding #" + std::to_string(binding.bindingIndex) + " is a texture but another binding type has been supplied");
|
||||
auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex);
|
||||
|
||||
const TextureBinding& texBinding = std::get<TextureBinding>(binding.content);
|
||||
|
||||
auto& textureDescriptor = m_owner.GetTextureDescriptor(m_poolIndex, m_bindingIndex, resourceIndex);
|
||||
textureDescriptor.bindingIndex = UInt32(binding.bindingIndex);
|
||||
|
||||
if (OpenGLTexture* glTexture = static_cast<OpenGLTexture*>(texBinding.texture))
|
||||
if (OpenGLTexture* glTexture = static_cast<OpenGLTexture*>(arg.texture))
|
||||
{
|
||||
textureDescriptor.texture = glTexture->GetTexture().GetObjectId();
|
||||
|
||||
if (OpenGLTextureSampler* glSampler = static_cast<OpenGLTextureSampler*>(texBinding.sampler))
|
||||
if (OpenGLTextureSampler* glSampler = static_cast<OpenGLTextureSampler*>(arg.sampler))
|
||||
textureDescriptor.sampler = glSampler->GetSampler(glTexture->GetLevelCount() > 1).GetObjectId();
|
||||
else
|
||||
textureDescriptor.sampler = 0;
|
||||
@@ -87,23 +85,14 @@ namespace Nz
|
||||
textureDescriptor.texture = 0;
|
||||
textureDescriptor.textureTarget = GL::TextureTarget::Target2D;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ShaderBindingType::UniformBuffer:
|
||||
else if constexpr (std::is_same_v<T, UniformBufferBinding>)
|
||||
{
|
||||
if (!std::holds_alternative<UniformBufferBinding>(binding.content))
|
||||
throw std::runtime_error("binding #" + std::to_string(binding.bindingIndex) + " is an uniform buffer but another binding type has been supplied");
|
||||
auto& uboDescriptor = m_owner.GetUniformBufferDescriptor(m_poolIndex, m_bindingIndex, binding.bindingIndex);
|
||||
uboDescriptor.offset = arg.offset;
|
||||
uboDescriptor.size = arg.range;
|
||||
|
||||
const UniformBufferBinding& uboBinding = std::get<UniformBufferBinding>(binding.content);
|
||||
|
||||
auto& uboDescriptor = m_owner.GetUniformBufferDescriptor(m_poolIndex, m_bindingIndex, resourceIndex);
|
||||
uboDescriptor.bindingIndex = static_cast<UInt32>(binding.bindingIndex);
|
||||
uboDescriptor.offset = uboBinding.offset;
|
||||
uboDescriptor.size = uboBinding.range;
|
||||
|
||||
if (OpenGLBuffer* glBuffer = static_cast<OpenGLBuffer*>(uboBinding.buffer))
|
||||
if (OpenGLBuffer* glBuffer = static_cast<OpenGLBuffer*>(arg.buffer))
|
||||
{
|
||||
if (glBuffer->GetType() != BufferType::Uniform)
|
||||
throw std::runtime_error("expected uniform buffer");
|
||||
@@ -112,10 +101,11 @@ namespace Nz
|
||||
}
|
||||
else
|
||||
uboDescriptor.buffer = 0;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
|
||||
}, binding.content);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,8 @@
|
||||
#include <Nazara/OpenGLRenderer/OpenGLShaderModule.hpp>
|
||||
#include <Nazara/Core/MemoryView.hpp>
|
||||
#include <Nazara/OpenGLRenderer/Utils.hpp>
|
||||
#include <Nazara/Shader/GlslWriter.hpp>
|
||||
#include <Nazara/Shader/ShaderLangLexer.hpp>
|
||||
#include <Nazara/Shader/ShaderLangParser.hpp>
|
||||
#include <Nazara/Shader/Ast/AstCloner.hpp>
|
||||
#include <Nazara/Shader/Ast/AstSerializer.hpp>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/OpenGLRenderer/Debug.hpp>
|
||||
@@ -16,12 +14,14 @@
|
||||
namespace Nz
|
||||
{
|
||||
OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states) :
|
||||
m_device(device)
|
||||
{
|
||||
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, const ShaderWriter::States& states)
|
||||
OpenGLShaderModule::OpenGLShaderModule(OpenGLDevice& device, ShaderStageTypeFlags shaderStages, ShaderLanguage lang, const void* source, std::size_t sourceSize, const ShaderWriter::States& states) :
|
||||
m_device(device)
|
||||
{
|
||||
NazaraAssert(shaderStages != 0, "at least one shader stage must be specified");
|
||||
|
||||
@@ -36,16 +36,8 @@ namespace Nz
|
||||
{
|
||||
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);
|
||||
|
||||
auto& entry = m_shaders.emplace_back();
|
||||
entry.shader = std::move(shader);
|
||||
entry.shader = GlslShader{ std::string(reinterpret_cast<const char*>(source), std::size_t(sourceSize)) };
|
||||
entry.stage = shaderStage;
|
||||
break;
|
||||
}
|
||||
@@ -90,16 +82,9 @@ namespace Nz
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLShaderModule::CheckCompilationStatus(GL::Shader& shader)
|
||||
ShaderStageTypeFlags OpenGLShaderModule::Attach(GL::Program& program, const GlslWriter::BindingMapping& bindingMapping) const
|
||||
{
|
||||
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& context = m_device.GetReferenceContext();
|
||||
const auto& contextParams = context.GetParams();
|
||||
|
||||
GlslWriter::Environment env;
|
||||
@@ -115,27 +100,64 @@ namespace Nz
|
||||
GlslWriter writer;
|
||||
writer.SetEnv(env);
|
||||
|
||||
ShaderStageTypeFlags stageFlags;
|
||||
for (const auto& shaderEntry : m_shaders)
|
||||
{
|
||||
GL::Shader shader;
|
||||
|
||||
if (!shader.Create(m_device, ToOpenGL(shaderEntry.stage)))
|
||||
throw std::runtime_error("failed to create shader"); //< TODO: Handle error message
|
||||
|
||||
std::visit([&](auto&& arg)
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
if constexpr (std::is_same_v<T, GlslShader>)
|
||||
shader.SetSource(arg.sourceCode.data(), GLint(arg.sourceCode.size()));
|
||||
else if constexpr (std::is_same_v<T, ShaderStatement>)
|
||||
{
|
||||
std::string code = writer.Generate(shaderEntry.stage, *arg.ast, bindingMapping, m_states);
|
||||
shader.SetSource(code.data(), GLint(code.size()));
|
||||
}
|
||||
else
|
||||
static_assert(AlwaysFalse<T>::value, "non-exhaustive visitor");
|
||||
|
||||
}, shaderEntry.shader);
|
||||
|
||||
shader.Compile();
|
||||
|
||||
CheckCompilationStatus(shader);
|
||||
|
||||
program.AttachShader(shader.GetObjectId());
|
||||
// Shader can be deleted now (it won't be deleted by the driver until program gets deleted)
|
||||
|
||||
stageFlags |= shaderEntry.stage;
|
||||
}
|
||||
|
||||
return stageFlags;
|
||||
}
|
||||
|
||||
void OpenGLShaderModule::Create(OpenGLDevice& /*device*/, ShaderStageTypeFlags shaderStages, ShaderAst::Statement& shaderAst, const ShaderWriter::States& states)
|
||||
{
|
||||
m_states = states;
|
||||
m_states.sanitized = true; //< Shader is always sanitized (because of keywords)
|
||||
std::shared_ptr<ShaderAst::Statement> sanitized = GlslWriter::Sanitize(shaderAst);
|
||||
|
||||
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(), GLint(code.size()));
|
||||
shader.Compile();
|
||||
|
||||
CheckCompilationStatus(shader);
|
||||
|
||||
auto& entry = m_shaders.emplace_back();
|
||||
entry.shader = std::move(shader);
|
||||
entry.shader = ShaderStatement{ sanitized };
|
||||
entry.stage = shaderStage;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLShaderModule::CheckCompilationStatus(GL::Shader& shader)
|
||||
{
|
||||
std::string errorLog;
|
||||
if (!shader.GetCompilationStatus(&errorLog))
|
||||
throw std::runtime_error("Failed to compile shader: " + errorLog);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user