Files
UnrealEngine/Engine/Source/ThirdParty/MaterialX/MaterialX-1.38.10/source/MaterialXGenGlsl/GlslResourceBindingContext.cpp
2025-05-18 13:04:45 +08:00

191 lines
7.3 KiB
C++

//
// Copyright Contributors to the MaterialX Project
// SPDX-License-Identifier: Apache-2.0
//
#include <MaterialXGenGlsl/GlslResourceBindingContext.h>
MATERIALX_NAMESPACE_BEGIN
//
// GlslResourceBindingContext
//
GlslResourceBindingContext::GlslResourceBindingContext(
size_t uniformBindingLocation, size_t samplerBindingLocation) :
_hwInitUniformBindLocation(uniformBindingLocation),
_hwInitSamplerBindLocation(samplerBindingLocation)
{
_requiredExtensions.insert("GL_ARB_shading_language_420pack");
}
void GlslResourceBindingContext::initialize()
{
// Reset bind location counter.
_hwUniformBindLocation = _hwInitUniformBindLocation;
// Reset sampler bind location counter.
_hwSamplerBindLocation = _hwInitSamplerBindLocation;
}
void GlslResourceBindingContext::emitDirectives(GenContext& context, ShaderStage& stage)
{
const ShaderGenerator& generator = context.getShaderGenerator();
// Write shader stage directives for Vulkan compliance if required
if (_separateBindingLocation)
{
std::string shaderStage;
if (stage.getName() == Stage::VERTEX)
{
shaderStage = "vertex";
}
else if (stage.getName() == Stage::PIXEL)
{
shaderStage = "fragment";
}
if (!shaderStage.empty())
{
generator.emitLine("#pragma shader_stage(" + shaderStage + ")", stage, false);
}
}
for (auto& extension : _requiredExtensions)
{
generator.emitLine("#extension " + extension + " : enable", stage, false);
}
}
void GlslResourceBindingContext::emitResourceBindings(GenContext& context, const VariableBlock& uniforms, ShaderStage& stage)
{
const ShaderGenerator& generator = context.getShaderGenerator();
const Syntax& syntax = generator.getSyntax();
// First, emit all value uniforms in a block with single layout binding
bool hasValueUniforms = false;
for (auto uniform : uniforms.getVariableOrder())
{
if (uniform->getType() != Type::FILENAME)
{
hasValueUniforms = true;
break;
}
}
if (hasValueUniforms)
{
generator.emitLine("layout (std140, binding=" + std::to_string(_hwUniformBindLocation++) + ") " +
syntax.getUniformQualifier() + " " + uniforms.getName() + "_" + stage.getName(),
stage, false);
generator.emitScopeBegin(stage);
for (auto uniform : uniforms.getVariableOrder())
{
if (uniform->getType() != Type::FILENAME)
{
generator.emitLineBegin(stage);
generator.emitVariableDeclaration(uniform, EMPTY_STRING, context, stage, false);
generator.emitString(Syntax::SEMICOLON, stage);
generator.emitLineEnd(stage, false);
}
}
generator.emitScopeEnd(stage, true);
}
// Second, emit all sampler uniforms as separate uniforms with separate layout bindings
for (auto uniform : uniforms.getVariableOrder())
{
if (*uniform->getType() == *Type::FILENAME)
{
generator.emitString("layout (binding=" + std::to_string(_separateBindingLocation ? _hwUniformBindLocation++ : _hwSamplerBindLocation++) + ") " + syntax.getUniformQualifier() + " ", stage);
generator.emitVariableDeclaration(uniform, EMPTY_STRING, context, stage, false);
generator.emitLineEnd(stage, true);
}
}
generator.emitLineBreak(stage);
}
void GlslResourceBindingContext::emitStructuredResourceBindings(GenContext& context, const VariableBlock& uniforms,
ShaderStage& stage, const std::string& structInstanceName,
const std::string& arraySuffix)
{
const ShaderGenerator& generator = context.getShaderGenerator();
const Syntax& syntax = generator.getSyntax();
// Glsl structures need to be aligned. We make a best effort to base align struct members and add
// padding if required.
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_uniform_buffer_object.txt
const size_t baseAlignment = 16;
std::unordered_map<const TypeDesc*, size_t> alignmentMap({ { Type::FLOAT, baseAlignment / 4 },
{ Type::INTEGER, baseAlignment / 4 },
{ Type::BOOLEAN, baseAlignment / 4 },
{ Type::COLOR3, baseAlignment },
{ Type::COLOR4, baseAlignment },
{ Type::VECTOR2, baseAlignment },
{ Type::VECTOR3, baseAlignment },
{ Type::VECTOR4, baseAlignment },
{ Type::MATRIX33, baseAlignment * 4 },
{ Type::MATRIX44, baseAlignment * 4 } });
// Get struct alignment and size
// alignment, uniform member index
vector<std::pair<size_t, size_t>> memberOrder;
size_t structSize = 0;
for (size_t i = 0; i < uniforms.size(); ++i)
{
auto it = alignmentMap.find(uniforms[i]->getType());
if (it == alignmentMap.end())
{
structSize += baseAlignment;
memberOrder.push_back(std::make_pair(baseAlignment, i));
}
else
{
structSize += it->second;
memberOrder.push_back(std::make_pair(it->second, i));
}
}
// Align up and determine number of padding floats to add
const size_t numPaddingfloats =
(((structSize + (baseAlignment - 1)) & ~(baseAlignment - 1)) - structSize) / 4;
// Sort order from largest to smallest
std::sort(memberOrder.begin(), memberOrder.end(),
[](const std::pair<size_t, size_t>& a, const std::pair<size_t, size_t>& b)
{
return a.first > b.first;
});
// Emit the struct
generator.emitLine("struct " + uniforms.getName(), stage, false);
generator.emitScopeBegin(stage);
for (size_t i = 0; i < uniforms.size(); ++i)
{
size_t variableIndex = memberOrder[i].second;
generator.emitLineBegin(stage);
generator.emitVariableDeclaration(
uniforms[variableIndex], EMPTY_STRING, context, stage, false);
generator.emitString(Syntax::SEMICOLON, stage);
generator.emitLineEnd(stage, false);
}
// Emit padding
for (size_t i = 0; i < numPaddingfloats; ++i)
{
generator.emitLine("float pad" + std::to_string(i), stage, true);
}
generator.emitScopeEnd(stage, true);
// Emit binding information
generator.emitLineBreak(stage);
generator.emitLine("layout (std140, binding=" + std::to_string(_hwUniformBindLocation++) +
") " + syntax.getUniformQualifier() + " " + uniforms.getName() + "_" +
stage.getName(), stage, false);
generator.emitScopeBegin(stage);
generator.emitLine(uniforms.getName() + " " + structInstanceName + arraySuffix, stage);
generator.emitScopeEnd(stage, true);
}
MATERIALX_NAMESPACE_END