Commit 3f647b1b by Jamie Madill Committed by Commit Bot

Vulkan: Improve Bresenham line emulation.

Clamps the vertex position to the subpixel grid before interpolation. This will give more correct results on systems that have less than 8 bits of subpixel accuracy. Also uses a more accurate formulation for the emulation filter in the fragment shader using dfdx and dfdy. Fixes line raster CTS tests on SwiftShader. Still does not produce spec conformant lines. Updates the public docs to indicate this. Bug: angleproject:2830 Change-Id: Ib9a268df3e7d986bd2b1348be664389fe8fc0ef2 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1826598 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent b9b1bae4
...@@ -504,6 +504,9 @@ struct ShBuiltInResources ...@@ -504,6 +504,9 @@ struct ShBuiltInResources
int MaxGeometryShaderStorageBlocks; int MaxGeometryShaderStorageBlocks;
int MaxGeometryShaderInvocations; int MaxGeometryShaderInvocations;
int MaxGeometryImageUniforms; int MaxGeometryImageUniforms;
// Subpixel bits used in rasterization.
int SubPixelBits;
}; };
// //
......
...@@ -253,6 +253,8 @@ void InitBuiltInResources(ShBuiltInResources *resources) ...@@ -253,6 +253,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->MaxGeometryShaderStorageBlocks = 0; resources->MaxGeometryShaderStorageBlocks = 0;
resources->MaxGeometryShaderInvocations = 32; resources->MaxGeometryShaderInvocations = 32;
resources->MaxGeometryImageUniforms = 0; resources->MaxGeometryImageUniforms = 0;
resources->SubPixelBits = 8;
} }
// //
......
...@@ -438,7 +438,7 @@ TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root, ...@@ -438,7 +438,7 @@ TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root,
insertSequence->push_back(GenerateLineRasterIfDef()); insertSequence->push_back(GenerateLineRasterIfDef());
// Define a driver varying vec2 "ANGLEPosition". // Define a driver varying vec2 "ANGLEPosition".
TType *varyingType = new TType(EbtFloat, EbpMedium, qualifier, 4); TType *varyingType = new TType(EbtFloat, EbpMedium, qualifier, 2);
TVariable *varyingVar = new TVariable(symbolTable, ImmutableString("ANGLEPosition"), TVariable *varyingVar = new TVariable(symbolTable, ImmutableString("ANGLEPosition"),
varyingType, SymbolType::AngleInternal); varyingType, SymbolType::AngleInternal);
TIntermSymbol *varyingDeclarator = new TIntermSymbol(varyingVar); TIntermSymbol *varyingDeclarator = new TIntermSymbol(varyingVar);
...@@ -455,22 +455,72 @@ TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root, ...@@ -455,22 +455,72 @@ TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root,
return varyingVar; return varyingVar;
} }
void AddANGLEPositionVarying(TIntermBlock *root, TSymbolTable *symbolTable) ANGLE_NO_DISCARD bool AddBresenhamEmulationVS(TCompiler *compiler,
TIntermBlock *root,
TSymbolTable *symbolTable,
const TVariable *driverUniforms)
{ {
TVariable *anglePosition = AddANGLEPositionVaryingDeclaration(root, symbolTable, EvqVaryingOut); TVariable *anglePosition = AddANGLEPositionVaryingDeclaration(root, symbolTable, EvqVaryingOut);
// Create an assignment "ANGLEPosition = gl_Position". // Clamp position to subpixel grid.
const TVariable *position = BuiltInVariable::gl_Position(); // Do perspective divide (get normalized device coords)
TIntermSymbol *varyingRef = new TIntermSymbol(anglePosition); // "vec2 ndc = gl_Position.xy / gl_Position.w"
TIntermBinary *assignment = const TType *vec2Type = StaticType::GetBasic<EbtFloat, 2>();
new TIntermBinary(EOpAssign, varyingRef, new TIntermSymbol(position)); TIntermBinary *viewportRef = CreateDriverUniformRef(driverUniforms, kViewport);
TIntermSymbol *glPos = new TIntermSymbol(BuiltInVariable::gl_Position());
// Ensure the assignment runs at the end of the main() function. TIntermSwizzle *glPosXY = CreateSwizzle(glPos, 0, 1);
TIntermSwizzle *glPosW = CreateSwizzle(glPos->deepCopy(), 3);
TVariable *ndc = CreateTempVariable(symbolTable, vec2Type);
TIntermBinary *noPerspective = new TIntermBinary(EOpDiv, glPosXY, glPosW);
TIntermDeclaration *ndcDecl = CreateTempInitDeclarationNode(ndc, noPerspective);
// Convert NDC to window coordinates. According to Vulkan spec.
// "vec2 window = 0.5 * viewport.wh * (ndc + 1) + viewport.xy"
TIntermBinary *ndcPlusOne =
new TIntermBinary(EOpAdd, CreateTempSymbolNode(ndc), CreateFloatNode(1.0f));
TIntermSwizzle *viewportZW = CreateSwizzle(viewportRef, 2, 3);
TIntermBinary *ndcViewport = new TIntermBinary(EOpMul, viewportZW, ndcPlusOne);
TIntermBinary *ndcViewportHalf =
new TIntermBinary(EOpVectorTimesScalar, ndcViewport, CreateFloatNode(0.5f));
TIntermSwizzle *viewportXY = CreateSwizzle(viewportRef->deepCopy(), 0, 1);
TIntermBinary *ndcToWindow = new TIntermBinary(EOpAdd, ndcViewportHalf, viewportXY);
TVariable *windowCoords = CreateTempVariable(symbolTable, vec2Type);
TIntermDeclaration *windowDecl = CreateTempInitDeclarationNode(windowCoords, ndcToWindow);
// Clamp to subpixel grid.
// "vec2 clamped = round(window * 2^{subpixelBits}) / 2^{subpixelBits}"
int subpixelBits = compiler->getResources().SubPixelBits;
TIntermConstantUnion *scaleConstant = CreateFloatNode(static_cast<float>(1 << subpixelBits));
TIntermBinary *windowScaled =
new TIntermBinary(EOpVectorTimesScalar, CreateTempSymbolNode(windowCoords), scaleConstant);
TIntermUnary *windowRounded = new TIntermUnary(EOpRound, windowScaled, nullptr);
TIntermBinary *windowRoundedBack =
new TIntermBinary(EOpDiv, windowRounded, scaleConstant->deepCopy());
TVariable *clampedWindowCoords = CreateTempVariable(symbolTable, vec2Type);
TIntermDeclaration *clampedDecl =
CreateTempInitDeclarationNode(clampedWindowCoords, windowRoundedBack);
// Set varying.
// "ANGLEPosition = 2 * (clamped - viewport.xy) / viewport.wh - 1"
TIntermBinary *clampedOffset = new TIntermBinary(
EOpSub, CreateTempSymbolNode(clampedWindowCoords), viewportXY->deepCopy());
TIntermBinary *clampedOff2x =
new TIntermBinary(EOpVectorTimesScalar, clampedOffset, CreateFloatNode(2.0f));
TIntermBinary *clampedDivided = new TIntermBinary(EOpDiv, clampedOff2x, viewportZW->deepCopy());
TIntermBinary *clampedNDC = new TIntermBinary(EOpSub, clampedDivided, CreateFloatNode(1.0f));
TIntermSymbol *varyingRef = new TIntermSymbol(anglePosition);
TIntermBinary *varyingAssign = new TIntermBinary(EOpAssign, varyingRef, clampedNDC);
// Ensure the statements run at the end of the main() function.
TIntermFunctionDefinition *main = FindMain(root); TIntermFunctionDefinition *main = FindMain(root);
TIntermBlock *mainBody = main->getBody(); TIntermBlock *mainBody = main->getBody();
mainBody->appendStatement(GenerateLineRasterIfDef()); mainBody->appendStatement(GenerateLineRasterIfDef());
mainBody->appendStatement(assignment); mainBody->appendStatement(ndcDecl);
mainBody->appendStatement(windowDecl);
mainBody->appendStatement(clampedDecl);
mainBody->appendStatement(varyingAssign);
mainBody->appendStatement(GenerateEndIf()); mainBody->appendStatement(GenerateEndIf());
return compiler->validateAST(root);
} }
ANGLE_NO_DISCARD bool InsertFragCoordCorrection(TCompiler *compiler, ANGLE_NO_DISCARD bool InsertFragCoordCorrection(TCompiler *compiler,
...@@ -498,133 +548,105 @@ ANGLE_NO_DISCARD bool InsertFragCoordCorrection(TCompiler *compiler, ...@@ -498,133 +548,105 @@ ANGLE_NO_DISCARD bool InsertFragCoordCorrection(TCompiler *compiler,
// formula to test if the line segment crosses the pixel center. gl_FragCoord is used along with an // formula to test if the line segment crosses the pixel center. gl_FragCoord is used along with an
// internal position varying to determine the inputs to the formula. // internal position varying to determine the inputs to the formula.
// //
// The implementation of the test code is similar to the following pseudocode: // The implementation of the test is similar to the following pseudocode:
// //
// void main() // void main()
// { // {
// vec2 b = (((position.xy / position.w) * 0.5) + 0.5) * gl_Viewport.zw + gl_Viewport.xy; // vec2 p = (((((ANGLEPosition.xy) * 0.5) + 0.5) * viewport.zw) + viewport.xy);
// vec2 ba = abs(b - gl_FragCoord.xy); // vec2 d = dFdx(p) + dFdy(p);
// vec2 ba2 = 2.0 * (ba * ba); // vec2 f = gl_FragCoord.xy;
// vec2 bp = ba2 + ba2.yx - ba; // vec2 p_ = p.yx;
// if (bp.x > epsilon && bp.y > epsilon) // vec2 d_ = d.yx;
// discard; // vec2 f_ = f.yx;
//
// vec2 i = abs(p - f + (d / d_) * (f_ - p_));
//
// if (i.x > (0.5 + e) && i.y > (0.5 + e))
// discard;
// <otherwise run fragment shader main> // <otherwise run fragment shader main>
// } // }
ANGLE_NO_DISCARD bool AddLineSegmentRasterizationEmulation(TCompiler *compiler, //
TInfoSinkBase &sink, // Note this emulation can not provide fully correct rasterization. See the docs more more info.
TIntermBlock *root,
TSymbolTable *symbolTable, ANGLE_NO_DISCARD bool AddBresenhamEmulationFS(TCompiler *compiler,
const TVariable *driverUniforms, TInfoSinkBase &sink,
bool usesFragCoord) TIntermBlock *root,
TSymbolTable *symbolTable,
const TVariable *driverUniforms,
bool usesFragCoord)
{ {
TVariable *anglePosition = AddANGLEPositionVaryingDeclaration(root, symbolTable, EvqVaryingIn); TVariable *anglePosition = AddANGLEPositionVaryingDeclaration(root, symbolTable, EvqVaryingIn);
const TType *vec2Type = StaticType::GetBasic<EbtFloat, 2>();
TIntermBinary *viewportRef = CreateDriverUniformRef(driverUniforms, kViewport);
const TType *vec2Type = StaticType::GetBasic<EbtFloat, 2>(); // vec2 p = ((ANGLEPosition * 0.5) + 0.5) * viewport.zw + viewport.xy
TIntermSwizzle *viewportXY = CreateSwizzle(viewportRef->deepCopy(), 0, 1);
// Create a swizzle to "ANGLEUniforms.viewport.xy". TIntermSwizzle *viewportZW = CreateSwizzle(viewportRef, 2, 3);
TIntermBinary *viewportRef = CreateDriverUniformRef(driverUniforms, kViewport); TIntermSymbol *position = new TIntermSymbol(anglePosition);
TVector<int> swizzleOffsetXY = {0, 1};
TIntermSwizzle *viewportXY = new TIntermSwizzle(viewportRef->deepCopy(), swizzleOffsetXY);
// Create a swizzle to "ANGLEUniforms.viewport.zw".
TVector<int> swizzleOffsetZW = {2, 3};
TIntermSwizzle *viewportZW = new TIntermSwizzle(viewportRef, swizzleOffsetZW);
// ANGLEPosition.xy / ANGLEPosition.w
TIntermSymbol *position = new TIntermSymbol(anglePosition);
TIntermSwizzle *positionXY = new TIntermSwizzle(position, swizzleOffsetXY);
TVector<int> swizzleOffsetW = {3};
TIntermSwizzle *positionW = new TIntermSwizzle(position->deepCopy(), swizzleOffsetW);
TIntermBinary *positionNDC = new TIntermBinary(EOpDiv, positionXY, positionW);
// ANGLEPosition * 0.5
TIntermConstantUnion *oneHalf = CreateFloatNode(0.5f); TIntermConstantUnion *oneHalf = CreateFloatNode(0.5f);
TIntermBinary *halfPosition = new TIntermBinary(EOpVectorTimesScalar, positionNDC, oneHalf); TIntermBinary *halfPosition = new TIntermBinary(EOpVectorTimesScalar, position, oneHalf);
// (ANGLEPosition * 0.5) + 0.5
TIntermBinary *offsetHalfPosition = TIntermBinary *offsetHalfPosition =
new TIntermBinary(EOpAdd, halfPosition, oneHalf->deepCopy()); new TIntermBinary(EOpAdd, halfPosition, oneHalf->deepCopy());
// ((ANGLEPosition * 0.5) + 0.5) * ANGLEUniforms.viewport.zw
TIntermBinary *scaledPosition = new TIntermBinary(EOpMul, offsetHalfPosition, viewportZW); TIntermBinary *scaledPosition = new TIntermBinary(EOpMul, offsetHalfPosition, viewportZW);
// ((ANGLEPosition * 0.5) + 0.5) * ANGLEUniforms.viewport + ANGLEUniforms.viewport.xy
TIntermBinary *windowPosition = new TIntermBinary(EOpAdd, scaledPosition, viewportXY); TIntermBinary *windowPosition = new TIntermBinary(EOpAdd, scaledPosition, viewportXY);
TVariable *p = CreateTempVariable(symbolTable, vec2Type);
TIntermDeclaration *pDecl = CreateTempInitDeclarationNode(p, windowPosition);
// Assign to a temporary "b". // vec2 d = dFdx(p) + dFdy(p)
TVariable *bTemp = CreateTempVariable(symbolTable, vec2Type); TIntermUnary *dfdx = new TIntermUnary(EOpDFdx, new TIntermSymbol(p), nullptr);
TIntermDeclaration *bDecl = CreateTempInitDeclarationNode(bTemp, windowPosition); TIntermUnary *dfdy = new TIntermUnary(EOpDFdy, new TIntermSymbol(p), nullptr);
TIntermBinary *dfsum = new TIntermBinary(EOpAdd, dfdx, dfdy);
TVariable *d = CreateTempVariable(symbolTable, vec2Type);
TIntermDeclaration *dDecl = CreateTempInitDeclarationNode(d, dfsum);
// gl_FragCoord.xy // vec2 f = gl_FragCoord.xy
const TVariable *fragCoord = BuiltInVariable::gl_FragCoord(); const TVariable *fragCoord = BuiltInVariable::gl_FragCoord();
TIntermSymbol *fragCoordRef = new TIntermSymbol(fragCoord); TIntermSwizzle *fragCoordXY = CreateSwizzle(new TIntermSymbol(fragCoord), 0, 1);
TIntermSwizzle *fragCoordXY = new TIntermSwizzle(fragCoordRef, swizzleOffsetXY); TVariable *f = CreateTempVariable(symbolTable, vec2Type);
TIntermDeclaration *fDecl = CreateTempInitDeclarationNode(f, fragCoordXY);
// b - gl_FragCoord.xy
TIntermSymbol *bRef = CreateTempSymbolNode(bTemp); // vec2 p_ = p.yx
TIntermBinary *differenceExpr = new TIntermBinary(EOpSub, bRef, fragCoordXY); TIntermSwizzle *pyx = CreateSwizzle(new TIntermSymbol(p), 1, 0);
TVariable *p_ = CreateTempVariable(symbolTable, vec2Type);
// abs(b - gl_FragCoord.xy) TIntermDeclaration *p_decl = CreateTempInitDeclarationNode(p_, pyx);
TIntermUnary *baAbs = new TIntermUnary(EOpAbs, differenceExpr, nullptr);
// vec2 d_ = d.yx
// Assign to a temporary "ba". TIntermSwizzle *dyx = CreateSwizzle(new TIntermSymbol(d), 1, 0);
TVariable *baTemp = CreateTempVariable(symbolTable, vec2Type); TVariable *d_ = CreateTempVariable(symbolTable, vec2Type);
TIntermDeclaration *baDecl = CreateTempInitDeclarationNode(baTemp, baAbs); TIntermDeclaration *d_decl = CreateTempInitDeclarationNode(d_, dyx);
TIntermSymbol *ba = CreateTempSymbolNode(baTemp);
// vec2 f_ = f.yx
// ba * ba TIntermSwizzle *fyx = CreateSwizzle(new TIntermSymbol(f), 1, 0);
TIntermBinary *baSq = new TIntermBinary(EOpMul, ba, ba->deepCopy()); TVariable *f_ = CreateTempVariable(symbolTable, vec2Type);
TIntermDeclaration *f_decl = CreateTempInitDeclarationNode(f_, fyx);
// 2.0 * ba * ba
TIntermTyped *two = CreateFloatNode(2.0f); // vec2 i = abs(p - f + (d/d_) * (f_ - p_))
TIntermBinary *twoBaSq = new TIntermBinary(EOpVectorTimesScalar, baSq, two); TIntermBinary *dd = new TIntermBinary(EOpDiv, new TIntermSymbol(d), new TIntermSymbol(d_));
TIntermBinary *fp = new TIntermBinary(EOpSub, new TIntermSymbol(f_), new TIntermSymbol(p_));
// Assign to a temporary "ba2". TIntermBinary *ddfp = new TIntermBinary(EOpMul, dd, fp);
TVariable *ba2Temp = CreateTempVariable(symbolTable, vec2Type); TIntermBinary *pf = new TIntermBinary(EOpSub, new TIntermSymbol(p), new TIntermSymbol(f));
TIntermDeclaration *ba2Decl = CreateTempInitDeclarationNode(ba2Temp, twoBaSq); TIntermBinary *expr = new TIntermBinary(EOpAdd, pf, ddfp);
TIntermUnary *absd = new TIntermUnary(EOpAbs, expr, nullptr);
// Create a swizzle to "ba2.yx". TVariable *i = CreateTempVariable(symbolTable, vec2Type);
TVector<int> swizzleOffsetYX = {1, 0}; TIntermDeclaration *iDecl = CreateTempInitDeclarationNode(i, absd);
TIntermSymbol *ba2 = CreateTempSymbolNode(ba2Temp);
TIntermSwizzle *ba2YX = new TIntermSwizzle(ba2, swizzleOffsetYX);
// ba2 + ba2.yx - ba
TIntermBinary *ba2PlusBaYX2 = new TIntermBinary(EOpAdd, ba2->deepCopy(), ba2YX);
TIntermBinary *bpInit = new TIntermBinary(EOpSub, ba2PlusBaYX2, ba->deepCopy());
// Assign to a temporary "bp".
TVariable *bpTemp = CreateTempVariable(symbolTable, vec2Type);
TIntermDeclaration *bpDecl = CreateTempInitDeclarationNode(bpTemp, bpInit);
TIntermSymbol *bp = CreateTempSymbolNode(bpTemp);
// Create a swizzle to "bp.x".
TVector<int> swizzleOffsetX = {0};
TIntermSwizzle *bpX = new TIntermSwizzle(bp, swizzleOffsetX);
// Using a small epsilon value ensures that we don't suffer from numerical instability when // Using a small epsilon value ensures that we don't suffer from numerical instability when
// lines are exactly vertical or horizontal. // lines are exactly vertical or horizontal.
static constexpr float kEpisilon = 0.00001f; static constexpr float kEpsilon = 0.0001f;
TIntermConstantUnion *epsilon = CreateFloatNode(kEpisilon); static constexpr float kThreshold = 0.5 + kEpsilon;
TIntermConstantUnion *threshold = CreateFloatNode(kThreshold);
// bp.x > epsilon
TIntermBinary *checkX = new TIntermBinary(EOpGreaterThan, bpX, epsilon); // if (i.x > (0.5 + e) && i.y > (0.5 + e))
TIntermSwizzle *ix = CreateSwizzle(new TIntermSymbol(i), 0);
// Create a swizzle to "bp.y". TIntermBinary *checkX = new TIntermBinary(EOpGreaterThan, ix, threshold);
TVector<int> swizzleOffsetY = {1}; TIntermSwizzle *iy = CreateSwizzle(new TIntermSymbol(i), 1);
TIntermSwizzle *bpY = new TIntermSwizzle(bp->deepCopy(), swizzleOffsetY); TIntermBinary *checkY = new TIntermBinary(EOpGreaterThan, iy, threshold->deepCopy());
// bp.y > epsilon
TIntermBinary *checkY = new TIntermBinary(EOpGreaterThan, bpY, epsilon->deepCopy());
// (bp.x > epsilon) && (bp.y > epsilon)
TIntermBinary *checkXY = new TIntermBinary(EOpLogicalAnd, checkX, checkY); TIntermBinary *checkXY = new TIntermBinary(EOpLogicalAnd, checkX, checkY);
// discard // discard
TIntermBranch *discard = new TIntermBranch(EOpKill, nullptr); TIntermBranch *discard = new TIntermBranch(EOpKill, nullptr);
TIntermBlock *discardBlock = new TIntermBlock; TIntermBlock *discardBlock = new TIntermBlock;
discardBlock->appendStatement(discard); discardBlock->appendStatement(discard);
// if ((bp.x > epsilon) && (bp.y > epsilon)) discard;
TIntermIfElse *ifStatement = new TIntermIfElse(checkXY, discardBlock, nullptr); TIntermIfElse *ifStatement = new TIntermIfElse(checkXY, discardBlock, nullptr);
// Ensure the line raster code runs at the beginning of main(). // Ensure the line raster code runs at the beginning of main().
...@@ -632,8 +654,8 @@ ANGLE_NO_DISCARD bool AddLineSegmentRasterizationEmulation(TCompiler *compiler, ...@@ -632,8 +654,8 @@ ANGLE_NO_DISCARD bool AddLineSegmentRasterizationEmulation(TCompiler *compiler,
TIntermSequence *mainSequence = main->getBody()->getSequence(); TIntermSequence *mainSequence = main->getBody()->getSequence();
ASSERT(mainSequence); ASSERT(mainSequence);
std::array<TIntermNode *, 6> nodes = { std::array<TIntermNode *, 9> nodes = {
{bDecl, baDecl, ba2Decl, bpDecl, ifStatement, GenerateEndIf()}}; {pDecl, dDecl, fDecl, p_decl, d_decl, f_decl, iDecl, ifStatement, GenerateEndIf()}};
mainSequence->insert(mainSequence->begin(), nodes.begin(), nodes.end()); mainSequence->insert(mainSequence->begin(), nodes.begin(), nodes.end());
// If the shader does not use frag coord, we should insert it inside the ifdef. // If the shader does not use frag coord, we should insert it inside the ifdef.
...@@ -646,7 +668,6 @@ ANGLE_NO_DISCARD bool AddLineSegmentRasterizationEmulation(TCompiler *compiler, ...@@ -646,7 +668,6 @@ ANGLE_NO_DISCARD bool AddLineSegmentRasterizationEmulation(TCompiler *compiler,
} }
mainSequence->insert(mainSequence->begin(), GenerateLineRasterIfDef()); mainSequence->insert(mainSequence->begin(), GenerateLineRasterIfDef());
return compiler->validateAST(root); return compiler->validateAST(root);
} }
...@@ -836,8 +857,8 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root, ...@@ -836,8 +857,8 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
} }
} }
if (!AddLineSegmentRasterizationEmulation(this, sink, root, &getSymbolTable(), if (!AddBresenhamEmulationFS(this, sink, root, &getSymbolTable(), driverUniforms,
driverUniforms, usesFragCoord)) usesFragCoord))
{ {
return false; return false;
} }
...@@ -902,7 +923,10 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root, ...@@ -902,7 +923,10 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
} }
else if (getShaderType() == GL_VERTEX_SHADER) else if (getShaderType() == GL_VERTEX_SHADER)
{ {
AddANGLEPositionVarying(root, &getSymbolTable()); if (!AddBresenhamEmulationVS(this, root, &getSymbolTable(), driverUniforms))
{
return false;
}
// Add a macro to declare transform feedback buffers. // Add a macro to declare transform feedback buffers.
sink << "@@ XFB-DECL @@\n\n"; sink << "@@ XFB-DECL @@\n\n";
...@@ -921,8 +945,6 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root, ...@@ -921,8 +945,6 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
} }
else if (getShaderType() == GL_GEOMETRY_SHADER) else if (getShaderType() == GL_GEOMETRY_SHADER)
{ {
AddANGLEPositionVarying(root, &getSymbolTable());
WriteGeometryShaderLayoutQualifiers( WriteGeometryShaderLayoutQualifiers(
sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(), sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
getGeometryShaderOutputPrimitiveType(), getGeometryShaderMaxVertices()); getGeometryShaderOutputPrimitiveType(), getGeometryShaderMaxVertices());
......
...@@ -73,6 +73,23 @@ TIntermTyped *CreateBuiltInFunctionCallNode(const char *name, ...@@ -73,6 +73,23 @@ TIntermTyped *CreateBuiltInFunctionCallNode(const char *name,
const TSymbolTable &symbolTable, const TSymbolTable &symbolTable,
int shaderVersion); int shaderVersion);
inline void GetSwizzleIndex(TVector<int> *indexOut) {}
template <typename T, typename... ArgsT>
void GetSwizzleIndex(TVector<int> *indexOut, T arg, ArgsT... args)
{
indexOut->push_back(arg);
GetSwizzleIndex(indexOut, args...);
}
template <typename... ArgsT>
TIntermSwizzle *CreateSwizzle(TIntermTyped *reference, ArgsT... args)
{
TVector<int> swizzleIndex;
GetSwizzleIndex(&swizzleIndex, args...);
return new TIntermSwizzle(reference, swizzleIndex);
}
} // namespace sh } // namespace sh
#endif // COMPILER_TRANSLATOR_INTERMNODEUTIL_H_ #endif // COMPILER_TRANSLATOR_INTERMNODEUTIL_H_
...@@ -187,6 +187,9 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state) ...@@ -187,6 +187,9 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state)
mResources.MaxGeometryShaderStorageBlocks = caps.maxShaderStorageBlocks[ShaderType::Geometry]; mResources.MaxGeometryShaderStorageBlocks = caps.maxShaderStorageBlocks[ShaderType::Geometry];
mResources.MaxGeometryShaderInvocations = caps.maxGeometryShaderInvocations; mResources.MaxGeometryShaderInvocations = caps.maxGeometryShaderInvocations;
mResources.MaxGeometryImageUniforms = caps.maxShaderImageUniforms[ShaderType::Geometry]; mResources.MaxGeometryImageUniforms = caps.maxShaderImageUniforms[ShaderType::Geometry];
// Subpixel bits.
mResources.SubPixelBits = static_cast<int>(caps.subPixelBits);
} }
Compiler::~Compiler() Compiler::~Compiler()
......
...@@ -23,22 +23,29 @@ more info. See the below diagram for an illustration of the diamond rule: ...@@ -23,22 +23,29 @@ more info. See the below diagram for an illustration of the diamond rule:
![OpenGL Diamond Rule Example][DiamondRule] ![OpenGL Diamond Rule Example][DiamondRule]
We can implement the OpenGL test by checking the intersection of the line and the medial axes of the The diamond rule can be implemented in the fragment shader by computing the
pixel `p`. If the length of the line segment between intersections `p` and the point center is intersection between the line segment and the grid that crosses the pixel
greater than a half-pixel for all possible `p` then the pixel is not on the segment. To solve for center. If the distance between an intersection and the pixel center is less
`p` we use the pixel center `a` given by `gl_FragCoord` and the projection of `a` onto the line than half a pixel then the line enters and exits the diamond. `f` is the pixel
segment `b` given by the interpolated `gl_Position`. Since `gl_Position` is not available in the center in the diagram. The green circle indicates a diamond exit and the red
fragment shader we must add an internal position varying when drawing lines. circles indicate intersections that do not exit the diamond. We detect
non-Bresenham fragments when both intersections are outside the diamond.
The full code derivation is omitted for brevity. It reduces to the following shader snippet:
The full code derivation is omitted for brevity. It produces the following
```vec2 position = PositionVarying.xy / PositionVarying.w; fragment shader patch implementation:
vec2 b = ((position * 0.5) + 0.5) * gl_Viewport.zw + gl_Viewport.xy;
vec2 ba = abs(b - gl_FragCoord.xy); ```
vec2 ba2 = 2.0 * (ba * ba); vec2 p = (((((ANGLEPosition.xy) * 0.5) + 0.5) * viewport.zw) + viewport.xy);
vec2 bp = ba2 + ba2.yx - ba; vec2 d = dFdx(p) + dFdy(p);
if (bp.x > epsilon && bp.y > epsilon) vec2 f = gl_FragCoord.xy;
discard; vec2 p_ = p.yx;
vec2 d_ = d.yx;
vec2 f_ = f.yx;
vec2 i = abs(p - f + (d/d_) * (f_ - p_));
if (i.x > 0.500001 && i.y > 0.500001)
discard;
``` ```
Note that we must also pass the viewport size as an internal uniform. We use a small epsilon value Note that we must also pass the viewport size as an internal uniform. We use a small epsilon value
...@@ -46,8 +53,22 @@ to correct for cases when the line segment is perfectly parallel or perpendicula ...@@ -46,8 +53,22 @@ to correct for cases when the line segment is perfectly parallel or perpendicula
code please see [TranslatorVulkan.cpp][TranslatorVulkan.cpp] under code please see [TranslatorVulkan.cpp][TranslatorVulkan.cpp] under
`AddLineSegmentRasterizationEmulation`. `AddLineSegmentRasterizationEmulation`.
## Limitations
Although this emulation passes all current GLES CTS tests it is not guaranteed
to produce conformant lines. In particular lines that very nearly intersect
the junction of four pixels render with holes. For example:
![Holes in the emulated Bresenham line][Holes]
Therefore for a complete implementation we require the Bresenham line
rasterization feature from
[VK_EXT_line_rasterization][VK_EXT_line_rasterization].
[Bresenham]: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm [Bresenham]: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
[DiamondRule]: img/LineRasterPixelExample.png [DiamondRule]: img/LineRasterPixelExample.png
[Holes]: img/LineRasterHoles.jpg
[TranslatorVulkan.cpp]: https://chromium.googlesource.com/angle/angle/+/refs/heads/master/src/compiler/translator/TranslatorVulkan.cpp [TranslatorVulkan.cpp]: https://chromium.googlesource.com/angle/angle/+/refs/heads/master/src/compiler/translator/TranslatorVulkan.cpp
[VK_EXT_line_rasterization]: https://www.khronos.org/registry/vulkan/specs/1.1-extensions/man/html/VK_EXT_line_rasterization.html
[VulkanLineRaster]: https://www.khronos.org/registry/vulkan/specs/1.1/html/chap24.html#primsrast-lines-basic [VulkanLineRaster]: https://www.khronos.org/registry/vulkan/specs/1.1/html/chap24.html#primsrast-lines-basic
[VulkanVsGLLineRaster]: img/LineRasterComparison.gif [VulkanVsGLLineRaster]: img/LineRasterComparison.gif
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