mirror of
https://github.com/godotengine/godot-angle-static.git
synced 2026-01-03 14:09:33 +03:00
Validate missing fragment outputs for dual-source blending
* A secondary fragment output must be declared when dual-source blending is enabled in WebGL contexts. * Omitting locations for multiple fragment outputs is not allowed in WebGL contexts. Bug: angleproject:1085 Change-Id: I57febdc02c9ccc571971a81b6671869f19b0aa96 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4834672 Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org> Commit-Queue: Alexey Knyazev <lexa.knyazev@gmail.com>
This commit is contained in:
committed by
Angle LUCI CQ
parent
7c3c7b7b9f
commit
9624db05ed
@@ -887,7 +887,7 @@ bool TCompiler::checkAndSimplifyAST(TIntermBlock *root,
|
||||
|
||||
if (mShaderVersion >= 300 && mShaderType == GL_FRAGMENT_SHADER &&
|
||||
!ValidateOutputs(root, getExtensionBehavior(), mResources, hasPixelLocalStorageUniforms(),
|
||||
&mDiagnostics))
|
||||
IsWebGLBasedSpec(mShaderSpec), &mDiagnostics))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,8 @@ class ValidateOutputsTraverser : public TIntermTraverser
|
||||
public:
|
||||
ValidateOutputsTraverser(const TExtensionBehavior &extBehavior,
|
||||
const ShBuiltInResources &resources,
|
||||
bool usesPixelLocalStorage);
|
||||
bool usesPixelLocalStorage,
|
||||
bool isWebGL);
|
||||
|
||||
void validate(TDiagnostics *diagnostics) const;
|
||||
|
||||
@@ -43,6 +44,7 @@ class ValidateOutputsTraverser : public TIntermTraverser
|
||||
bool mEnablesBlendFuncExtended;
|
||||
bool mUsesIndex1;
|
||||
bool mUsesPixelLocalStorage;
|
||||
bool mIsWebGL;
|
||||
bool mUsesFragDepth;
|
||||
|
||||
typedef std::vector<TIntermSymbol *> OutputVector;
|
||||
@@ -54,7 +56,8 @@ class ValidateOutputsTraverser : public TIntermTraverser
|
||||
|
||||
ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &extBehavior,
|
||||
const ShBuiltInResources &resources,
|
||||
bool usesPixelLocalStorage)
|
||||
bool usesPixelLocalStorage,
|
||||
bool isWebGL)
|
||||
: TIntermTraverser(true, false, false),
|
||||
mMaxDrawBuffers(resources.MaxDrawBuffers),
|
||||
mMaxDualSourceDrawBuffers(resources.MaxDualSourceDrawBuffers),
|
||||
@@ -62,6 +65,7 @@ ValidateOutputsTraverser::ValidateOutputsTraverser(const TExtensionBehavior &ext
|
||||
IsExtensionEnabled(extBehavior, TExtension::EXT_blend_func_extended)),
|
||||
mUsesIndex1(false),
|
||||
mUsesPixelLocalStorage(usesPixelLocalStorage),
|
||||
mIsWebGL(isWebGL),
|
||||
mUsesFragDepth(false)
|
||||
{}
|
||||
|
||||
@@ -187,6 +191,12 @@ void ValidateOutputsTraverser::validate(TDiagnostics *diagnostics) const
|
||||
"must explicitly specify all locations when using multiple fragment outputs and "
|
||||
"pixel local storage, even if EXT_blend_func_extended is enabled";
|
||||
}
|
||||
else if (mIsWebGL)
|
||||
{
|
||||
unspecifiedLocationErrorMessage =
|
||||
"must explicitly specify all locations when using multiple fragment outputs "
|
||||
"in WebGL contexts, even if EXT_blend_func_extended is enabled";
|
||||
}
|
||||
if (unspecifiedLocationErrorMessage != nullptr)
|
||||
{
|
||||
for (const auto &symbol : mUnspecifiedLocationOutputs)
|
||||
@@ -215,9 +225,11 @@ bool ValidateOutputs(TIntermBlock *root,
|
||||
const TExtensionBehavior &extBehavior,
|
||||
const ShBuiltInResources &resources,
|
||||
bool usesPixelLocalStorage,
|
||||
bool isWebGL,
|
||||
TDiagnostics *diagnostics)
|
||||
{
|
||||
ValidateOutputsTraverser validateOutputs(extBehavior, resources, usesPixelLocalStorage);
|
||||
ValidateOutputsTraverser validateOutputs(extBehavior, resources, usesPixelLocalStorage,
|
||||
isWebGL);
|
||||
root->traverse(&validateOutputs);
|
||||
int numErrorsBefore = diagnostics->numErrors();
|
||||
validateOutputs.validate(diagnostics);
|
||||
|
||||
@@ -27,6 +27,7 @@ bool ValidateOutputs(TIntermBlock *root,
|
||||
const TExtensionBehavior &extBehavior,
|
||||
const ShBuiltInResources &resources,
|
||||
bool usesPixelLocalStorage,
|
||||
bool isWebGL,
|
||||
TDiagnostics *diagnostics);
|
||||
|
||||
} // namespace sh
|
||||
|
||||
@@ -429,6 +429,7 @@ void ProgramExecutable::reset()
|
||||
mPODStruct.attributesMask.reset();
|
||||
mPODStruct.maxActiveAttribLocation = 0;
|
||||
mPODStruct.activeOutputVariablesMask.reset();
|
||||
mPODStruct.activeSecondaryOutputVariablesMask.reset();
|
||||
|
||||
mPODStruct.defaultUniformRange = RangeUI(0, 0);
|
||||
mPODStruct.samplerUniformRange = RangeUI(0, 0);
|
||||
@@ -1207,6 +1208,7 @@ bool ProgramExecutable::linkValidateOutputVariables(
|
||||
const ProgramAliasedBindings &fragmentOutputIndices)
|
||||
{
|
||||
ASSERT(mPODStruct.activeOutputVariablesMask.none());
|
||||
ASSERT(mPODStruct.activeSecondaryOutputVariablesMask.none());
|
||||
ASSERT(mPODStruct.drawBufferTypeMask.none());
|
||||
ASSERT(!mPODStruct.hasYUVOutput);
|
||||
|
||||
@@ -1425,7 +1427,9 @@ bool ProgramExecutable::gatherOutputTypes()
|
||||
for (const sh::ShaderVariable &outputVariable : mOutputVariables)
|
||||
{
|
||||
if (outputVariable.isBuiltIn() && outputVariable.name != "gl_FragColor" &&
|
||||
outputVariable.name != "gl_FragData")
|
||||
outputVariable.name != "gl_FragData" &&
|
||||
outputVariable.name != "gl_SecondaryFragColorEXT" &&
|
||||
outputVariable.name != "gl_SecondaryFragDataEXT")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -1434,6 +1438,10 @@ bool ProgramExecutable::gatherOutputTypes()
|
||||
(outputVariable.location == -1 ? 0u
|
||||
: static_cast<unsigned int>(outputVariable.location));
|
||||
|
||||
const bool secondary =
|
||||
outputVariable.index == 1 || (outputVariable.name == "gl_SecondaryFragColorEXT" ||
|
||||
outputVariable.name == "gl_SecondaryFragDataEXT");
|
||||
|
||||
const ComponentType componentType =
|
||||
GLenumToComponentType(VariableComponentType(outputVariable.type));
|
||||
|
||||
@@ -1444,7 +1452,15 @@ bool ProgramExecutable::gatherOutputTypes()
|
||||
{
|
||||
const unsigned int location = baseLocation + elementIndex;
|
||||
ASSERT(location < mPODStruct.activeOutputVariablesMask.size());
|
||||
mPODStruct.activeOutputVariablesMask.set(location);
|
||||
ASSERT(location < mPODStruct.activeSecondaryOutputVariablesMask.size());
|
||||
if (secondary)
|
||||
{
|
||||
mPODStruct.activeSecondaryOutputVariablesMask.set(location);
|
||||
}
|
||||
else
|
||||
{
|
||||
mPODStruct.activeOutputVariablesMask.set(location);
|
||||
}
|
||||
const ComponentType storedComponentType =
|
||||
gl::GetComponentTypeMask(mPODStruct.drawBufferTypeMask, location);
|
||||
if (storedComponentType == ComponentType::InvalidEnum)
|
||||
|
||||
@@ -490,6 +490,10 @@ class ProgramExecutable final : public angle::Subject
|
||||
{
|
||||
return mPODStruct.activeOutputVariablesMask;
|
||||
}
|
||||
DrawBufferMask getActiveSecondaryOutputVariablesMask() const
|
||||
{
|
||||
return mPODStruct.activeSecondaryOutputVariablesMask;
|
||||
}
|
||||
|
||||
bool linkUniforms(const Caps &caps,
|
||||
const ShaderMap<std::vector<sh::ShaderVariable>> &shaderUniforms,
|
||||
@@ -579,6 +583,7 @@ class ProgramExecutable final : public angle::Subject
|
||||
// removed.
|
||||
AttributesMask attributesMask;
|
||||
DrawBufferMask activeOutputVariablesMask;
|
||||
DrawBufferMask activeSecondaryOutputVariablesMask;
|
||||
ComponentTypeMask drawBufferTypeMask;
|
||||
|
||||
RangeUI defaultUniformRange;
|
||||
|
||||
@@ -486,11 +486,16 @@ bool ValidateFragmentShaderColorBufferMaskMatch(const Context *context)
|
||||
const Program *program = context->getActiveLinkedProgram();
|
||||
const Framebuffer *framebuffer = glState.getDrawFramebuffer();
|
||||
|
||||
auto drawBufferMask =
|
||||
framebuffer->getDrawBufferMask() & glState.getBlendStateExt().compareColorMask(0);
|
||||
const auto &blendStateExt = glState.getBlendStateExt();
|
||||
auto drawBufferMask = framebuffer->getDrawBufferMask() & blendStateExt.compareColorMask(0);
|
||||
auto dualSourceBlendingMask = drawBufferMask & blendStateExt.getEnabledMask() &
|
||||
blendStateExt.getUsesExtendedBlendFactorMask();
|
||||
auto fragmentOutputMask = program->getExecutable().getActiveOutputVariablesMask();
|
||||
auto fragmentSecondaryOutputMask =
|
||||
program->getExecutable().getActiveSecondaryOutputVariablesMask();
|
||||
|
||||
return drawBufferMask == (drawBufferMask & fragmentOutputMask);
|
||||
return drawBufferMask == (drawBufferMask & fragmentOutputMask) &&
|
||||
dualSourceBlendingMask == (dualSourceBlendingMask & fragmentSecondaryOutputMask);
|
||||
}
|
||||
|
||||
bool ValidateFragmentShaderColorBufferTypeMatch(const Context *context)
|
||||
|
||||
@@ -1177,6 +1177,10 @@ b/273271471 WIN INTEL VULKAN : ShaderAlgorithmTest.rgb_to_hsl_vertex_shader/* =
|
||||
8131 LINUX INTEL : SampleMultisampleInterpolationTest.SampleMaskInPerSample/* = SKIP
|
||||
8131 LINUX INTEL : SampleMultisampleInterpolationTest.SampleMaskInPerSampleNoPerspective/* = SKIP
|
||||
|
||||
// Driver bugs related to handling secondary fragment output arrays
|
||||
8336 ANDROID VULKAN : WebGLCompatibilityTest.EXTBlendFuncExtendedMissingOutputsArrays/* = SKIP
|
||||
8336 ANDROID VULKAN : WebGL2CompatibilityTest.EXTBlendFuncExtendedMissingOutputsArrays/* = SKIP
|
||||
|
||||
// bits 24..31 from glClearValueuiv value don't work on Intel Metal.
|
||||
7794 MAC INTEL METAL : PixelLocalStorageTest.ClearValues_r32/* = SKIP
|
||||
|
||||
|
||||
@@ -6140,6 +6140,199 @@ void main() {
|
||||
EXPECT_FALSE(prg.valid());
|
||||
}
|
||||
|
||||
// Test that EXT_blend_func_extended does not allow omitting locations in WebGL 2.0 contexts.
|
||||
TEST_P(WebGL2CompatibilityTest, EXTBlendFuncExtendedNoLocations)
|
||||
{
|
||||
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_blend_func_extended"));
|
||||
|
||||
constexpr char kFS[] = R"(#version 300 es
|
||||
#extension GL_EXT_blend_func_extended : require
|
||||
out highp vec4 color0;
|
||||
out highp vec4 color1;
|
||||
void main() {
|
||||
color0 = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
color1 = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
|
||||
GLProgram prg;
|
||||
prg.makeRaster(essl3_shaders::vs::Simple(), kFS);
|
||||
EXPECT_FALSE(prg.valid());
|
||||
}
|
||||
|
||||
// Test that a secondary fragment output is declared
|
||||
// when using EXT_blend_func_extended in WebGL 1.0 contexts.
|
||||
TEST_P(WebGLCompatibilityTest, EXTBlendFuncExtendedMissingOutputs)
|
||||
{
|
||||
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_blend_func_extended"));
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_SRC1_COLOR_EXT);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
{
|
||||
constexpr char kFragColor[] = R"(
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragColor);
|
||||
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
{
|
||||
constexpr char kSecondaryFragColor[] = R"(#extension GL_EXT_blend_func_extended : require
|
||||
void main() {
|
||||
gl_SecondaryFragColorEXT = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kSecondaryFragColor);
|
||||
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
{
|
||||
constexpr char kFragColorAndSecondaryFragColor[] =
|
||||
R"(#extension GL_EXT_blend_func_extended : require
|
||||
void main() {
|
||||
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
gl_SecondaryFragColorEXT = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragColorAndSecondaryFragColor);
|
||||
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a secondary fragment output is declared
|
||||
// when using EXT_blend_func_extended in WebGL 1.0 contexts.
|
||||
TEST_P(WebGLCompatibilityTest, EXTBlendFuncExtendedMissingOutputsArrays)
|
||||
{
|
||||
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_blend_func_extended"));
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_SRC1_COLOR_EXT);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
{
|
||||
constexpr char kFragData[] = R"(
|
||||
void main() {
|
||||
gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragData);
|
||||
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
{
|
||||
constexpr char kSecondaryFragData[] = R"(#extension GL_EXT_blend_func_extended : require
|
||||
void main() {
|
||||
gl_SecondaryFragDataEXT[0] = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kSecondaryFragData);
|
||||
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
{
|
||||
constexpr char kFragDataAndSecondaryFragData[] =
|
||||
R"(#extension GL_EXT_blend_func_extended : require
|
||||
void main() {
|
||||
gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
gl_SecondaryFragDataEXT[0] = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragDataAndSecondaryFragData);
|
||||
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a secondary fragment output is declared
|
||||
// when using EXT_blend_func_extended in WebGL 2.0 contexts.
|
||||
TEST_P(WebGL2CompatibilityTest, EXTBlendFuncExtendedMissingOutputs)
|
||||
{
|
||||
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_blend_func_extended"));
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_SRC1_COLOR_EXT);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
{
|
||||
constexpr char kColor0[] = R"(#version 300 es
|
||||
out mediump vec4 color0;
|
||||
void main() {
|
||||
color0 = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kColor0);
|
||||
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
{
|
||||
constexpr char kColor1[] = R"(#version 300 es
|
||||
#extension GL_EXT_blend_func_extended : require
|
||||
layout(location = 0, index = 1) out mediump vec4 color1;
|
||||
void main() {
|
||||
color1 = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kColor1);
|
||||
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
{
|
||||
constexpr char kColor0AndColor1[] = R"(#version 300 es
|
||||
#extension GL_EXT_blend_func_extended : require
|
||||
layout(location = 0, index = 0) out mediump vec4 color0;
|
||||
layout(location = 0, index = 1) out mediump vec4 color1;
|
||||
void main() {
|
||||
color0 = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
color1 = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kColor0AndColor1);
|
||||
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a secondary fragment output is declared
|
||||
// when using EXT_blend_func_extended in WebGL 2.0 contexts.
|
||||
TEST_P(WebGL2CompatibilityTest, EXTBlendFuncExtendedMissingOutputsArrays)
|
||||
{
|
||||
ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_EXT_blend_func_extended"));
|
||||
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_SRC1_COLOR_EXT);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
|
||||
{
|
||||
constexpr char kArrayColor0[] = R"(#version 300 es
|
||||
out mediump vec4 color0[1];
|
||||
void main() {
|
||||
color0[0] = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kArrayColor0);
|
||||
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
{
|
||||
constexpr char kArrayColor1[] = R"(#version 300 es
|
||||
#extension GL_EXT_blend_func_extended : require
|
||||
layout(location = 0, index = 1) out mediump vec4 color1[1];
|
||||
void main() {
|
||||
color1[0] = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kArrayColor1);
|
||||
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_ERROR(GL_INVALID_OPERATION);
|
||||
}
|
||||
{
|
||||
constexpr char kArrayColor0AndColor0[] = R"(#version 300 es
|
||||
#extension GL_EXT_blend_func_extended : require
|
||||
layout(location = 0, index = 0) out mediump vec4 color0[1];
|
||||
layout(location = 0, index = 1) out mediump vec4 color1[1];
|
||||
void main() {
|
||||
color0[0] = vec4(1.0, 0.0, 0.0, 1.0);
|
||||
color1[0] = vec4(0.0, 1.0, 0.0, 1.0);
|
||||
})";
|
||||
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kArrayColor0AndColor0);
|
||||
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f, 1.0f, true);
|
||||
ASSERT_GL_NO_ERROR();
|
||||
}
|
||||
}
|
||||
|
||||
// Test for a mishandling of instanced vertex attributes with zero-sized buffers bound on Apple
|
||||
// OpenGL drivers.
|
||||
TEST_P(WebGL2CompatibilityTest, DrawWithZeroSizedBuffer)
|
||||
|
||||
Reference in New Issue
Block a user