Commit f6df8692 by Jamie Madill Committed by Commit Bot

Vulkan: Support XFB in non-Vertex stages.

This updates the code in several places to support Geometry and Tessellation Evaluation shaders to output transform feedback. Does not turn on any new tests but enables support for XFB when we turn on GS/TS. Bug: angleproject:3571 Bug: angleproject:3572 Change-Id: I6dcb768f2df4eeee81a4a500e999fcf16716f58f Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2581941Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
parent 8707811d
......@@ -1150,6 +1150,19 @@ ShaderType GetShaderTypeFromBitfield(size_t singleShaderType)
return ShaderType::InvalidEnum;
}
}
bool ShaderTypeSupportsTransformFeedback(ShaderType shaderType)
{
switch (shaderType)
{
case ShaderType::Vertex:
case ShaderType::Geometry:
case ShaderType::TessEvaluation:
return true;
default:
return false;
}
}
} // namespace gl
namespace egl
......
......@@ -246,6 +246,7 @@ enum class SrgbOverride
};
ShaderType GetShaderTypeFromBitfield(size_t singleShaderType);
bool ShaderTypeSupportsTransformFeedback(ShaderType shaderType);
} // namespace gl
......
......@@ -341,9 +341,9 @@ ANGLE_NO_DISCARD bool AppendPreRotation(TCompiler *compiler,
return RunAtTheEndOfShader(compiler, root, assignment, symbolTable);
}
ANGLE_NO_DISCARD bool AppendVertexShaderTransformFeedbackOutputToMain(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable)
ANGLE_NO_DISCARD bool AppendTransformFeedbackOutputToMain(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable)
{
TVariable *xfbPlaceholder = new TVariable(symbolTable, ImmutableString("@@ XFB-OUT @@"),
new TType(), SymbolType::AngleInternal);
......@@ -745,11 +745,12 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
return false;
}
gl::ShaderType packedShaderType = gl::FromGLenum<gl::ShaderType>(getShaderType());
if (defaultUniformCount > 0)
{
gl::ShaderType shaderType = gl::FromGLenum<gl::ShaderType>(getShaderType());
sink << "\nlayout(set=0, binding=" << outputGLSL->nextUnusedBinding()
<< ", std140) uniform " << kDefaultUniformNames[shaderType] << "\n{\n";
<< ", std140) uniform " << kDefaultUniformNames[packedShaderType] << "\n{\n";
DeclareDefaultUniformsTraverser defaultTraverser(&sink, getHashFunction(), &getNameMap());
root->traverse(&defaultTraverser);
......@@ -797,7 +798,7 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
}
}
if (getShaderType() != GL_COMPUTE_SHADER)
if (packedShaderType != gl::ShaderType::Compute)
{
if (!ReplaceGLDepthRangeWithDriverUniform(this, root, driverUniforms, &getSymbolTable()))
{
......@@ -814,229 +815,249 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
}
}
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used.
if (getShaderType() == GL_FRAGMENT_SHADER)
if (gl::ShaderTypeSupportsTransformFeedback(packedShaderType))
{
bool usesPointCoord = false;
bool usesFragCoord = false;
bool usesSampleMaskIn = false;
// Add a macro to declare transform feedback buffers.
sink << "@@ XFB-DECL @@\n\n";
// Search for the gl_PointCoord usage, if its used, we need to flip the y coordinate.
for (const ShaderVariable &inputVarying : mInputVaryings)
// Append a macro for transform feedback substitution prior to modifying depth.
if (!AppendTransformFeedbackOutputToMain(this, root, &getSymbolTable()))
{
if (!inputVarying.isBuiltIn())
{
continue;
}
return false;
}
}
if (inputVarying.name == "gl_SampleMaskIn")
{
usesSampleMaskIn = true;
continue;
}
switch (packedShaderType)
{
case gl::ShaderType::Fragment:
{
bool usesPointCoord = false;
bool usesFragCoord = false;
bool usesSampleMaskIn = false;
if (inputVarying.name == "gl_PointCoord")
// Search for the gl_PointCoord usage, if its used, we need to flip the y coordinate.
for (const ShaderVariable &inputVarying : mInputVaryings)
{
usesPointCoord = true;
break;
}
if (!inputVarying.isBuiltIn())
{
continue;
}
if (inputVarying.name == "gl_FragCoord")
{
usesFragCoord = true;
break;
if (inputVarying.name == "gl_SampleMaskIn")
{
usesSampleMaskIn = true;
continue;
}
if (inputVarying.name == "gl_PointCoord")
{
usesPointCoord = true;
break;
}
if (inputVarying.name == "gl_FragCoord")
{
usesFragCoord = true;
break;
}
}
}
if (compileOptions & SH_ADD_BRESENHAM_LINE_RASTER_EMULATION)
{
if (!AddBresenhamEmulationFS(this, compileOptions, sink, root, &getSymbolTable(),
&surfaceRotationSpecConst, driverUniforms, usesFragCoord))
if (compileOptions & SH_ADD_BRESENHAM_LINE_RASTER_EMULATION)
{
return false;
if (!AddBresenhamEmulationFS(this, compileOptions, sink, root, &getSymbolTable(),
&surfaceRotationSpecConst, driverUniforms,
usesFragCoord))
{
return false;
}
mSpecConstUsageBits.set(vk::SpecConstUsage::LineRasterEmulation);
}
mSpecConstUsageBits.set(vk::SpecConstUsage::LineRasterEmulation);
}
bool hasGLFragColor = false;
bool hasGLFragData = false;
bool usePreRotation = compileOptions & SH_ADD_PRE_ROTATION;
bool hasGLSampleMask = false;
bool hasGLFragColor = false;
bool hasGLFragData = false;
bool usePreRotation = compileOptions & SH_ADD_PRE_ROTATION;
bool hasGLSampleMask = false;
for (const ShaderVariable &outputVar : mOutputVariables)
{
if (outputVar.name == "gl_FragColor")
for (const ShaderVariable &outputVar : mOutputVariables)
{
ASSERT(!hasGLFragColor);
hasGLFragColor = true;
continue;
if (outputVar.name == "gl_FragColor")
{
ASSERT(!hasGLFragColor);
hasGLFragColor = true;
continue;
}
else if (outputVar.name == "gl_FragData")
{
ASSERT(!hasGLFragData);
hasGLFragData = true;
continue;
}
else if (outputVar.name == "gl_SampleMask")
{
ASSERT(!hasGLSampleMask);
hasGLSampleMask = true;
continue;
}
}
else if (outputVar.name == "gl_FragData")
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used.
ASSERT(!(hasGLFragColor && hasGLFragData));
if (hasGLFragColor)
{
ASSERT(!hasGLFragData);
hasGLFragData = true;
continue;
sink << "layout(location = 0) out vec4 webgl_FragColor;\n";
}
else if (outputVar.name == "gl_SampleMask")
if (hasGLFragData)
{
ASSERT(!hasGLSampleMask);
hasGLSampleMask = true;
continue;
sink << "layout(location = 0) out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
}
}
ASSERT(!(hasGLFragColor && hasGLFragData));
if (hasGLFragColor)
{
sink << "layout(location = 0) out vec4 webgl_FragColor;\n";
}
if (hasGLFragData)
{
sink << "layout(location = 0) out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
}
if (usesPointCoord)
{
TIntermTyped *flipNegXY = surfaceRotationSpecConst.getNegFlipXY();
if (!flipNegXY)
if (usesPointCoord)
{
flipNegXY = driverUniforms->getNegFlipXYRef();
TIntermTyped *flipNegXY = surfaceRotationSpecConst.getNegFlipXY();
if (!flipNegXY)
{
flipNegXY = driverUniforms->getNegFlipXYRef();
}
TIntermConstantUnion *pivot = CreateFloatNode(0.5f);
TIntermTyped *fragRotation = nullptr;
if (usePreRotation)
{
fragRotation = surfaceRotationSpecConst.getFragRotationMatrix();
if (!fragRotation)
{
fragRotation = driverUniforms->getFragRotationMatrixRef();
}
}
if (!RotateAndFlipBuiltinVariable(this, root, GetMainSequence(root), flipNegXY,
&getSymbolTable(),
BuiltInVariable::gl_PointCoord(),
kFlippedPointCoordName, pivot, fragRotation))
{
return false;
}
}
TIntermConstantUnion *pivot = CreateFloatNode(0.5f);
TIntermTyped *fragRotation = nullptr;
if (usePreRotation)
if (usesFragCoord)
{
fragRotation = surfaceRotationSpecConst.getFragRotationMatrix();
if (!fragRotation)
if (!InsertFragCoordCorrection(this, compileOptions, root, GetMainSequence(root),
&getSymbolTable(), &surfaceRotationSpecConst,
driverUniforms))
{
fragRotation = driverUniforms->getFragRotationMatrixRef();
return false;
}
}
if (!RotateAndFlipBuiltinVariable(this, root, GetMainSequence(root), flipNegXY,
&getSymbolTable(), BuiltInVariable::gl_PointCoord(),
kFlippedPointCoordName, pivot, fragRotation))
if (!RewriteDfdy(this, compileOptions, root, getSymbolTable(), getShaderVersion(),
&surfaceRotationSpecConst, driverUniforms))
{
return false;
}
}
if (usesFragCoord)
{
if (!InsertFragCoordCorrection(this, compileOptions, root, GetMainSequence(root),
&getSymbolTable(), &surfaceRotationSpecConst,
driverUniforms))
if (!RewriteInterpolateAtOffset(this, compileOptions, root, getSymbolTable(),
getShaderVersion(), &surfaceRotationSpecConst,
driverUniforms))
{
return false;
}
}
if (!RewriteDfdy(this, compileOptions, root, getSymbolTable(), getShaderVersion(),
&surfaceRotationSpecConst, driverUniforms))
{
return false;
}
if (usesSampleMaskIn && !RewriteSampleMaskIn(this, root, &getSymbolTable()))
{
return false;
}
if (!RewriteInterpolateAtOffset(this, compileOptions, root, getSymbolTable(),
getShaderVersion(), &surfaceRotationSpecConst,
driverUniforms))
{
return false;
}
if (hasGLSampleMask)
{
TIntermBinary *numSamples = driverUniforms->getNumSamplesRef();
if (!RewriteSampleMask(this, root, &getSymbolTable(), numSamples))
{
return false;
}
}
if (usesSampleMaskIn && !RewriteSampleMaskIn(this, root, &getSymbolTable()))
{
return false;
{
const TVariable *numSamplesVar =
static_cast<const TVariable *>(getSymbolTable().findBuiltIn(
ImmutableString("gl_NumSamples"), getShaderVersion()));
TIntermBinary *numSamples = driverUniforms->getNumSamplesRef();
if (!ReplaceVariableWithTyped(this, root, numSamplesVar, numSamples))
{
return false;
}
}
EmitEarlyFragmentTestsGLSL(*this, sink);
break;
}
if (hasGLSampleMask)
case gl::ShaderType::Vertex:
{
TIntermBinary *numSamples = driverUniforms->getNumSamplesRef();
if (!RewriteSampleMask(this, root, &getSymbolTable(), numSamples))
if (compileOptions & SH_ADD_BRESENHAM_LINE_RASTER_EMULATION)
{
return false;
if (!AddBresenhamEmulationVS(this, root, &getSymbolTable(), driverUniforms))
{
return false;
}
mSpecConstUsageBits.set(vk::SpecConstUsage::LineRasterEmulation);
}
}
{
const TVariable *numSamplesVar = static_cast<const TVariable *>(
getSymbolTable().findBuiltIn(ImmutableString("gl_NumSamples"), getShaderVersion()));
TIntermBinary *numSamples = driverUniforms->getNumSamplesRef();
if (!ReplaceVariableWithTyped(this, root, numSamplesVar, numSamples))
// Search for the gl_ClipDistance usage, if its used, we need to do some replacements.
bool useClipDistance = false;
for (const ShaderVariable &outputVarying : mOutputVaryings)
{
if (outputVarying.name == "gl_ClipDistance")
{
useClipDistance = true;
break;
}
}
if (useClipDistance &&
!ReplaceClipDistanceAssignments(this, root, &getSymbolTable(),
driverUniforms->getClipDistancesEnabled()))
{
return false;
}
}
EmitEarlyFragmentTestsGLSL(*this, sink);
}
else if (getShaderType() == GL_VERTEX_SHADER)
{
if (compileOptions & SH_ADD_BRESENHAM_LINE_RASTER_EMULATION)
{
if (!AddBresenhamEmulationVS(this, root, &getSymbolTable(), driverUniforms))
// Append depth range translation to main.
if (!transformDepthBeforeCorrection(root, driverUniforms))
{
return false;
}
mSpecConstUsageBits.set(vk::SpecConstUsage::LineRasterEmulation);
}
// Add a macro to declare transform feedback buffers.
sink << "@@ XFB-DECL @@\n\n";
// Append a macro for transform feedback substitution prior to modifying depth.
if (!AppendVertexShaderTransformFeedbackOutputToMain(this, root, &getSymbolTable()))
{
return false;
}
// Search for the gl_ClipDistance usage, if its used, we need to do some replacements.
bool useClipDistance = false;
for (const ShaderVariable &outputVarying : mOutputVaryings)
{
if (outputVarying.name == "gl_ClipDistance")
if (!AppendVertexShaderDepthCorrectionToMain(this, root, &getSymbolTable()))
{
useClipDistance = true;
break;
return false;
}
}
if (useClipDistance &&
!ReplaceClipDistanceAssignments(this, root, &getSymbolTable(),
driverUniforms->getClipDistancesEnabled()))
{
return false;
if ((compileOptions & SH_ADD_PRE_ROTATION) != 0 &&
!AppendPreRotation(this, root, &getSymbolTable(), &surfaceRotationSpecConst,
driverUniforms))
{
return false;
}
break;
}
// Append depth range translation to main.
if (!transformDepthBeforeCorrection(root, driverUniforms))
case gl::ShaderType::Geometry:
{
return false;
int maxVertices = getGeometryShaderMaxVertices();
// max_vertices=0 is not valid in Vulkan
maxVertices = std::max(1, maxVertices);
WriteGeometryShaderLayoutQualifiers(
sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
getGeometryShaderOutputPrimitiveType(), maxVertices);
break;
}
if (!AppendVertexShaderDepthCorrectionToMain(this, root, &getSymbolTable()))
case gl::ShaderType::Compute:
{
return false;
EmitWorkGroupSizeGLSL(*this, sink);
break;
}
if ((compileOptions & SH_ADD_PRE_ROTATION) != 0 &&
!AppendPreRotation(this, root, &getSymbolTable(), &surfaceRotationSpecConst,
driverUniforms))
{
return false;
}
}
else if (getShaderType() == GL_GEOMETRY_SHADER)
{
int maxVertices = getGeometryShaderMaxVertices();
// max_vertices=0 is not valid in Vulkan
maxVertices = std::max(1, maxVertices);
WriteGeometryShaderLayoutQualifiers(sink, getGeometryShaderInputPrimitiveType(),
getGeometryShaderInvocations(),
getGeometryShaderOutputPrimitiveType(), maxVertices);
}
else
{
ASSERT(getShaderType() == GL_COMPUTE_SHADER);
EmitWorkGroupSizeGLSL(*this, sink);
default:
UNREACHABLE();
break;
}
surfaceRotationSpecConst.outputLayoutString(sink);
......
......@@ -1276,6 +1276,19 @@ ShaderType ProgramState::getLastAttachedShaderStageType() const
return ShaderType::InvalidEnum;
}
ShaderType ProgramState::getAttachedTransformFeedbackStage() const
{
if (mAttachedShaders[ShaderType::Geometry])
{
return ShaderType::Geometry;
}
if (mAttachedShaders[ShaderType::TessEvaluation])
{
return ShaderType::TessEvaluation;
}
return ShaderType::Vertex;
}
Program::Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle)
: mSerial(factory->generateSerial()),
mProgram(factory->createProgram(mState)),
......
......@@ -372,6 +372,8 @@ class ProgramState final : angle::NonCopyable
int getBaseInstanceLocation() const { return mBaseInstanceLocation; }
ShaderType getAttachedTransformFeedbackStage() const;
private:
friend class MemoryProgramCache;
friend class Program;
......
......@@ -538,4 +538,17 @@ bool ProgramExecutable::isYUVOutput() const
{
return !isCompute() && mYUVOutput;
}
ShaderType ProgramExecutable::getLinkedTransformFeedbackStage() const
{
if (mLinkedGraphicsShaderStages[ShaderType::Geometry])
{
return ShaderType::Geometry;
}
if (mLinkedGraphicsShaderStages[ShaderType::TessEvaluation])
{
return ShaderType::TessEvaluation;
}
return ShaderType::Vertex;
}
} // namespace gl
......@@ -152,6 +152,8 @@ class ProgramExecutable final : public angle::Subject
: mLinkedGraphicsShaderStages.count();
}
ShaderType getLinkedTransformFeedbackStage() const;
// A PPO can have both graphics and compute programs attached, so
// we don't know if the PPO is a 'graphics' or 'compute' PPO until the
// actual draw/dispatch call.
......
......@@ -466,7 +466,7 @@ bool IsFirstRegisterOfVarying(const gl::PackedVaryingRegister &varyingReg)
// values for the SPIR-V transformation.
void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programState,
const gl::ProgramLinkedResources &resources,
std::string *vertexShader,
std::string *xfbShaderSource,
uint32_t *locationsUsedForXfbExtensionOut)
{
const std::vector<gl::TransformFeedbackVarying> &tfVaryings =
......@@ -502,7 +502,7 @@ void GenerateTransformFeedbackExtensionOutputs(const gl::ProgramState &programSt
}
}
*vertexShader = SubstituteTransformFeedbackMarkers(*vertexShader, xfbDecl, xfbOut);
*xfbShaderSource = SubstituteTransformFeedbackMarkers(*xfbShaderSource, xfbDecl, xfbOut);
}
void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable,
......@@ -3664,11 +3664,12 @@ void GlslangAssignLocations(const GlslangSourceOptions &options,
programInterfaceInfo, variableInfoMapOut);
if (!programExecutable.getLinkedTransformFeedbackVaryings().empty() &&
options.supportsTransformFeedbackExtension && (shaderType == gl::ShaderType::Vertex))
options.supportsTransformFeedbackExtension &&
(shaderType == programExecutable.getLinkedTransformFeedbackStage()))
{
AssignTransformFeedbackExtensionQualifiers(
programExecutable, programInterfaceInfo->locationsUsedForXfbExtension,
gl::ShaderType::Vertex, &(*variableInfoMapOut)[gl::ShaderType::Vertex]);
programExecutable, programInterfaceInfo->locationsUsedForXfbExtension, shaderType,
&(*variableInfoMapOut)[shaderType]);
}
}
......@@ -3693,34 +3694,48 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
(*shaderSourcesOut)[shaderType] = glShader ? glShader->getTranslatedSource() : "";
}
std::string *vertexSource = &(*shaderSourcesOut)[gl::ShaderType::Vertex];
gl::ShaderType xfbStage = programState.getAttachedTransformFeedbackStage();
std::string *xfbSource = &(*shaderSourcesOut)[xfbStage];
// Write transform feedback output code.
if (!vertexSource->empty())
if (!xfbSource->empty())
{
if (programState.getLinkedTransformFeedbackVaryings().empty())
{
*vertexSource = SubstituteTransformFeedbackMarkers(*vertexSource, "", "");
}
else
if (!programState.getLinkedTransformFeedbackVaryings().empty())
{
if (options.supportsTransformFeedbackExtension)
{
GenerateTransformFeedbackExtensionOutputs(
programState, resources, vertexSource,
programState, resources, xfbSource,
&programInterfaceInfo->locationsUsedForXfbExtension);
}
else if (options.emulateTransformFeedback)
{
GenerateTransformFeedbackEmulationOutputs(
options, programState, programInterfaceInfo, vertexSource,
&(*variableInfoMapOut)[gl::ShaderType::Vertex]);
ASSERT(xfbStage == gl::ShaderType::Vertex);
GenerateTransformFeedbackEmulationOutputs(options, programState,
programInterfaceInfo, xfbSource,
&(*variableInfoMapOut)[xfbStage]);
}
else
{
*vertexSource = SubstituteTransformFeedbackMarkers(*vertexSource, "", "");
*xfbSource = SubstituteTransformFeedbackMarkers(*xfbSource, "", "");
}
}
else
{
*xfbSource = SubstituteTransformFeedbackMarkers(*xfbSource, "", "");
}
}
std::string *tessEvalSources = &(*shaderSourcesOut)[gl::ShaderType::TessEvaluation];
if (xfbStage > gl::ShaderType::TessEvaluation && !tessEvalSources->empty())
{
*tessEvalSources = SubstituteTransformFeedbackMarkers(*tessEvalSources, "", "");
}
std::string *vertexSource = &(*shaderSourcesOut)[gl::ShaderType::Vertex];
if (xfbStage > gl::ShaderType::Vertex && !vertexSource->empty())
{
*vertexSource = SubstituteTransformFeedbackMarkers(*vertexSource, "", "");
}
gl::ShaderType frontShaderType = gl::ShaderType::InvalidEnum;
......
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