зеркало из https://github.com/AvaloniaUI/angle.git
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: Tim Van Patten <timvp@google.com>
This commit is contained in:
Родитель
b9b1bae4ef
Коммит
3f647b1bf9
|
@ -504,6 +504,9 @@ struct ShBuiltInResources
|
|||
int MaxGeometryShaderStorageBlocks;
|
||||
int MaxGeometryShaderInvocations;
|
||||
int MaxGeometryImageUniforms;
|
||||
|
||||
// Subpixel bits used in rasterization.
|
||||
int SubPixelBits;
|
||||
};
|
||||
|
||||
//
|
||||
|
|
|
@ -253,6 +253,8 @@ void InitBuiltInResources(ShBuiltInResources *resources)
|
|||
resources->MaxGeometryShaderStorageBlocks = 0;
|
||||
resources->MaxGeometryShaderInvocations = 32;
|
||||
resources->MaxGeometryImageUniforms = 0;
|
||||
|
||||
resources->SubPixelBits = 8;
|
||||
}
|
||||
|
||||
//
|
||||
|
|
|
@ -438,7 +438,7 @@ TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root,
|
|||
insertSequence->push_back(GenerateLineRasterIfDef());
|
||||
|
||||
// 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"),
|
||||
varyingType, SymbolType::AngleInternal);
|
||||
TIntermSymbol *varyingDeclarator = new TIntermSymbol(varyingVar);
|
||||
|
@ -455,22 +455,72 @@ TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root,
|
|||
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);
|
||||
|
||||
// Create an assignment "ANGLEPosition = gl_Position".
|
||||
const TVariable *position = BuiltInVariable::gl_Position();
|
||||
TIntermSymbol *varyingRef = new TIntermSymbol(anglePosition);
|
||||
TIntermBinary *assignment =
|
||||
new TIntermBinary(EOpAssign, varyingRef, new TIntermSymbol(position));
|
||||
// Clamp position to subpixel grid.
|
||||
// Do perspective divide (get normalized device coords)
|
||||
// "vec2 ndc = gl_Position.xy / gl_Position.w"
|
||||
const TType *vec2Type = StaticType::GetBasic<EbtFloat, 2>();
|
||||
TIntermBinary *viewportRef = CreateDriverUniformRef(driverUniforms, kViewport);
|
||||
TIntermSymbol *glPos = new TIntermSymbol(BuiltInVariable::gl_Position());
|
||||
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);
|
||||
|
||||
// Ensure the assignment runs at the end of the main() function.
|
||||
// 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);
|
||||
TIntermBlock *mainBody = main->getBody();
|
||||
mainBody->appendStatement(GenerateLineRasterIfDef());
|
||||
mainBody->appendStatement(assignment);
|
||||
mainBody->appendStatement(ndcDecl);
|
||||
mainBody->appendStatement(windowDecl);
|
||||
mainBody->appendStatement(clampedDecl);
|
||||
mainBody->appendStatement(varyingAssign);
|
||||
mainBody->appendStatement(GenerateEndIf());
|
||||
return compiler->validateAST(root);
|
||||
}
|
||||
|
||||
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
|
||||
// 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()
|
||||
// {
|
||||
// vec2 b = (((position.xy / position.w) * 0.5) + 0.5) * gl_Viewport.zw + gl_Viewport.xy;
|
||||
// vec2 ba = abs(b - gl_FragCoord.xy);
|
||||
// vec2 ba2 = 2.0 * (ba * ba);
|
||||
// vec2 bp = ba2 + ba2.yx - ba;
|
||||
// if (bp.x > epsilon && bp.y > epsilon)
|
||||
// discard;
|
||||
// vec2 p = (((((ANGLEPosition.xy) * 0.5) + 0.5) * viewport.zw) + viewport.xy);
|
||||
// vec2 d = dFdx(p) + dFdy(p);
|
||||
// vec2 f = gl_FragCoord.xy;
|
||||
// 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.5 + e) && i.y > (0.5 + e))
|
||||
// discard;
|
||||
// <otherwise run fragment shader main>
|
||||
// }
|
||||
ANGLE_NO_DISCARD bool AddLineSegmentRasterizationEmulation(TCompiler *compiler,
|
||||
TInfoSinkBase &sink,
|
||||
TIntermBlock *root,
|
||||
TSymbolTable *symbolTable,
|
||||
const TVariable *driverUniforms,
|
||||
bool usesFragCoord)
|
||||
//
|
||||
// Note this emulation can not provide fully correct rasterization. See the docs more more info.
|
||||
|
||||
ANGLE_NO_DISCARD bool AddBresenhamEmulationFS(TCompiler *compiler,
|
||||
TInfoSinkBase &sink,
|
||||
TIntermBlock *root,
|
||||
TSymbolTable *symbolTable,
|
||||
const TVariable *driverUniforms,
|
||||
bool usesFragCoord)
|
||||
{
|
||||
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>();
|
||||
|
||||
// Create a swizzle to "ANGLEUniforms.viewport.xy".
|
||||
TIntermBinary *viewportRef = CreateDriverUniformRef(driverUniforms, kViewport);
|
||||
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
|
||||
// vec2 p = ((ANGLEPosition * 0.5) + 0.5) * viewport.zw + viewport.xy
|
||||
TIntermSwizzle *viewportXY = CreateSwizzle(viewportRef->deepCopy(), 0, 1);
|
||||
TIntermSwizzle *viewportZW = CreateSwizzle(viewportRef, 2, 3);
|
||||
TIntermSymbol *position = new TIntermSymbol(anglePosition);
|
||||
TIntermConstantUnion *oneHalf = CreateFloatNode(0.5f);
|
||||
TIntermBinary *halfPosition = new TIntermBinary(EOpVectorTimesScalar, positionNDC, oneHalf);
|
||||
|
||||
// (ANGLEPosition * 0.5) + 0.5
|
||||
TIntermBinary *halfPosition = new TIntermBinary(EOpVectorTimesScalar, position, oneHalf);
|
||||
TIntermBinary *offsetHalfPosition =
|
||||
new TIntermBinary(EOpAdd, halfPosition, oneHalf->deepCopy());
|
||||
|
||||
// ((ANGLEPosition * 0.5) + 0.5) * ANGLEUniforms.viewport.zw
|
||||
TIntermBinary *scaledPosition = new TIntermBinary(EOpMul, offsetHalfPosition, viewportZW);
|
||||
|
||||
// ((ANGLEPosition * 0.5) + 0.5) * ANGLEUniforms.viewport + ANGLEUniforms.viewport.xy
|
||||
TIntermBinary *windowPosition = new TIntermBinary(EOpAdd, scaledPosition, viewportXY);
|
||||
TVariable *p = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *pDecl = CreateTempInitDeclarationNode(p, windowPosition);
|
||||
|
||||
// Assign to a temporary "b".
|
||||
TVariable *bTemp = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *bDecl = CreateTempInitDeclarationNode(bTemp, windowPosition);
|
||||
// vec2 d = dFdx(p) + dFdy(p)
|
||||
TIntermUnary *dfdx = new TIntermUnary(EOpDFdx, new TIntermSymbol(p), nullptr);
|
||||
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();
|
||||
TIntermSymbol *fragCoordRef = new TIntermSymbol(fragCoord);
|
||||
TIntermSwizzle *fragCoordXY = new TIntermSwizzle(fragCoordRef, swizzleOffsetXY);
|
||||
TIntermSwizzle *fragCoordXY = CreateSwizzle(new TIntermSymbol(fragCoord), 0, 1);
|
||||
TVariable *f = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *fDecl = CreateTempInitDeclarationNode(f, fragCoordXY);
|
||||
|
||||
// b - gl_FragCoord.xy
|
||||
TIntermSymbol *bRef = CreateTempSymbolNode(bTemp);
|
||||
TIntermBinary *differenceExpr = new TIntermBinary(EOpSub, bRef, fragCoordXY);
|
||||
// vec2 p_ = p.yx
|
||||
TIntermSwizzle *pyx = CreateSwizzle(new TIntermSymbol(p), 1, 0);
|
||||
TVariable *p_ = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *p_decl = CreateTempInitDeclarationNode(p_, pyx);
|
||||
|
||||
// abs(b - gl_FragCoord.xy)
|
||||
TIntermUnary *baAbs = new TIntermUnary(EOpAbs, differenceExpr, nullptr);
|
||||
// vec2 d_ = d.yx
|
||||
TIntermSwizzle *dyx = CreateSwizzle(new TIntermSymbol(d), 1, 0);
|
||||
TVariable *d_ = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *d_decl = CreateTempInitDeclarationNode(d_, dyx);
|
||||
|
||||
// Assign to a temporary "ba".
|
||||
TVariable *baTemp = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *baDecl = CreateTempInitDeclarationNode(baTemp, baAbs);
|
||||
TIntermSymbol *ba = CreateTempSymbolNode(baTemp);
|
||||
// vec2 f_ = f.yx
|
||||
TIntermSwizzle *fyx = CreateSwizzle(new TIntermSymbol(f), 1, 0);
|
||||
TVariable *f_ = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *f_decl = CreateTempInitDeclarationNode(f_, fyx);
|
||||
|
||||
// ba * ba
|
||||
TIntermBinary *baSq = new TIntermBinary(EOpMul, ba, ba->deepCopy());
|
||||
|
||||
// 2.0 * ba * ba
|
||||
TIntermTyped *two = CreateFloatNode(2.0f);
|
||||
TIntermBinary *twoBaSq = new TIntermBinary(EOpVectorTimesScalar, baSq, two);
|
||||
|
||||
// Assign to a temporary "ba2".
|
||||
TVariable *ba2Temp = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *ba2Decl = CreateTempInitDeclarationNode(ba2Temp, twoBaSq);
|
||||
|
||||
// Create a swizzle to "ba2.yx".
|
||||
TVector<int> swizzleOffsetYX = {1, 0};
|
||||
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);
|
||||
// vec2 i = abs(p - f + (d/d_) * (f_ - p_))
|
||||
TIntermBinary *dd = new TIntermBinary(EOpDiv, new TIntermSymbol(d), new TIntermSymbol(d_));
|
||||
TIntermBinary *fp = new TIntermBinary(EOpSub, new TIntermSymbol(f_), new TIntermSymbol(p_));
|
||||
TIntermBinary *ddfp = new TIntermBinary(EOpMul, dd, fp);
|
||||
TIntermBinary *pf = new TIntermBinary(EOpSub, new TIntermSymbol(p), new TIntermSymbol(f));
|
||||
TIntermBinary *expr = new TIntermBinary(EOpAdd, pf, ddfp);
|
||||
TIntermUnary *absd = new TIntermUnary(EOpAbs, expr, nullptr);
|
||||
TVariable *i = CreateTempVariable(symbolTable, vec2Type);
|
||||
TIntermDeclaration *iDecl = CreateTempInitDeclarationNode(i, absd);
|
||||
|
||||
// Using a small epsilon value ensures that we don't suffer from numerical instability when
|
||||
// lines are exactly vertical or horizontal.
|
||||
static constexpr float kEpisilon = 0.00001f;
|
||||
TIntermConstantUnion *epsilon = CreateFloatNode(kEpisilon);
|
||||
static constexpr float kEpsilon = 0.0001f;
|
||||
static constexpr float kThreshold = 0.5 + kEpsilon;
|
||||
TIntermConstantUnion *threshold = CreateFloatNode(kThreshold);
|
||||
|
||||
// bp.x > epsilon
|
||||
TIntermBinary *checkX = new TIntermBinary(EOpGreaterThan, bpX, epsilon);
|
||||
|
||||
// Create a swizzle to "bp.y".
|
||||
TVector<int> swizzleOffsetY = {1};
|
||||
TIntermSwizzle *bpY = new TIntermSwizzle(bp->deepCopy(), swizzleOffsetY);
|
||||
|
||||
// bp.y > epsilon
|
||||
TIntermBinary *checkY = new TIntermBinary(EOpGreaterThan, bpY, epsilon->deepCopy());
|
||||
|
||||
// (bp.x > epsilon) && (bp.y > epsilon)
|
||||
// if (i.x > (0.5 + e) && i.y > (0.5 + e))
|
||||
TIntermSwizzle *ix = CreateSwizzle(new TIntermSymbol(i), 0);
|
||||
TIntermBinary *checkX = new TIntermBinary(EOpGreaterThan, ix, threshold);
|
||||
TIntermSwizzle *iy = CreateSwizzle(new TIntermSymbol(i), 1);
|
||||
TIntermBinary *checkY = new TIntermBinary(EOpGreaterThan, iy, threshold->deepCopy());
|
||||
TIntermBinary *checkXY = new TIntermBinary(EOpLogicalAnd, checkX, checkY);
|
||||
|
||||
// discard
|
||||
TIntermBranch *discard = new TIntermBranch(EOpKill, nullptr);
|
||||
TIntermBlock *discardBlock = new TIntermBlock;
|
||||
discardBlock->appendStatement(discard);
|
||||
|
||||
// if ((bp.x > epsilon) && (bp.y > epsilon)) discard;
|
||||
TIntermIfElse *ifStatement = new TIntermIfElse(checkXY, discardBlock, nullptr);
|
||||
|
||||
// Ensure the line raster code runs at the beginning of main().
|
||||
|
@ -632,8 +654,8 @@ ANGLE_NO_DISCARD bool AddLineSegmentRasterizationEmulation(TCompiler *compiler,
|
|||
TIntermSequence *mainSequence = main->getBody()->getSequence();
|
||||
ASSERT(mainSequence);
|
||||
|
||||
std::array<TIntermNode *, 6> nodes = {
|
||||
{bDecl, baDecl, ba2Decl, bpDecl, ifStatement, GenerateEndIf()}};
|
||||
std::array<TIntermNode *, 9> nodes = {
|
||||
{pDecl, dDecl, fDecl, p_decl, d_decl, f_decl, iDecl, ifStatement, GenerateEndIf()}};
|
||||
mainSequence->insert(mainSequence->begin(), nodes.begin(), nodes.end());
|
||||
|
||||
// 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,
|
|||
}
|
||||
|
||||
mainSequence->insert(mainSequence->begin(), GenerateLineRasterIfDef());
|
||||
|
||||
return compiler->validateAST(root);
|
||||
}
|
||||
|
||||
|
@ -836,8 +857,8 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
|
|||
}
|
||||
}
|
||||
|
||||
if (!AddLineSegmentRasterizationEmulation(this, sink, root, &getSymbolTable(),
|
||||
driverUniforms, usesFragCoord))
|
||||
if (!AddBresenhamEmulationFS(this, sink, root, &getSymbolTable(), driverUniforms,
|
||||
usesFragCoord))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -902,7 +923,10 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
|
|||
}
|
||||
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.
|
||||
sink << "@@ XFB-DECL @@\n\n";
|
||||
|
@ -921,8 +945,6 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
|
|||
}
|
||||
else if (getShaderType() == GL_GEOMETRY_SHADER)
|
||||
{
|
||||
AddANGLEPositionVarying(root, &getSymbolTable());
|
||||
|
||||
WriteGeometryShaderLayoutQualifiers(
|
||||
sink, getGeometryShaderInputPrimitiveType(), getGeometryShaderInvocations(),
|
||||
getGeometryShaderOutputPrimitiveType(), getGeometryShaderMaxVertices());
|
||||
|
|
|
@ -73,6 +73,23 @@ TIntermTyped *CreateBuiltInFunctionCallNode(const char *name,
|
|||
const TSymbolTable &symbolTable,
|
||||
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
|
||||
|
||||
#endif // COMPILER_TRANSLATOR_INTERMNODEUTIL_H_
|
||||
|
|
|
@ -187,6 +187,9 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const State &state)
|
|||
mResources.MaxGeometryShaderStorageBlocks = caps.maxShaderStorageBlocks[ShaderType::Geometry];
|
||||
mResources.MaxGeometryShaderInvocations = caps.maxGeometryShaderInvocations;
|
||||
mResources.MaxGeometryImageUniforms = caps.maxShaderImageUniforms[ShaderType::Geometry];
|
||||
|
||||
// Subpixel bits.
|
||||
mResources.SubPixelBits = static_cast<int>(caps.subPixelBits);
|
||||
}
|
||||
|
||||
Compiler::~Compiler()
|
||||
|
|
|
@ -23,22 +23,29 @@ more info. See the below diagram for an illustration of the diamond rule:
|
|||
|
||||
![OpenGL Diamond Rule Example][DiamondRule]
|
||||
|
||||
We can implement the OpenGL test by checking the intersection of the line and the medial axes of the
|
||||
pixel `p`. If the length of the line segment between intersections `p` and the point center is
|
||||
greater than a half-pixel for all possible `p` then the pixel is not on the segment. To solve for
|
||||
`p` we use the pixel center `a` given by `gl_FragCoord` and the projection of `a` onto the line
|
||||
segment `b` given by the interpolated `gl_Position`. Since `gl_Position` is not available in the
|
||||
fragment shader we must add an internal position varying when drawing lines.
|
||||
The diamond rule can be implemented in the fragment shader by computing the
|
||||
intersection between the line segment and the grid that crosses the pixel
|
||||
center. If the distance between an intersection and the pixel center is less
|
||||
than half a pixel then the line enters and exits the diamond. `f` is the pixel
|
||||
center in the diagram. The green circle indicates a diamond exit and the red
|
||||
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
|
||||
fragment shader patch implementation:
|
||||
|
||||
```vec2 position = PositionVarying.xy / PositionVarying.w;
|
||||
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 bp = ba2 + ba2.yx - ba;
|
||||
if (bp.x > epsilon && bp.y > epsilon)
|
||||
discard;
|
||||
```
|
||||
vec2 p = (((((ANGLEPosition.xy) * 0.5) + 0.5) * viewport.zw) + viewport.xy);
|
||||
vec2 d = dFdx(p) + dFdy(p);
|
||||
vec2 f = gl_FragCoord.xy;
|
||||
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
|
||||
|
@ -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
|
||||
`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
|
||||
[DiamondRule]: img/LineRasterPixelExample.png
|
||||
[Holes]: img/LineRasterHoles.jpg
|
||||
[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
|
||||
[VulkanVsGLLineRaster]: img/LineRasterComparison.gif
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 2.0 KiB |
Двоичные данные
src/libANGLE/renderer/vulkan/doc/img/LineRasterPixelExample.png
Двоичные данные
src/libANGLE/renderer/vulkan/doc/img/LineRasterPixelExample.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 8.1 KiB После Ширина: | Высота: | Размер: 17 KiB |
Загрузка…
Ссылка в новой задаче