Commit 8e9d2340 by Jamie Madill Committed by Commit Bot

Vulkan: Fix FragCoord scaling when a viewport is applied.

We were using the pivot based on the viewport dimensions which is only valid if the viewport is the size of the framebuffer. The more correct pivot size is actually based on the Framebuffer height. Also updates the driver uniforms block to be a bit simpler. Bug: angleproject:2598 Change-Id: I1cb500cded7141d10e8db6862b6ed29758cc7fb4 Reviewed-on: https://chromium-review.googlesource.com/1214205 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarFrank Henigman <fjhenigman@chromium.org>
parent 480edb8c
......@@ -161,8 +161,24 @@ TIntermConstantUnion *CreateConstantFloat(float value)
return new TIntermConstantUnion(constantValue, *constantType);
}
TIntermBinary *CreateDriverUniformRef(const TVariable *driverUniforms, int fieldIndex)
size_t FieldFieldIndex(const TFieldList &fieldList, const char *fieldName)
{
for (size_t fieldIndex = 0; fieldIndex < fieldList.size(); ++fieldIndex)
{
if (strcmp(fieldList[fieldIndex]->name().data(), fieldName) == 0)
{
return fieldIndex;
}
}
UNREACHABLE();
return 0;
}
TIntermBinary *CreateDriverUniformRef(const TVariable *driverUniforms, const char *fieldName)
{
size_t fieldIndex =
FieldFieldIndex(driverUniforms->getType().getInterfaceBlock()->fields(), fieldName);
TIntermSymbol *angleUniformsRef = new TIntermSymbol(driverUniforms);
TConstantUnion *uniformIndex = new TConstantUnion;
uniformIndex->setIConst(fieldIndex);
......@@ -173,8 +189,7 @@ TIntermBinary *CreateDriverUniformRef(const TVariable *driverUniforms, int field
// Replaces a builtin variable with a version that corrects the Y coordinate.
void FlipBuiltinVariable(TIntermBlock *root,
const TVariable *driverUniforms,
int driverUniformSwizzleIndex,
TIntermTyped *viewportYScale,
TSymbolTable *symbolTable,
const TVariable *builtin,
const ImmutableString &flippedVariableName,
......@@ -199,19 +214,9 @@ void FlipBuiltinVariable(TIntermBlock *root,
// Use this new variable instead of 'builtin' everywhere.
ReplaceVariable(root, builtin, replacementVar);
// ANGLEUniforms.viewportScaleFactor
TIntermBinary *viewportScaleFactorRef = CreateDriverUniformRef(driverUniforms, 1);
// Creates a swizzle to ANGLEUniforms.viewportScaleFactor[index]
TVector<int> viewportScaleSwizzleOffsetY;
viewportScaleSwizzleOffsetY.push_back(driverUniformSwizzleIndex);
TIntermSwizzle *viewportScaleY =
new TIntermSwizzle(viewportScaleFactorRef->deepCopy(), viewportScaleSwizzleOffsetY);
// Create the expression "(builtin.y - pivot) * ANGLEUniforms.viewportScaleFactor[index] +
// pivot
// Create the expression "(builtin.y - pivot) * viewportYScale + pivot
TIntermBinary *removePivot = new TIntermBinary(EOpSub, builtinY, pivot);
TIntermBinary *inverseY = new TIntermBinary(EOpMul, removePivot, viewportScaleY);
TIntermBinary *inverseY = new TIntermBinary(EOpMul, removePivot, viewportYScale);
TIntermBinary *plusPivot = new TIntermBinary(EOpAdd, inverseY, pivot->deepCopy());
// Create the corrected variable and copy the value of the original builtin.
......@@ -241,7 +246,8 @@ void ReplaceGLDepthRangeWithDriverUniform(TIntermBlock *root,
symbolTable->findBuiltIn(ImmutableString("gl_DepthRange"), 0));
// ANGLEUniforms.depthRange
TIntermBinary *angleEmulatedDepthRangeRef = CreateDriverUniformRef(driverUniforms, 2);
TIntermBinary *angleEmulatedDepthRangeRef =
CreateDriverUniformRef(driverUniforms, "depthRange");
// Use this variable instead of gl_DepthRange everywhere.
ReplaceVariableWithTyped(root, depthRangeVar, angleEmulatedDepthRangeRef);
......@@ -287,54 +293,60 @@ void AppendVertexShaderDepthCorrectionToMain(TIntermBlock *root, TSymbolTable *s
RunAtTheEndOfShader(root, assignment, symbolTable);
}
constexpr const char *kHalfRenderAreaHeight = "halfRenderAreaHeight";
constexpr const char *kViewportYScale = "viewportYScale";
constexpr const char *kInviewportYScale = "invViewportYScale";
constexpr size_t kNumDriverUniforms = 6;
constexpr std::array<const char *, kNumDriverUniforms> kDriverUniformNames = {
{"viewport", kHalfRenderAreaHeight, kViewportYScale, kInviewportYScale, "padding",
"depthRange"}};
// The AddDriverUniformsToShader operation adds an internal uniform block to a shader. The driver
// block is used to implement Vulkan-specific features and workarounds. Returns the driver uniforms
// variable.
const TVariable *AddDriverUniformsToShader(TIntermBlock *root, TSymbolTable *symbolTable)
{
// This field list mirrors the structure of ContextVk::DriverUniforms.
TFieldList *driverFieldList = new TFieldList;
// Add a vec4 field "viewport" to the driver uniform fields.
TType *driverViewportType = new TType(EbtFloat, 4);
TField *driverViewportSize = new TField(driverViewportType, ImmutableString("viewport"),
TSourceLoc(), SymbolType::AngleInternal);
driverFieldList->push_back(driverViewportSize);
// Add a vec4 field "viewportScaleFactor" to the driver uniform fields.
TType *driverViewportScaleFactorType = new TType(EbtFloat, 4);
TField *driverViewportScaleFactorSize =
new TField(driverViewportScaleFactorType, ImmutableString("viewportScaleFactor"),
TSourceLoc(), SymbolType::AngleInternal);
driverFieldList->push_back(driverViewportScaleFactorSize);
// Add a new struct field "depthRange" to the driver uniform fields.
const TSourceLoc zeroSourceLoc = {0, 0, 0, 0};
// Init the depth range type.
TFieldList *depthRangeParamsFields = new TFieldList();
depthRangeParamsFields->push_back(new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1),
ImmutableString("near"), zeroSourceLoc,
ImmutableString("near"), TSourceLoc(),
SymbolType::AngleInternal));
depthRangeParamsFields->push_back(new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1),
ImmutableString("far"), zeroSourceLoc,
ImmutableString("far"), TSourceLoc(),
SymbolType::AngleInternal));
depthRangeParamsFields->push_back(new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1),
ImmutableString("diff"), zeroSourceLoc,
ImmutableString("diff"), TSourceLoc(),
SymbolType::AngleInternal));
depthRangeParamsFields->push_back(new TField(new TType(EbtFloat, EbpHigh, EvqGlobal, 1, 1),
ImmutableString("dummyPacker"), zeroSourceLoc,
ImmutableString("dummyPacker"), TSourceLoc(),
SymbolType::AngleInternal));
TStructure *emulatedDepthRangeParams = new TStructure(
symbolTable, kEmulatedDepthRangeParams, depthRangeParamsFields, SymbolType::AngleInternal);
TType *emulatedDepthRangeType = new TType(emulatedDepthRangeParams, false);
// Declare a global depth range variable.
TVariable *depthRangeVar =
new TVariable(symbolTable->nextUniqueId(), kEmptyImmutableString, SymbolType::Empty,
TExtension::UNDEFINED, emulatedDepthRangeType);
DeclareGlobalVariable(root, depthRangeVar);
TField *driverDepthRangeSize = new TField(emulatedDepthRangeType, ImmutableString("depthRange"),
// This field list mirrors the structure of ContextVk::DriverUniforms.
TFieldList *driverFieldList = new TFieldList;
const std::array<TType *, kNumDriverUniforms> kDriverUniformTypes = {{
new TType(EbtFloat, 4), new TType(EbtFloat), new TType(EbtFloat), new TType(EbtFloat),
new TType(EbtFloat), emulatedDepthRangeType,
}};
for (size_t uniformIndex = 0; uniformIndex < kNumDriverUniforms; ++uniformIndex)
{
TField *driverUniformField = new TField(kDriverUniformTypes[uniformIndex],
ImmutableString(kDriverUniformNames[uniformIndex]),
TSourceLoc(), SymbolType::AngleInternal);
driverFieldList->push_back(driverDepthRangeSize);
driverFieldList->push_back(driverUniformField);
}
// Define a driver uniform block "ANGLEUniformBlock".
TLayoutQualifier driverLayoutQualifier = TLayoutQualifier::Create();
......@@ -464,8 +476,10 @@ void TranslatorVulkan::translate(TIntermBlock *root,
if (inputVarying.name == "gl_PointCoord")
{
TIntermBinary *viewportYScale =
CreateDriverUniformRef(driverUniforms, kInviewportYScale);
TIntermConstantUnion *pivot = CreateConstantFloat(0.5f);
FlipBuiltinVariable(root, driverUniforms, 1, &getSymbolTable(),
FlipBuiltinVariable(root, viewportYScale, &getSymbolTable(),
BuiltInVariable::gl_PointCoord(), kFlippedPointCoordName,
pivot);
break;
......@@ -473,15 +487,11 @@ void TranslatorVulkan::translate(TIntermBlock *root,
if (inputVarying.name == "gl_FragCoord")
{
// Create a reference to ANGLEDriverUniforms.viewport.w * 0.5
TIntermBinary *viewportRef = CreateDriverUniformRef(driverUniforms, 0);
TVector<int> swizzleOffset;
swizzleOffset.push_back(3);
TIntermSwizzle *viewportSwizzle = new TIntermSwizzle(viewportRef, swizzleOffset);
TIntermTyped *oneHalf = CreateConstantFloat(0.5f);
TIntermBinary *pivot = new TIntermBinary(EOpMul, viewportSwizzle, oneHalf);
FlipBuiltinVariable(root, driverUniforms, 3, &getSymbolTable(),
TIntermBinary *viewportYScale =
CreateDriverUniformRef(driverUniforms, kViewportYScale);
TIntermBinary *pivot =
CreateDriverUniformRef(driverUniforms, kHalfRenderAreaHeight);
FlipBuiltinVariable(root, viewportYScale, &getSymbolTable(),
BuiltInVariable::gl_FragCoord(), kFlippedFragCoordName, pivot);
break;
}
......
......@@ -1091,6 +1091,8 @@ angle::Result ContextVk::handleDirtyDriverUniforms(const gl::Context *context,
mDriverUniformsBuffer.releaseRetainedBuffers(mRenderer);
const gl::Rectangle &glViewport = mState.getState().getViewport();
float halfRenderAreaHeight =
static_cast<float>(mDrawFramebuffer->getState().getDimensions().height) * 0.5f;
// Allocate a new region in the dynamic buffer.
uint8_t *ptr = nullptr;
......@@ -1110,7 +1112,10 @@ angle::Result ContextVk::handleDirtyDriverUniforms(const gl::Context *context,
*driverUniforms = {
{static_cast<float>(glViewport.x), static_cast<float>(glViewport.y),
static_cast<float>(glViewport.width), static_cast<float>(glViewport.height)},
{1.0f, -scaleY, 1.0f, scaleY},
halfRenderAreaHeight,
scaleY,
-scaleY,
0.0f,
{depthRangeNear, depthRangeFar, depthRangeDiff, 0.0f}};
ANGLE_TRY(mDriverUniformsBuffer.flush(this));
......
......@@ -284,11 +284,16 @@ class ContextVk : public ContextImpl, public vk::Context
struct DriverUniforms
{
std::array<float, 4> viewport;
std::array<float, 4> viewportScaleFactor;
float halfRenderAreaHeight;
float viewportYScale;
float invViewportYScale;
float padding;
// We'll use x, y, z for near / far / diff respectively.
std::array<float, 4> depthRange;
};
vk::DynamicBuffer mDriverUniformsBuffer;
VkDescriptorSet mDriverUniformsDescriptorSet;
vk::BindingPointer<vk::DescriptorSetLayout> mDriverUniformsSetLayout;
......
......@@ -4836,6 +4836,26 @@ void main()
EXPECT_EQ(userFBOData, backbufferData);
}
bool SubrectEquals(const std::vector<GLColor> &bigArray,
const std::vector<GLColor> &smallArray,
int bigSize,
int offset,
int smallSize)
{
int badPixels = 0;
for (int y = 0; y < smallSize; y++)
{
for (int x = 0; x < smallSize; x++)
{
int bigOffset = (y + offset) * bigSize + x + offset;
int smallOffset = y * smallSize + x;
if (bigArray[bigOffset] != smallArray[smallOffset])
badPixels++;
}
}
return badPixels == 0;
}
// Tests that FragCoord behaves the same betweeen a user FBO and the back buffer.
TEST_P(GLSLTest, FragCoordConsistency)
{
......@@ -4881,7 +4901,38 @@ void main()
ASSERT_GL_NO_ERROR();
ASSERT_EQ(userFBOData.size(), backbufferData.size());
EXPECT_EQ(userFBOData, backbufferData);
EXPECT_EQ(userFBOData, backbufferData)
<< "FragCoord should be the same to default and user FBO";
// Repeat the same test but with a smaller viewport.
ASSERT_EQ(getWindowHeight(), getWindowWidth());
const int kQuarterSize = getWindowWidth() >> 2;
glViewport(kQuarterSize, kQuarterSize, kQuarterSize * 2, kQuarterSize * 2);
glClearColor(1.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5);
std::vector<GLColor> userFBOViewportData(kQuarterSize * kQuarterSize * 4);
glReadPixels(kQuarterSize, kQuarterSize, kQuarterSize * 2, kQuarterSize * 2, GL_RGBA,
GL_UNSIGNED_BYTE, userFBOViewportData.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5);
std::vector<GLColor> defaultFBOViewportData(kQuarterSize * kQuarterSize * 4);
glReadPixels(kQuarterSize, kQuarterSize, kQuarterSize * 2, kQuarterSize * 2, GL_RGBA,
GL_UNSIGNED_BYTE, defaultFBOViewportData.data());
ASSERT_GL_NO_ERROR();
EXPECT_EQ(userFBOViewportData, defaultFBOViewportData)
<< "FragCoord should be the same to default and user FBO even with a custom viewport";
// Check that the subrectangles are the same between the viewport and non-viewport modes.
EXPECT_TRUE(SubrectEquals(userFBOData, userFBOViewportData, getWindowWidth(), kQuarterSize,
kQuarterSize * 2));
EXPECT_TRUE(SubrectEquals(backbufferData, defaultFBOViewportData, getWindowWidth(),
kQuarterSize, kQuarterSize * 2));
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these
......
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