Commit 5ca050d1 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Set fragment output locations in SPIR-V

The shader translator outputs arbitrary location indices for fragment outputs. Once compiled by glslang, the SPIR-V transformer modifies these decorations. Bug: angleproject:3394 Change-Id: Ib9d8336bccc392e789e4d93031fdcce9c466b7a6 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2011214 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent bef8ca7d
......@@ -38,18 +38,20 @@ TOutputVulkanGLSL::TOutputVulkanGLSL(TInfoSinkBase &objSink,
output,
compileOptions),
mNextUnusedBinding(0),
mNextUnusedInputLocation(0)
mNextUnusedInputLocation(0),
mNextUnusedOutputLocation(0)
{}
void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
{
const TType &type = variable->getType();
bool needsCustomLayout =
type.getQualifier() == EvqFragmentOut || IsVarying(type.getQualifier());
bool needsCustomLayout = IsVarying(type.getQualifier());
bool needsSetBinding =
IsSampler(type.getBasicType()) || type.isInterfaceBlock() || IsImage(type.getBasicType());
bool needsLocation = type.getQualifier() == EvqAttribute || type.getQualifier() == EvqVertexIn;
bool needsLocation = type.getQualifier() == EvqAttribute ||
type.getQualifier() == EvqVertexIn ||
type.getQualifier() == EvqFragmentOut;
if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout && !needsSetBinding &&
!needsLocation)
......@@ -121,8 +123,11 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
{
const unsigned int locationCount =
CalculateVaryingLocationCount(symbol, getShaderType());
uint32_t location = IsShaderIn(type.getQualifier())
? nextUnusedInputLocation(locationCount)
: nextUnusedOutputLocation(locationCount);
out << "location=" << nextUnusedInputLocation(locationCount);
out << "location=" << location;
separator = ", ";
}
}
......
......@@ -36,6 +36,12 @@ class TOutputVulkanGLSL : public TOutputGLSL
mNextUnusedInputLocation += consumedCount;
return nextUnused;
}
uint32_t nextUnusedOutputLocation(uint32_t consumedCount)
{
uint32_t nextUnused = mNextUnusedOutputLocation;
mNextUnusedOutputLocation += consumedCount;
return nextUnused;
}
protected:
void writeLayoutQualifier(TIntermTyped *variable) override;
......@@ -51,6 +57,7 @@ class TOutputVulkanGLSL : public TOutputGLSL
// Glslang wrapper modifies set, binding and location decorations in SPIR-V directly.
uint32_t mNextUnusedBinding;
uint32_t mNextUnusedInputLocation;
uint32_t mNextUnusedOutputLocation;
};
} // namespace sh
......@@ -520,14 +520,21 @@ ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *inf
// Add location information for an in/out variable.
ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName,
gl::ShaderType shaderType,
uint32_t location,
uint32_t component,
gl::ShaderBitSet activeStages)
uint32_t component)
{
ShaderInterfaceVariableInfo *info = AddShaderInterfaceVariable(infoMap, varName);
info->location = location;
info->component = component;
info->activeStages = activeStages;
// The info map for this name may or may not exist already. This function merges the
// location/component information.
ShaderInterfaceVariableInfo *info = &(*infoMap)[varName];
ASSERT(info->descriptorSet == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->binding == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->location[shaderType] == ShaderInterfaceVariableInfo::kInvalid);
ASSERT(info->component[shaderType] == ShaderInterfaceVariableInfo::kInvalid);
info->location[shaderType] = location;
info->component[shaderType] = component;
return info;
}
......@@ -779,24 +786,20 @@ void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programSt
void AssignAttributeLocations(const gl::ProgramState &programState,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
gl::ShaderBitSet vertexOnly;
vertexOnly.set(gl::ShaderType::Vertex);
// Assign attribute locations for the vertex shader.
for (const sh::ShaderVariable &attribute : programState.getProgramInputs())
{
ASSERT(attribute.active);
AddLocationInfo(variableInfoMapOut, attribute.mappedName, attribute.location,
ShaderInterfaceVariableInfo::kInvalid, vertexOnly);
AddLocationInfo(variableInfoMapOut, attribute.mappedName, gl::ShaderType::Vertex,
attribute.location, ShaderInterfaceVariableInfo::kInvalid);
}
}
void AssignOutputLocations(const gl::ProgramState &programState,
IntermediateShaderSource *fragmentSource)
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
// Parse output locations and replace them in the fragment shader.
// See corresponding code in OutputVulkanGLSL.cpp.
// Assign output locations for the fragment shader.
// TODO(syoussefi): Add support for EXT_blend_func_extended. http://anglebug.com/3385
const auto &outputLocations = programState.getOutputLocations();
const auto &outputVariables = programState.getOutputVariables();
......@@ -808,23 +811,22 @@ void AssignOutputLocations(const gl::ProgramState &programState,
{
const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index];
std::string name = outputVar.name;
std::string locationString;
uint32_t location = 0;
if (outputVar.location != -1)
{
locationString = "location = " + Str(outputVar.location);
location = outputVar.location;
}
else if (std::find(implicitOutputs.begin(), implicitOutputs.end(), name) ==
else if (std::find(implicitOutputs.begin(), implicitOutputs.end(), outputVar.name) ==
implicitOutputs.end())
{
// If there is only one output, it is allowed not to have a location qualifier, in
// which case it defaults to 0. GLSL ES 3.00 spec, section 4.3.8.2.
ASSERT(CountExplicitOutputs(outputVariables.begin(), outputVariables.end(),
implicitOutputs.begin(), implicitOutputs.end()) == 1);
locationString = "location = 0";
}
fragmentSource->insertLayoutSpecifier(name, locationString);
AddLocationInfo(variableInfoMapOut, outputVar.mappedName, gl::ShaderType::Fragment,
location, ShaderInterfaceVariableInfo::kInvalid);
}
}
}
......@@ -1493,16 +1495,10 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
switch (decoration)
{
case spv::DecorationLocation:
if (info->activeStages[mShaderType])
{
newDecorationValue = info->location;
}
newDecorationValue = info->location[mShaderType];
break;
case spv::DecorationComponent:
if (info->activeStages[mShaderType])
{
newDecorationValue = info->component;
}
newDecorationValue = info->component[mShaderType];
break;
case spv::DecorationBinding:
newDecorationValue = info->binding;
......@@ -1548,6 +1544,14 @@ size_t SpirvTransformer::getCurrentOutputOffset() const
}
} // anonymous namespace
const uint32_t ShaderInterfaceVariableInfo::kInvalid;
ShaderInterfaceVariableInfo::ShaderInterfaceVariableInfo()
{
location.fill(kInvalid);
component.fill(kInvalid);
}
void GlslangInitialize()
{
int result = ShInitialize();
......@@ -1646,7 +1650,7 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
// Assign outputs to the fragment shader, if any.
if (!fragmentSource->empty())
{
AssignOutputLocations(programState, fragmentSource);
AssignOutputLocations(programState, variableInfoMapOut);
}
// Assign attributes to the vertex shader, if any.
......
......@@ -49,16 +49,20 @@ using GlslangErrorCallback = std::function<angle::Result(GlslangError)>;
// attributes require a location.
struct ShaderInterfaceVariableInfo
{
ShaderInterfaceVariableInfo();
static constexpr uint32_t kInvalid = std::numeric_limits<uint32_t>::max();
// Used for interface blocks and opaque uniforms.
uint32_t descriptorSet = kInvalid;
uint32_t binding = kInvalid;
// Used for vertex attributes, fragment shader outputs and varyings.
uint32_t location = kInvalid;
uint32_t component = kInvalid;
// Used for varyings.
gl::ShaderBitSet activeStages;
// Used for vertex attributes, fragment shader outputs and varyings. There could be different
// variables that share the same name, such as a vertex attribute and a fragment output. They
// will share this object since they have the same name, but will find possibly different
// locations in their respective slots. This is also used to indicate in which stages a varying
// is active, as the rest would contain kInvalid.
gl::ShaderMap<uint32_t> location;
gl::ShaderMap<uint32_t> component;
// Used for transform feedback extension to decorate vertex shader output.
uint32_t xfbBuffer = kInvalid;
uint32_t xfbOffset = kInvalid;
......
......@@ -440,31 +440,33 @@ angle::Result ProgramVk::loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStre
// Read the shader source
mShaderSources[shaderType] = stream->readString();
// Read the expected bindings
size_t infoCount = stream->readInt<size_t>();
for (size_t i = 0; i < infoCount; ++i)
{
std::string varName = stream->readString();
ShaderInterfaceVariableInfo info;
info.descriptorSet = stream->readInt<uint32_t>();
info.binding = stream->readInt<uint32_t>();
info.location = stream->readInt<uint32_t>();
info.component = stream->readInt<uint32_t>();
info.activeStages = gl::ShaderBitSet(static_cast<uint8_t>(stream->readInt<uint32_t>()));
info.xfbBuffer = stream->readInt<uint32_t>();
info.xfbOffset = stream->readInt<uint32_t>();
info.xfbStride = stream->readInt<uint32_t>();
mVariableInfoMap[varName] = info;
}
SpirvBlob *spirvBlob = &mShaderInfo.getSpirvBlobs()[shaderType];
// Read the SPIR-V
stream->readIntVector<uint32_t>(spirvBlob);
}
// Read the expected bindings
size_t infoCount = stream->readInt<size_t>();
for (size_t i = 0; i < infoCount; ++i)
{
std::string varName = stream->readString();
ShaderInterfaceVariableInfo info;
info.descriptorSet = stream->readInt<uint32_t>();
info.binding = stream->readInt<uint32_t>();
info.xfbBuffer = stream->readInt<uint32_t>();
info.xfbOffset = stream->readInt<uint32_t>();
info.xfbStride = stream->readInt<uint32_t>();
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
info.location[shaderType] = stream->readInt<uint32_t>();
info.component[shaderType] = stream->readInt<uint32_t>();
}
mVariableInfoMap[varName] = info;
}
return angle::Result::Continue;
}
......@@ -476,26 +478,28 @@ void ProgramVk::saveSpirvBlob(gl::BinaryOutputStream *stream)
// Write the shader source
stream->writeString(mShaderSources[shaderType]);
// Write the expected bindings
stream->writeInt(mVariableInfoMap.size());
for (const auto &nameInfo : mVariableInfoMap)
{
stream->writeString(nameInfo.first);
stream->writeIntOrNegOne(nameInfo.second.descriptorSet);
stream->writeIntOrNegOne(nameInfo.second.binding);
stream->writeIntOrNegOne(nameInfo.second.location);
stream->writeIntOrNegOne(nameInfo.second.component);
stream->writeIntOrNegOne(nameInfo.second.activeStages.bits());
stream->writeIntOrNegOne(nameInfo.second.xfbBuffer);
stream->writeIntOrNegOne(nameInfo.second.xfbOffset);
stream->writeIntOrNegOne(nameInfo.second.xfbStride);
}
const SpirvBlob &spirvBlob = mShaderInfo.getSpirvBlobs()[shaderType];
// Write the SPIR-V
stream->writeIntVector(spirvBlob);
}
// Write the expected bindings
stream->writeInt(mVariableInfoMap.size());
for (const auto &nameInfo : mVariableInfoMap)
{
stream->writeString(nameInfo.first);
stream->writeIntOrNegOne(nameInfo.second.descriptorSet);
stream->writeIntOrNegOne(nameInfo.second.binding);
stream->writeIntOrNegOne(nameInfo.second.xfbBuffer);
stream->writeIntOrNegOne(nameInfo.second.xfbOffset);
stream->writeIntOrNegOne(nameInfo.second.xfbStride);
for (gl::ShaderType shaderType : gl::AllShaderTypes())
{
stream->writeIntOrNegOne(nameInfo.second.location[shaderType]);
stream->writeIntOrNegOne(nameInfo.second.component[shaderType]);
}
}
}
// ProgramVk implementation.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment