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, ...@@ -38,18 +38,20 @@ TOutputVulkanGLSL::TOutputVulkanGLSL(TInfoSinkBase &objSink,
output, output,
compileOptions), compileOptions),
mNextUnusedBinding(0), mNextUnusedBinding(0),
mNextUnusedInputLocation(0) mNextUnusedInputLocation(0),
mNextUnusedOutputLocation(0)
{} {}
void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable) void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
{ {
const TType &type = variable->getType(); const TType &type = variable->getType();
bool needsCustomLayout = bool needsCustomLayout = IsVarying(type.getQualifier());
type.getQualifier() == EvqFragmentOut || IsVarying(type.getQualifier());
bool needsSetBinding = bool needsSetBinding =
IsSampler(type.getBasicType()) || type.isInterfaceBlock() || IsImage(type.getBasicType()); 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 && if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout && !needsSetBinding &&
!needsLocation) !needsLocation)
...@@ -121,8 +123,11 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable) ...@@ -121,8 +123,11 @@ void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable)
{ {
const unsigned int locationCount = const unsigned int locationCount =
CalculateVaryingLocationCount(symbol, getShaderType()); CalculateVaryingLocationCount(symbol, getShaderType());
uint32_t location = IsShaderIn(type.getQualifier())
? nextUnusedInputLocation(locationCount)
: nextUnusedOutputLocation(locationCount);
out << "location=" << nextUnusedInputLocation(locationCount); out << "location=" << location;
separator = ", "; separator = ", ";
} }
} }
......
...@@ -36,6 +36,12 @@ class TOutputVulkanGLSL : public TOutputGLSL ...@@ -36,6 +36,12 @@ class TOutputVulkanGLSL : public TOutputGLSL
mNextUnusedInputLocation += consumedCount; mNextUnusedInputLocation += consumedCount;
return nextUnused; return nextUnused;
} }
uint32_t nextUnusedOutputLocation(uint32_t consumedCount)
{
uint32_t nextUnused = mNextUnusedOutputLocation;
mNextUnusedOutputLocation += consumedCount;
return nextUnused;
}
protected: protected:
void writeLayoutQualifier(TIntermTyped *variable) override; void writeLayoutQualifier(TIntermTyped *variable) override;
...@@ -51,6 +57,7 @@ class TOutputVulkanGLSL : public TOutputGLSL ...@@ -51,6 +57,7 @@ class TOutputVulkanGLSL : public TOutputGLSL
// Glslang wrapper modifies set, binding and location decorations in SPIR-V directly. // Glslang wrapper modifies set, binding and location decorations in SPIR-V directly.
uint32_t mNextUnusedBinding; uint32_t mNextUnusedBinding;
uint32_t mNextUnusedInputLocation; uint32_t mNextUnusedInputLocation;
uint32_t mNextUnusedOutputLocation;
}; };
} // namespace sh } // namespace sh
...@@ -520,14 +520,21 @@ ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *inf ...@@ -520,14 +520,21 @@ ShaderInterfaceVariableInfo *AddResourceInfo(ShaderInterfaceVariableInfoMap *inf
// Add location information for an in/out variable. // Add location information for an in/out variable.
ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap, ShaderInterfaceVariableInfo *AddLocationInfo(ShaderInterfaceVariableInfoMap *infoMap,
const std::string &varName, const std::string &varName,
gl::ShaderType shaderType,
uint32_t location, uint32_t location,
uint32_t component, uint32_t component)
gl::ShaderBitSet activeStages)
{ {
ShaderInterfaceVariableInfo *info = AddShaderInterfaceVariable(infoMap, varName); // The info map for this name may or may not exist already. This function merges the
info->location = location; // location/component information.
info->component = component; ShaderInterfaceVariableInfo *info = &(*infoMap)[varName];
info->activeStages = activeStages;
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; return info;
} }
...@@ -779,24 +786,20 @@ void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programSt ...@@ -779,24 +786,20 @@ void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programSt
void AssignAttributeLocations(const gl::ProgramState &programState, void AssignAttributeLocations(const gl::ProgramState &programState,
ShaderInterfaceVariableInfoMap *variableInfoMapOut) ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{ {
gl::ShaderBitSet vertexOnly;
vertexOnly.set(gl::ShaderType::Vertex);
// Assign attribute locations for the vertex shader. // Assign attribute locations for the vertex shader.
for (const sh::ShaderVariable &attribute : programState.getProgramInputs()) for (const sh::ShaderVariable &attribute : programState.getProgramInputs())
{ {
ASSERT(attribute.active); ASSERT(attribute.active);
AddLocationInfo(variableInfoMapOut, attribute.mappedName, attribute.location, AddLocationInfo(variableInfoMapOut, attribute.mappedName, gl::ShaderType::Vertex,
ShaderInterfaceVariableInfo::kInvalid, vertexOnly); attribute.location, ShaderInterfaceVariableInfo::kInvalid);
} }
} }
void AssignOutputLocations(const gl::ProgramState &programState, void AssignOutputLocations(const gl::ProgramState &programState,
IntermediateShaderSource *fragmentSource) ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{ {
// Parse output locations and replace them in the fragment shader. // Assign output locations for the fragment shader.
// See corresponding code in OutputVulkanGLSL.cpp.
// TODO(syoussefi): Add support for EXT_blend_func_extended. http://anglebug.com/3385 // TODO(syoussefi): Add support for EXT_blend_func_extended. http://anglebug.com/3385
const auto &outputLocations = programState.getOutputLocations(); const auto &outputLocations = programState.getOutputLocations();
const auto &outputVariables = programState.getOutputVariables(); const auto &outputVariables = programState.getOutputVariables();
...@@ -808,23 +811,22 @@ void AssignOutputLocations(const gl::ProgramState &programState, ...@@ -808,23 +811,22 @@ void AssignOutputLocations(const gl::ProgramState &programState,
{ {
const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index]; const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index];
std::string name = outputVar.name; uint32_t location = 0;
std::string locationString;
if (outputVar.location != -1) 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()) implicitOutputs.end())
{ {
// If there is only one output, it is allowed not to have a location qualifier, in // 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. // which case it defaults to 0. GLSL ES 3.00 spec, section 4.3.8.2.
ASSERT(CountExplicitOutputs(outputVariables.begin(), outputVariables.end(), ASSERT(CountExplicitOutputs(outputVariables.begin(), outputVariables.end(),
implicitOutputs.begin(), implicitOutputs.end()) == 1); 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 ...@@ -1493,16 +1495,10 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction, size_t wor
switch (decoration) switch (decoration)
{ {
case spv::DecorationLocation: case spv::DecorationLocation:
if (info->activeStages[mShaderType]) newDecorationValue = info->location[mShaderType];
{
newDecorationValue = info->location;
}
break; break;
case spv::DecorationComponent: case spv::DecorationComponent:
if (info->activeStages[mShaderType]) newDecorationValue = info->component[mShaderType];
{
newDecorationValue = info->component;
}
break; break;
case spv::DecorationBinding: case spv::DecorationBinding:
newDecorationValue = info->binding; newDecorationValue = info->binding;
...@@ -1548,6 +1544,14 @@ size_t SpirvTransformer::getCurrentOutputOffset() const ...@@ -1548,6 +1544,14 @@ size_t SpirvTransformer::getCurrentOutputOffset() const
} }
} // anonymous namespace } // anonymous namespace
const uint32_t ShaderInterfaceVariableInfo::kInvalid;
ShaderInterfaceVariableInfo::ShaderInterfaceVariableInfo()
{
location.fill(kInvalid);
component.fill(kInvalid);
}
void GlslangInitialize() void GlslangInitialize()
{ {
int result = ShInitialize(); int result = ShInitialize();
...@@ -1646,7 +1650,7 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options, ...@@ -1646,7 +1650,7 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
// Assign outputs to the fragment shader, if any. // Assign outputs to the fragment shader, if any.
if (!fragmentSource->empty()) if (!fragmentSource->empty())
{ {
AssignOutputLocations(programState, fragmentSource); AssignOutputLocations(programState, variableInfoMapOut);
} }
// Assign attributes to the vertex shader, if any. // Assign attributes to the vertex shader, if any.
......
...@@ -49,16 +49,20 @@ using GlslangErrorCallback = std::function<angle::Result(GlslangError)>; ...@@ -49,16 +49,20 @@ using GlslangErrorCallback = std::function<angle::Result(GlslangError)>;
// attributes require a location. // attributes require a location.
struct ShaderInterfaceVariableInfo struct ShaderInterfaceVariableInfo
{ {
ShaderInterfaceVariableInfo();
static constexpr uint32_t kInvalid = std::numeric_limits<uint32_t>::max(); static constexpr uint32_t kInvalid = std::numeric_limits<uint32_t>::max();
// Used for interface blocks and opaque uniforms. // Used for interface blocks and opaque uniforms.
uint32_t descriptorSet = kInvalid; uint32_t descriptorSet = kInvalid;
uint32_t binding = kInvalid; uint32_t binding = kInvalid;
// Used for vertex attributes, fragment shader outputs and varyings. // Used for vertex attributes, fragment shader outputs and varyings. There could be different
uint32_t location = kInvalid; // variables that share the same name, such as a vertex attribute and a fragment output. They
uint32_t component = kInvalid; // will share this object since they have the same name, but will find possibly different
// Used for varyings. // locations in their respective slots. This is also used to indicate in which stages a varying
gl::ShaderBitSet activeStages; // 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. // Used for transform feedback extension to decorate vertex shader output.
uint32_t xfbBuffer = kInvalid; uint32_t xfbBuffer = kInvalid;
uint32_t xfbOffset = kInvalid; uint32_t xfbOffset = kInvalid;
......
...@@ -440,31 +440,33 @@ angle::Result ProgramVk::loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStre ...@@ -440,31 +440,33 @@ angle::Result ProgramVk::loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStre
// Read the shader source // Read the shader source
mShaderSources[shaderType] = stream->readString(); 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]; SpirvBlob *spirvBlob = &mShaderInfo.getSpirvBlobs()[shaderType];
// Read the SPIR-V // Read the SPIR-V
stream->readIntVector<uint32_t>(spirvBlob); 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; return angle::Result::Continue;
} }
...@@ -476,26 +478,28 @@ void ProgramVk::saveSpirvBlob(gl::BinaryOutputStream *stream) ...@@ -476,26 +478,28 @@ void ProgramVk::saveSpirvBlob(gl::BinaryOutputStream *stream)
// Write the shader source // Write the shader source
stream->writeString(mShaderSources[shaderType]); 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]; const SpirvBlob &spirvBlob = mShaderInfo.getSpirvBlobs()[shaderType];
// Write the SPIR-V // Write the SPIR-V
stream->writeIntVector(spirvBlob); 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. // 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