Vulkan: Shader support for KHR_blend_equation_advanced

Translator can accept the layout qualifiers for the advanced blend
equation.  No emulation code is currently generated, and ANGLE will
initially rely on the corresponding Vulkan extension.

Based on change by Brandon Schade <b.schade@samsung.com>

Bug: angleproject:3586
Test: angle_unittests --gtest_filter=*KHRBlendEquationAdvanced*
Change-Id: I3b728c5f144386d7030bbbb301ddb07daa1492b9
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3481309
Reviewed-by: Brandon Schade <b.schade@samsung.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
This commit is contained in:
Shahbaz Youssefi
2022-02-22 10:51:15 -05:00
committed by Angle LUCI CQ
parent b69349bc62
commit 1e773db907
23 changed files with 759 additions and 47 deletions

View File

@@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 270
#define ANGLE_SH_VERSION 271
enum ShShaderSpec
{
@@ -418,6 +418,7 @@ struct ShBuiltInResources
int OES_primitive_bounding_box;
int ANGLE_base_vertex_base_instance_shader_builtin;
int ANDROID_extension_pack_es31a;
int KHR_blend_equation_advanced;
// Set to 1 to enable replacing GL_EXT_draw_buffers #extension directives
// with GL_NV_draw_buffers in ESSL output. This flag can be used to emulate
@@ -794,6 +795,10 @@ GLenum GetTessGenSpacing(const ShHandle handle);
GLenum GetTessGenVertexOrder(const ShHandle handle);
GLenum GetTessGenPointMode(const ShHandle handle);
// Returns the blend equation list supported in the fragment shader. This is a bitset of
// gl::BlendEquationType, and can only include bits from KHR_blend_equation_advanced.
uint32_t GetAdvancedBlendEquations(const ShHandle handle);
//
// Helper function to identify specs that are based on the WebGL spec.
//

View File

@@ -15,7 +15,7 @@
#include <string>
#include <vector>
// This type is defined here to simplify ANGLE's integration with glslang for SPIRv.
// This type is defined here to simplify ANGLE's integration with glslang for SPIR-V.
using ShCompileOptions = uint64_t;
namespace sh
@@ -219,7 +219,7 @@ struct ShaderVariable
bool readonly;
bool writeonly;
// From EXT_shader_framebuffer_fetch
// From EXT_shader_framebuffer_fetch / KHR_blend_equation_advanced
bool isFragmentInOut;
// OutputVariable

View File

@@ -4,11 +4,11 @@
"src/compiler/translator/generate_parser.py":
"ad919972a040d9b3b4aa5dc547fadc75",
"src/compiler/translator/glslang.l":
"921021111f9906b6b4869375b152499b",
"9b5ed85f6601eb6b13a9476916e448e0",
"src/compiler/translator/glslang.y":
"bbc109b19c5a4724d0e2f9191ac52603",
"src/compiler/translator/glslang_lex_autogen.cpp":
"18f56a5303e6b7e061d8aadef69faf9b",
"9ad7d62cfd81e36db3c97390c8f8ad26",
"src/compiler/translator/glslang_tab_autogen.cpp":
"d2408828dd4449860b1bd06ea97707a5",
"src/compiler/translator/glslang_tab_autogen.h":

View File

@@ -347,6 +347,51 @@ std::ostream &operator<<(std::ostream &os, BlendEquationType value)
case BlendEquationType::ReverseSubtract:
os << "GL_FUNC_REVERSE_SUBTRACT";
break;
case BlendEquationType::Multiply:
os << "GL_MULTIPLY_KHR";
break;
case BlendEquationType::Screen:
os << "GL_SCREEN_KHR";
break;
case BlendEquationType::Overlay:
os << "GL_OVERLAY_KHR";
break;
case BlendEquationType::Darken:
os << "GL_DARKEN_KHR";
break;
case BlendEquationType::Lighten:
os << "GL_LIGHTEN_KHR";
break;
case BlendEquationType::Colordodge:
os << "GL_COLORDODGE_KHR";
break;
case BlendEquationType::Colorburn:
os << "GL_COLORBURN_KHR";
break;
case BlendEquationType::Hardlight:
os << "GL_HARDLIGHT_KHR";
break;
case BlendEquationType::Softlight:
os << "GL_SOFTLIGHT_KHR";
break;
case BlendEquationType::Difference:
os << "GL_DIFFERENCE_KHR";
break;
case BlendEquationType::Exclusion:
os << "GL_EXCLUSION_KHR";
break;
case BlendEquationType::HslHue:
os << "GL_HSL_HUE_KHR";
break;
case BlendEquationType::HslSaturation:
os << "GL_HSL_SATURATION_KHR";
break;
case BlendEquationType::HslColor:
os << "GL_HSL_COLOR_KHR";
break;
case BlendEquationType::HslLuminosity:
os << "GL_HSL_LUMINOSITY_KHR";
break;
default:
os << "GL_INVALID_ENUM";
break;

View File

@@ -341,23 +341,72 @@ enum class BlendEquationType
Unused = 3,
Subtract = 4, // GLenum == 0x800A
ReverseSubtract = 5, // GLenum == 0x800B
InvalidEnum = 6,
EnumCount = 6
Multiply = 6, // GLenum == 0x9294
Screen = 7, // GLenum == 0x9295
Overlay = 8, // GLenum == 0x9296
Darken = 9, // GLenum == 0x9297
Lighten = 10, // GLenum == 0x9298
Colordodge = 11, // GLenum == 0x9299
Colorburn = 12, // GLenum == 0x929A
Hardlight = 13, // GLenum == 0x929B
Softlight = 14, // GLenum == 0x929C
Unused2 = 15,
Difference = 16, // GLenum == 0x929E
Unused3 = 17,
Exclusion = 18, // GLenum == 0x92A0
HslHue = 19, // GLenum == 0x92AD
HslSaturation = 20, // GLenum == 0x92AE
HslColor = 21, // GLenum == 0x92AF
HslLuminosity = 22, // GLenum == 0x92B0
InvalidEnum = 23,
EnumCount = InvalidEnum
};
using BlendEquationBitSet = angle::PackedEnumBitSet<gl::BlendEquationType>;
template <>
constexpr BlendEquationType FromGLenum<BlendEquationType>(GLenum from)
{
const GLenum scaled = (from - GL_FUNC_ADD);
return (scaled == static_cast<GLenum>(BlendEquationType::Unused) ||
scaled >= static_cast<GLenum>(BlendEquationType::EnumCount))
? BlendEquationType::InvalidEnum
: static_cast<BlendEquationType>(scaled);
if (from <= GL_FUNC_REVERSE_SUBTRACT)
{
const GLenum scaled = (from - GL_FUNC_ADD);
return (scaled == static_cast<GLenum>(BlendEquationType::Unused))
? BlendEquationType::InvalidEnum
: static_cast<BlendEquationType>(scaled);
}
if (from <= GL_EXCLUSION_KHR)
{
const GLenum scaled =
(from - GL_MULTIPLY_KHR + static_cast<uint32_t>(BlendEquationType::Multiply));
return (scaled == static_cast<GLenum>(BlendEquationType::Unused2) ||
scaled == static_cast<GLenum>(BlendEquationType::Unused3))
? BlendEquationType::InvalidEnum
: static_cast<BlendEquationType>(scaled);
}
if (from <= GL_HSL_LUMINOSITY_KHR)
{
return static_cast<BlendEquationType>(from - GL_HSL_HUE_KHR +
static_cast<uint32_t>(BlendEquationType::HslHue));
}
return BlendEquationType::InvalidEnum;
}
constexpr GLenum ToGLenum(BlendEquationType from)
{
return static_cast<GLenum>(from) + GL_FUNC_ADD;
if (from <= BlendEquationType::ReverseSubtract)
{
return static_cast<GLenum>(from) + GL_FUNC_ADD;
}
if (from <= BlendEquationType::Exclusion)
{
return static_cast<GLenum>(from) - static_cast<GLenum>(BlendEquationType::Multiply) +
GL_MULTIPLY_KHR;
}
return static_cast<GLenum>(from) - static_cast<GLenum>(BlendEquationType::HslHue) +
GL_HSL_HUE_KHR;
}
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Add, GL_FUNC_ADD);
@@ -365,6 +414,21 @@ ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Min, GL_MIN);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Max, GL_MAX);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Subtract, GL_FUNC_SUBTRACT);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, ReverseSubtract, GL_FUNC_REVERSE_SUBTRACT);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Multiply, GL_MULTIPLY_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Screen, GL_SCREEN_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Overlay, GL_OVERLAY_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Darken, GL_DARKEN_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Lighten, GL_LIGHTEN_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Colordodge, GL_COLORDODGE_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Colorburn, GL_COLORBURN_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Hardlight, GL_HARDLIGHT_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Softlight, GL_SOFTLIGHT_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Difference, GL_DIFFERENCE_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, Exclusion, GL_EXCLUSION_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, HslHue, GL_HSL_HUE_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, HslSaturation, GL_HSL_SATURATION_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, HslColor, GL_HSL_COLOR_KHR);
ANGLE_VALIDATE_PACKED_ENUM(BlendEquationType, HslLuminosity, GL_HSL_LUMINOSITY_KHR);
std::ostream &operator<<(std::ostream &os, BlendEquationType value);

View File

@@ -22,6 +22,7 @@ angle_translator_sources = [
"include/GLES3/gl3platform.h",
"include/KHR/khrplatform.h",
"include/angle_gl.h",
"src/compiler/translator/BaseTypes.cpp",
"src/compiler/translator/BaseTypes.h",
"src/compiler/translator/BuiltInFunctionEmulator.cpp",
"src/compiler/translator/BuiltInFunctionEmulator.h",

View File

@@ -0,0 +1,64 @@
//
// Copyright 2022 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
#include "BaseTypes.h"
#include "common/PackedEnums.h"
namespace sh
{
namespace
{
constexpr gl::BlendEquationBitSet kAdvancedBlendBits{
gl::BlendEquationType::Multiply, gl::BlendEquationType::Screen,
gl::BlendEquationType::Overlay, gl::BlendEquationType::Darken,
gl::BlendEquationType::Lighten, gl::BlendEquationType::Colordodge,
gl::BlendEquationType::Colorburn, gl::BlendEquationType::Hardlight,
gl::BlendEquationType::Softlight, gl::BlendEquationType::Difference,
gl::BlendEquationType::Exclusion, gl::BlendEquationType::HslHue,
gl::BlendEquationType::HslSaturation, gl::BlendEquationType::HslColor,
gl::BlendEquationType::HslLuminosity,
};
constexpr gl::BlendEquationBitSet kAdvancedBlendHslBits{
gl::BlendEquationType::HslHue,
gl::BlendEquationType::HslSaturation,
gl::BlendEquationType::HslColor,
gl::BlendEquationType::HslLuminosity,
};
bool IsValidAdvancedBlendBitSet(uint32_t enabledEquations)
{
return (gl::BlendEquationBitSet(enabledEquations) & ~kAdvancedBlendBits).none();
}
} // anonymous namespace
bool AdvancedBlendEquations::any() const
{
ASSERT(IsValidAdvancedBlendBitSet(mEnabledBlendEquations));
return mEnabledBlendEquations != 0;
}
bool AdvancedBlendEquations::anyHsl() const
{
ASSERT(IsValidAdvancedBlendBitSet(mEnabledBlendEquations));
return (gl::BlendEquationBitSet(mEnabledBlendEquations) & kAdvancedBlendHslBits).any();
}
void AdvancedBlendEquations::setAll()
{
ASSERT(IsValidAdvancedBlendBitSet(mEnabledBlendEquations));
mEnabledBlendEquations = kAdvancedBlendBits.bits();
}
void AdvancedBlendEquations::set(uint32_t blendEquation)
{
gl::BlendEquationType eq = static_cast<gl::BlendEquationType>(blendEquation);
mEnabledBlendEquations = gl::BlendEquationBitSet(mEnabledBlendEquations).set(eq).bits();
ASSERT(IsValidAdvancedBlendBitSet(mEnabledBlendEquations));
}
} // namespace sh

View File

@@ -1304,6 +1304,36 @@ enum TLayoutTessEvaluationType
EtetPointMode
};
class AdvancedBlendEquations
{
public:
// Must have a trivial default constructor since it is used in YYSTYPE.
AdvancedBlendEquations() = default;
explicit constexpr AdvancedBlendEquations(uint32_t initialState)
: mEnabledBlendEquations(initialState)
{}
bool any() const;
bool anyHsl() const;
void setAll();
void reset() { mEnabledBlendEquations = 0; }
// Parameter is gl::BlendEquationType, but PackedEnums.h include is not possible here.
void set(uint32_t blendEquation);
uint32_t bits() const { return mEnabledBlendEquations; }
AdvancedBlendEquations operator|=(AdvancedBlendEquations other)
{
mEnabledBlendEquations |= other.mEnabledBlendEquations;
return *this;
}
private:
uint32_t mEnabledBlendEquations;
};
struct TLayoutQualifier
{
// Must have a trivial default constructor since it is used in YYSTYPE.
@@ -1320,7 +1350,7 @@ struct TLayoutQualifier
invocations == 0 && maxVertices == -1 && vertices == 0 &&
tesPrimitiveType == EtetUndefined && tesVertexSpacingType == EtetUndefined &&
tesOrderingType == EtetUndefined && tesPointType == EtetUndefined && index == -1 &&
inputAttachmentIndex == -1 && noncoherent == false;
inputAttachmentIndex == -1 && noncoherent == false && !advancedBlendEquations.any();
}
bool isCombinationValid() const
@@ -1333,6 +1363,7 @@ struct TLayoutQualifier
bool otherLayoutQualifiersSpecified =
(location != -1 || binding != -1 || index != -1 || matrixPacking != EmpUnspecified ||
blockStorage != EbsUnspecified || imageInternalFormat != EiifUnspecified);
bool blendEquationSpecified = advancedBlendEquations.any();
// we can have either the work group size specified, or number of views,
// or yuv layout qualifier, or early_fragment_tests layout qualifier, or the other layout
@@ -1340,7 +1371,7 @@ struct TLayoutQualifier
return (workGroupSizeSpecified ? 1 : 0) + (numViewsSet ? 1 : 0) + (yuv ? 1 : 0) +
(earlyFragmentTests ? 1 : 0) + (otherLayoutQualifiersSpecified ? 1 : 0) +
(geometryShaderSpecified ? 1 : 0) + (subpassInputSpecified ? 1 : 0) +
(noncoherent ? 1 : 0) <=
(noncoherent ? 1 : 0) + (blendEquationSpecified ? 1 : 0) <=
1;
}
@@ -1391,6 +1422,9 @@ struct TLayoutQualifier
int inputAttachmentIndex;
bool noncoherent;
// KHR_blend_equation_advanced layout qualifiers.
AdvancedBlendEquations advancedBlendEquations;
private:
explicit constexpr TLayoutQualifier(int /*placeholder*/)
: location(-1),
@@ -1414,7 +1448,8 @@ struct TLayoutQualifier
tesPointType(EtetUndefined),
index(-1),
inputAttachmentIndex(-1),
noncoherent(false)
noncoherent(false),
advancedBlendEquations(0)
{}
};

View File

@@ -346,6 +346,7 @@ TCompiler::TCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output)
mTessEvaluationShaderInputOrderingType(EtetUndefined),
mTessEvaluationShaderInputPointType(EtetUndefined),
mHasAnyPreciseType(false),
mAdvancedBlendEquations(0),
mCompileOptions(0)
{}
@@ -560,6 +561,10 @@ void TCompiler::setASTMetadata(const TParseContext &parseContext)
mHasAnyPreciseType = parseContext.hasAnyPreciseType();
if (mShaderType == GL_FRAGMENT_SHADER)
{
mAdvancedBlendEquations = parseContext.getAdvancedBlendEquations();
}
if (mShaderType == GL_GEOMETRY_SHADER_EXT)
{
mGeometryShaderInputPrimitiveType = parseContext.getGeometryShaderInputPrimitiveType();

View File

@@ -184,6 +184,8 @@ class TCompiler : public TShHandleBase
bool hasAnyPreciseType() const { return mHasAnyPreciseType; }
AdvancedBlendEquations getAdvancedBlendEquations() const { return mAdvancedBlendEquations; }
unsigned int getSharedMemorySize() const;
sh::GLenum getShaderType() const { return mShaderType; }
@@ -339,6 +341,9 @@ class TCompiler : public TShHandleBase
bool mHasAnyPreciseType;
// advanced blend equation parameters
AdvancedBlendEquations mAdvancedBlendEquations;
// name hashing.
NameMap mNameMap;

View File

@@ -40,6 +40,7 @@
OP(EXT_texture_buffer) \
OP(EXT_texture_cube_map_array) \
OP(EXT_YUV_target) \
OP(KHR_blend_equation_advanced) \
OP(NV_EGL_stream_consumer_external) \
OP(NV_shader_framebuffer_fetch) \
OP(NV_shader_noperspective_interpolation) \

View File

@@ -46,6 +46,7 @@ enum class TExtension : uint8_t
EXT_texture_buffer,
EXT_texture_cube_map_array,
EXT_YUV_target,
KHR_blend_equation_advanced,
NV_EGL_stream_consumer_external,
NV_shader_framebuffer_fetch,
NV_shader_noperspective_interpolation,

View File

@@ -185,6 +185,10 @@ void InitExtensionBehavior(const ShBuiltInResources &resources, TExtensionBehavi
{
extBehavior[TExtension::ANDROID_extension_pack_es31a] = EBhUndefined;
}
if (resources.KHR_blend_equation_advanced)
{
extBehavior[TExtension::KHR_blend_equation_advanced] = EBhUndefined;
}
}
void ResetExtensionBehavior(const ShBuiltInResources &resources,

View File

@@ -148,6 +148,10 @@ TIntermTyped *FindLValueBase(TIntermTyped *node)
} while (true);
}
void AddAdvancedBlendEquation(gl::BlendEquationType eq, TLayoutQualifier *qualifier)
{
qualifier->advancedBlendEquations.set(static_cast<uint32_t>(eq));
}
} // namespace
// This tracks each binding point's current default offset for inheritance of subsequent
@@ -244,6 +248,7 @@ TParseContext::TParseContext(TSymbolTable &symt,
mTessEvaluationShaderInputOrderingType(EtetUndefined),
mTessEvaluationShaderInputPointType(EtetUndefined),
mHasAnyPreciseType(false),
mAdvancedBlendEquations(0),
mFunctionBodyNewScope(false),
mOutputType(outputType)
{}
@@ -1836,9 +1841,10 @@ void TParseContext::checkBindingIsValid(const TSourceLoc &identifierLocation, co
void TParseContext::checkCanUseLayoutQualifier(const TSourceLoc &location)
{
constexpr std::array<TExtension, 2u> extensions{
constexpr std::array<TExtension, 3u> extensions{
{TExtension::EXT_shader_framebuffer_fetch,
TExtension::EXT_shader_framebuffer_fetch_non_coherent}};
TExtension::EXT_shader_framebuffer_fetch_non_coherent,
TExtension::KHR_blend_equation_advanced}};
if (getShaderVersion() < 300 && !checkCanUseOneOfExtensions(location, extensions))
{
error(location, "qualifier supported in GLSL ES 3.00 and above only", "layout");
@@ -2120,6 +2126,20 @@ void TParseContext::checkInvariantVariableQualifier(bool invariant,
}
}
void TParseContext::checkAdvancedBlendEquationsNotSpecified(
const TSourceLoc &location,
const AdvancedBlendEquations &advancedBlendEquations,
const TQualifier &qualifier)
{
if (advancedBlendEquations.any() && qualifier != EvqFragmentOut)
{
error(location,
"invalid layout qualifier: blending equation qualifiers are only permitted on the "
"fragment 'out' qualifier ",
"blend_support_qualifier");
}
}
bool TParseContext::isExtensionEnabled(TExtension extension) const
{
return IsExtensionEnabled(extensionBehavior(), extension);
@@ -3626,6 +3646,9 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
checkStd430IsForShaderStorageBlock(typeQualifier.line, layoutQualifier.blockStorage,
typeQualifier.qualifier);
checkAdvancedBlendEquationsNotSpecified(
typeQualifier.line, layoutQualifier.advancedBlendEquations, typeQualifier.qualifier);
if (typeQualifier.qualifier != EvqFragmentIn)
{
checkEarlyFragmentTestsIsNotSpecified(typeQualifier.line,
@@ -3757,6 +3780,28 @@ void TParseContext::parseGlobalLayoutQualifier(const TTypeQualifierBuilder &type
mEarlyFragmentTestsSpecified = true;
}
else if (typeQualifier.qualifier == EvqFragmentOut)
{
if (mShaderVersion < 320 && !isExtensionEnabled(TExtension::KHR_blend_equation_advanced))
{
error(typeQualifier.line,
"out type qualifier without variable declaration is supported in GLSL ES 3.20,"
" or if GL_KHR_blend_equation_advanced is enabled",
"layout");
return;
}
if (!layoutQualifier.advancedBlendEquations.any())
{
error(typeQualifier.line,
"only blend equations are allowed as layout qualifier when not declaring a "
"variable",
"layout");
return;
}
mAdvancedBlendEquations |= layoutQualifier.advancedBlendEquations;
}
else if (typeQualifier.qualifier == EvqTessControlOut)
{
if (mShaderVersion < 310)
@@ -5281,15 +5326,95 @@ TLayoutQualifier TParseContext::parseLayoutQualifier(const ImmutableString &qual
error(qualifierTypeLine, "invalid layout qualifier", qualifierType);
}
}
else if (qualifierType == "noncoherent" && mShaderType == GL_FRAGMENT_SHADER)
else if (mShaderType == GL_FRAGMENT_SHADER)
{
if (checkCanUseOneOfExtensions(
qualifierTypeLine, std::array<TExtension, 2u>{
{TExtension::EXT_shader_framebuffer_fetch,
TExtension::EXT_shader_framebuffer_fetch_non_coherent}}))
if (qualifierType == "noncoherent")
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 100);
qualifier.noncoherent = true;
if (checkCanUseOneOfExtensions(
qualifierTypeLine,
std::array<TExtension, 2u>{
{TExtension::EXT_shader_framebuffer_fetch,
TExtension::EXT_shader_framebuffer_fetch_non_coherent}}))
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 100);
qualifier.noncoherent = true;
}
}
else if (qualifierType == "blend_support_multiply")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Multiply, &qualifier);
}
else if (qualifierType == "blend_support_screen")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Screen, &qualifier);
}
else if (qualifierType == "blend_support_overlay")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Overlay, &qualifier);
}
else if (qualifierType == "blend_support_darken")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Darken, &qualifier);
}
else if (qualifierType == "blend_support_lighten")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Lighten, &qualifier);
}
else if (qualifierType == "blend_support_colordodge")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Colordodge, &qualifier);
}
else if (qualifierType == "blend_support_colorburn")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Colorburn, &qualifier);
}
else if (qualifierType == "blend_support_hardlight")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Hardlight, &qualifier);
}
else if (qualifierType == "blend_support_softlight")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Softlight, &qualifier);
}
else if (qualifierType == "blend_support_difference")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Difference, &qualifier);
}
else if (qualifierType == "blend_support_exclusion")
{
AddAdvancedBlendEquation(gl::BlendEquationType::Exclusion, &qualifier);
}
else if (qualifierType == "blend_support_hsl_hue")
{
AddAdvancedBlendEquation(gl::BlendEquationType::HslHue, &qualifier);
}
else if (qualifierType == "blend_support_hsl_saturation")
{
AddAdvancedBlendEquation(gl::BlendEquationType::HslSaturation, &qualifier);
}
else if (qualifierType == "blend_support_hsl_color")
{
AddAdvancedBlendEquation(gl::BlendEquationType::HslColor, &qualifier);
}
else if (qualifierType == "blend_support_hsl_luminosity")
{
AddAdvancedBlendEquation(gl::BlendEquationType::HslLuminosity, &qualifier);
}
else if (qualifierType == "blend_support_all_equations")
{
qualifier.advancedBlendEquations.setAll();
}
else
{
error(qualifierTypeLine, "invalid layout qualifier", qualifierType);
}
if (qualifier.advancedBlendEquations.any() && mShaderVersion < 320)
{
if (!checkCanUseExtension(qualifierTypeLine, TExtension::KHR_blend_equation_advanced))
{
qualifier.advancedBlendEquations.reset();
}
}
}
else

View File

@@ -192,6 +192,12 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &qualifierLocation);
void checkLocalVariableConstStorageQualifier(const TQualifierWrapperBase &qualifier);
void checkTCSOutVarIndexIsValid(TIntermBinary *binaryExpression, const TSourceLoc &location);
void checkAdvancedBlendEquationsNotSpecified(
const TSourceLoc &location,
const AdvancedBlendEquations &advancedBlendEquations,
const TQualifier &qualifier);
const TPragma &pragma() const { return mDirectiveHandler.pragma(); }
const TExtensionBehavior &extensionBehavior() const
{
@@ -495,6 +501,7 @@ class TParseContext : angle::NonCopyable
void markShaderHasPrecise() { mHasAnyPreciseType = true; }
bool hasAnyPreciseType() const { return mHasAnyPreciseType; }
AdvancedBlendEquations getAdvancedBlendEquations() const { return mAdvancedBlendEquations; }
ShShaderOutput getOutputType() const { return mOutputType; }
@@ -735,6 +742,8 @@ class TParseContext : angle::NonCopyable
// Whether the |precise| keyword has been seen in the shader.
bool mHasAnyPreciseType;
AdvancedBlendEquations mAdvancedBlendEquations;
// Track when we add new scope for func body in ESSL 1.00 spec
bool mFunctionBodyNewScope;

View File

@@ -853,6 +853,11 @@ TLayoutQualifier JoinLayoutQualifiers(TLayoutQualifier leftQualifier,
joinedQualifier.index = rightQualifier.index;
}
if (rightQualifier.advancedBlendEquations.any())
{
joinedQualifier.advancedBlendEquations |= rightQualifier.advancedBlendEquations;
}
return joinedQualifier;
}

View File

@@ -233,6 +233,7 @@ void InitBuiltInResources(ShBuiltInResources *resources)
resources->EXT_texture_buffer = 0;
resources->OES_sample_variables = 0;
resources->EXT_clip_cull_distance = 0;
resources->KHR_blend_equation_advanced = 0;
resources->MaxClipDistances = 8;
resources->MaxCullDistances = 8;
@@ -925,6 +926,14 @@ unsigned int GetShaderSharedMemorySize(const ShHandle handle)
return sharedMemorySize;
}
uint32_t GetAdvancedBlendEquations(const ShHandle handle)
{
TCompiler *compiler = GetCompilerFromHandle(handle);
ASSERT(compiler);
return compiler->getAdvancedBlendEquations().bits();
}
void InitializeGlslang()
{
if (initializeGlslangRefCount == 0)

View File

@@ -96,7 +96,7 @@ static bool is_extension_enabled_or_is_core(TParseContext *context,
// which means in version V1, the symbol is reserved, and remains reserved until V3. From versions
// V2 until V3, it's a keyword if the extension is enabled. From version V3 on, it's a keyword in
// the spec itself. Prior to V1, the symbol can be used as identifier.
static int ES2_extension_2_ES3_keyword(TParseContext *context, TExtension extension1, TExtension extension2, int token);
static int ES2_extensions_ES3_keyword(TParseContext *context, TExtension extension1, TExtension extension2, TExtension extension3, int token);
static int ES2_reserved_ES3_keyword(TParseContext *context, int token);
static int ES2_keyword_ES3_reserved(TParseContext *context, int token);
static int ES3_keyword(TParseContext *context, int token);
@@ -247,7 +247,7 @@ O [0-7]
"samplerVideoWEBGL" { return WEBGL_video_texture_extension(context, SAMPLERVIDEOWEBGL); }
"struct" { return STRUCT; }
"layout" { return ES2_extension_2_ES3_keyword(context, TExtension::EXT_shader_framebuffer_fetch, TExtension::EXT_shader_framebuffer_fetch_non_coherent, LAYOUT); }
"layout" { return ES2_extensions_ES3_keyword(context, TExtension::EXT_shader_framebuffer_fetch, TExtension::EXT_shader_framebuffer_fetch_non_coherent, TExtension::KHR_blend_equation_advanced, LAYOUT); }
"yuvCscStandardEXT" { return ES3_extension(context, TExtension::EXT_YUV_target, YUVCSCSTANDARDEXT); }
"itu_601" { return yuvcscstandardext_constant(context); }
@@ -612,7 +612,7 @@ int WEBGL_video_texture_extension(TParseContext *context, int token)
return check_type(yyscanner);
}
int ES2_extension_2_ES3_keyword(TParseContext *context, TExtension extension1, TExtension extension2, int token)
int ES2_extensions_ES3_keyword(TParseContext *context, TExtension extension1, TExtension extension2, TExtension extension3, int token)
{
struct yyguts_t* yyg = (struct yyguts_t*) context->getScanner();
yyscan_t yyscanner = (yyscan_t) context->getScanner();
@@ -626,6 +626,10 @@ int ES2_extension_2_ES3_keyword(TParseContext *context, TExtension extension1, T
{
return token;
}
else if (is_extension_enabled_or_is_core(context, 100, extension3, 300))
{
return token;
}
// not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name
yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);

View File

@@ -918,10 +918,11 @@ static bool is_extension_enabled_or_is_core(TParseContext *context,
// which means in version V1, the symbol is reserved, and remains reserved until V3. From versions
// V2 until V3, it's a keyword if the extension is enabled. From version V3 on, it's a keyword in
// the spec itself. Prior to V1, the symbol can be used as identifier.
static int ES2_extension_2_ES3_keyword(TParseContext *context,
TExtension extension1,
TExtension extension2,
int token);
static int ES2_extensions_ES3_keyword(TParseContext *context,
TExtension extension1,
TExtension extension2,
TExtension extension3,
int token);
static int ES2_reserved_ES3_keyword(TParseContext *context, int token);
static int ES2_keyword_ES3_reserved(TParseContext *context, int token);
static int ES3_keyword(TParseContext *context, int token);
@@ -1689,9 +1690,10 @@ YY_DECL
case 97:
YY_RULE_SETUP
{
return ES2_extension_2_ES3_keyword(
return ES2_extensions_ES3_keyword(
context, TExtension::EXT_shader_framebuffer_fetch,
TExtension::EXT_shader_framebuffer_fetch_non_coherent, LAYOUT);
TExtension::EXT_shader_framebuffer_fetch_non_coherent,
TExtension::KHR_blend_equation_advanced, LAYOUT);
}
YY_BREAK
case 98:
@@ -3458,10 +3460,11 @@ int WEBGL_video_texture_extension(TParseContext *context, int token)
return check_type(yyscanner);
}
int ES2_extension_2_ES3_keyword(TParseContext *context,
TExtension extension1,
TExtension extension2,
int token)
int ES2_extensions_ES3_keyword(TParseContext *context,
TExtension extension1,
TExtension extension2,
TExtension extension3,
int token)
{
struct yyguts_t *yyg = (struct yyguts_t *)context->getScanner();
yyscan_t yyscanner = (yyscan_t)context->getScanner();
@@ -3475,6 +3478,10 @@ int ES2_extension_2_ES3_keyword(TParseContext *context,
{
return token;
}
else if (is_extension_enabled_or_is_core(context, 100, extension3, 300))
{
return token;
}
// not a reserved word in GLSL ES 1.00, so could be used as an identifier/type name
yylval->lex.string = AllocatePoolCharArray(yytext, yyleng);

View File

@@ -50,7 +50,7 @@ TEST(BlendStateExt, Init)
ASSERT_EQ(blendStateExt.mMaxDrawBuffers, 1u);
ASSERT_EQ(blendStateExt.mMaxEnabledMask.to_ulong(), 1u);
ASSERT_EQ(blendStateExt.mMaxColorMask, is64Bit ? 0xFFu : 0xFu);
ASSERT_EQ(blendStateExt.mMaxEquationMask, is64Bit ? 0xFFu : 0xFu);
ASSERT_EQ(blendStateExt.mMaxEquationMask, 0xFFu);
ASSERT_EQ(blendStateExt.mMaxFactorMask, 0xFFu);
checkInitState(blendStateExt);
}
@@ -60,7 +60,7 @@ TEST(BlendStateExt, Init)
ASSERT_EQ(blendStateExt.mMaxDrawBuffers, 4u);
ASSERT_EQ(blendStateExt.mMaxEnabledMask.to_ulong(), 0xFu);
ASSERT_EQ(blendStateExt.mMaxColorMask, is64Bit ? 0xFFFFFFFFu : 0xFFFFu);
ASSERT_EQ(blendStateExt.mMaxEquationMask, is64Bit ? 0xFFFFFFFFu : 0xFFFFu);
ASSERT_EQ(blendStateExt.mMaxEquationMask, 0xFFFFFFFFu);
ASSERT_EQ(blendStateExt.mMaxFactorMask, 0xFFFFFFFFu);
checkInitState(blendStateExt);
}
@@ -70,7 +70,7 @@ TEST(BlendStateExt, Init)
ASSERT_EQ(blendStateExt.mMaxDrawBuffers, 8u);
ASSERT_EQ(blendStateExt.mMaxEnabledMask.to_ulong(), 0xFFu);
ASSERT_EQ(blendStateExt.mMaxColorMask, is64Bit ? 0xFFFFFFFFFFFFFFFFu : 0xFFFFFFFFu);
ASSERT_EQ(blendStateExt.mMaxEquationMask, is64Bit ? 0xFFFFFFFFFFFFFFFFu : 0xFFFFFFFFu);
ASSERT_EQ(blendStateExt.mMaxEquationMask, 0xFFFFFFFFFFFFFFFFu);
ASSERT_EQ(blendStateExt.mMaxFactorMask, 0xFFFFFFFFFFFFFFFFu);
checkInitState(blendStateExt);
}
@@ -126,13 +126,13 @@ TEST(BlendStateExt, BlendEquations)
gl::BlendStateExt blendStateExt = gl::BlendStateExt(7);
blendStateExt.setEquations(GL_MIN, GL_FUNC_SUBTRACT);
ASSERT_EQ(blendStateExt.mEquationColor, is64Bit ? 0x01010101010101u : 0x1111111u);
ASSERT_EQ(blendStateExt.mEquationAlpha, is64Bit ? 0x04040404040404u : 0x4444444u);
ASSERT_EQ(blendStateExt.mEquationColor, 0x01010101010101u);
ASSERT_EQ(blendStateExt.mEquationAlpha, 0x04040404040404u);
blendStateExt.setEquationsIndexed(3, GL_MAX, GL_FUNC_SUBTRACT);
blendStateExt.setEquationsIndexed(5, GL_MIN, GL_FUNC_ADD);
ASSERT_EQ(blendStateExt.mEquationColor, is64Bit ? 0x01010102010101u : 0x1112111u);
ASSERT_EQ(blendStateExt.mEquationAlpha, is64Bit ? 0x04000404040404u : 0x4044444u);
ASSERT_EQ(blendStateExt.mEquationColor, 0x01010102010101u);
ASSERT_EQ(blendStateExt.mEquationAlpha, 0x04000404040404u);
ASSERT_EQ(blendStateExt.getEquationColorIndexed(3), static_cast<GLenum>(GL_MAX));
ASSERT_EQ(blendStateExt.getEquationAlphaIndexed(5), static_cast<GLenum>(GL_FUNC_ADD));
@@ -141,8 +141,8 @@ TEST(BlendStateExt, BlendEquations)
gl::BlendStateExt::EquationStorage::Type otherEquationAlpha =
blendStateExt.expandEquationAlphaIndexed(0);
ASSERT_EQ(otherEquationColor, is64Bit ? 0x01010101010101u : 0x1111111u);
ASSERT_EQ(otherEquationAlpha, is64Bit ? 0x04040404040404u : 0x4444444u);
ASSERT_EQ(otherEquationColor, 0x01010101010101u);
ASSERT_EQ(otherEquationAlpha, 0x04040404040404u);
const gl::DrawBufferMask diff =
blendStateExt.compareEquations(otherEquationColor, otherEquationAlpha);

View File

@@ -108,6 +108,7 @@ angle_unittests_compiler_tests_sources = [
"compiler_tests/ImmutableString_test.cpp",
"compiler_tests/InitOutputVariables_test.cpp",
"compiler_tests/IntermNode_test.cpp",
"compiler_tests/KHR_blend_equation_advanced_test.cpp",
"compiler_tests/NV_draw_buffers_test.cpp",
"compiler_tests/OES_sample_variables_test.cpp",
"compiler_tests/OES_standard_derivatives_test.cpp",

View File

@@ -0,0 +1,322 @@
//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// KHR_blend_equation_advanced_test.cpp:
// Test for KHR_blend_equation_advanced and KHR_blend_equation_advanced_coherent
//
#include "tests/test_utils/ShaderExtensionTest.h"
#include "common/PackedEnums.h"
namespace
{
const char EXTPragma[] =
"#extension GL_KHR_blend_equation_advanced : require\n"
"#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require\n";
// Use the multiply equation for blending
const char ESSL310_Simple[] =
R"(
precision highp float;
layout (blend_support_multiply) out;
layout (location = 0) out vec4 oCol;
uniform vec4 uSrcCol;
void main (void)
{
oCol = uSrcCol;
})";
const char ESSL310_DeclaredMultiplyScreenSeparately[] =
R"(
precision highp float;
layout (blend_support_multiply) out;
layout (blend_support_screen) out;
layout (location = 0) out vec4 oCol;
uniform vec4 uSrcCol;
void main (void)
{
oCol = uSrcCol;
})";
const char ESSL310_DeclaredMultiplyScreenSuccessively[] =
R"(
precision highp float;
layout (blend_support_multiply, blend_support_screen) out;
layout (location = 0) out vec4 oCol;
uniform vec4 uSrcCol;
void main (void)
{
oCol = uSrcCol;
})";
const char ESSL310_With_FramebufferFetch[] =
R"(
precision highp float;
layout (blend_support_multiply) out;
layout (location = 0, noncoherent) inout vec4 oCol;
uniform vec4 uSrcCol;
void main (void)
{
oCol = mix(oCol, uSrcCol, 0.5f);
})";
class KHRBlendEquationAdvancedTest : public sh::ShaderExtensionTest
{
public:
void SetUp() override
{
std::map<ShShaderOutput, std::string> shaderOutputList = {
{SH_GLSL_450_CORE_OUTPUT, "SH_GLSL_450_CORE_OUTPUT"},
#if defined(ANGLE_ENABLE_VULKAN)
{SH_SPIRV_VULKAN_OUTPUT, "SH_SPIRV_VULKAN_OUTPUT"}
#endif
};
Initialize(shaderOutputList);
}
void TearDown() override
{
for (auto shaderOutputType : mShaderOutputList)
{
DestroyCompiler(shaderOutputType.first);
}
}
void Initialize(std::map<ShShaderOutput, std::string> &shaderOutputList)
{
mShaderOutputList = std::move(shaderOutputList);
for (auto shaderOutputType : mShaderOutputList)
{
sh::InitBuiltInResources(&mResourceList[shaderOutputType.first]);
mCompilerList[shaderOutputType.first] = nullptr;
}
}
void DestroyCompiler(ShShaderOutput shaderOutputType)
{
if (mCompilerList[shaderOutputType])
{
sh::Destruct(mCompilerList[shaderOutputType]);
mCompilerList[shaderOutputType] = nullptr;
}
}
void InitializeCompiler()
{
for (auto shaderOutputType : mShaderOutputList)
{
InitializeCompiler(shaderOutputType.first);
}
}
void InitializeCompiler(ShShaderOutput shaderOutputType)
{
DestroyCompiler(shaderOutputType);
mCompilerList[shaderOutputType] =
sh::ConstructCompiler(GL_FRAGMENT_SHADER, testing::get<0>(GetParam()), shaderOutputType,
&mResourceList[shaderOutputType]);
ASSERT_TRUE(mCompilerList[shaderOutputType] != nullptr)
<< "Compiler for " << mShaderOutputList[shaderOutputType]
<< " could not be constructed.";
}
testing::AssertionResult TestShaderCompile(ShShaderOutput shaderOutputType, const char *pragma)
{
const char *shaderStrings[] = {testing::get<1>(GetParam()), pragma,
testing::get<2>(GetParam())};
bool success = sh::Compile(mCompilerList[shaderOutputType], shaderStrings, 3,
SH_VARIABLES | SH_OBJECT_CODE);
if (success)
{
return ::testing::AssertionSuccess()
<< "Compilation success(" << mShaderOutputList[shaderOutputType] << ")";
}
return ::testing::AssertionFailure() << sh::GetInfoLog(mCompilerList[shaderOutputType]);
}
void TestShaderCompile(bool expectation, const char *pragma)
{
for (auto shaderOutputType : mShaderOutputList)
{
if (expectation)
{
EXPECT_TRUE(TestShaderCompile(shaderOutputType.first, pragma));
}
else
{
EXPECT_FALSE(TestShaderCompile(shaderOutputType.first, pragma));
}
}
}
void SetExtensionEnable(bool enable)
{
for (auto shaderOutputType : mShaderOutputList)
{
mResourceList[shaderOutputType.first].KHR_blend_equation_advanced = enable;
mResourceList[shaderOutputType.first].EXT_shader_framebuffer_fetch_non_coherent =
enable;
}
}
protected:
std::map<ShShaderOutput, std::string> mShaderOutputList;
std::map<ShShaderOutput, ShHandle> mCompilerList;
std::map<ShShaderOutput, ShBuiltInResources> mResourceList;
};
class KHRBlendEquationAdvancedES310Test : public KHRBlendEquationAdvancedTest
{};
// Extension flag is required to compile properly. Expect failure when it is
// not present.
TEST_P(KHRBlendEquationAdvancedES310Test, CompileFailsWithoutExtension)
{
SetExtensionEnable(false);
InitializeCompiler();
TestShaderCompile(false, EXTPragma);
}
// Extension directive is required to compile properly. Expect failure when
// it is not present.
TEST_P(KHRBlendEquationAdvancedES310Test, CompileFailsWithExtensionWithoutPragma)
{
SetExtensionEnable(true);
InitializeCompiler();
TestShaderCompile(false, "");
}
INSTANTIATE_TEST_SUITE_P(CorrectESSL310Shaders,
KHRBlendEquationAdvancedES310Test,
Combine(Values(SH_GLES3_1_SPEC),
Values(sh::ESSLVersion310),
Values(ESSL310_Simple,
ESSL310_With_FramebufferFetch,
ESSL310_DeclaredMultiplyScreenSeparately,
ESSL310_DeclaredMultiplyScreenSuccessively)));
#if defined(ANGLE_ENABLE_VULKAN)
class KHRBlendEquationAdvancedSuccessTest : public KHRBlendEquationAdvancedTest
{
public:
void SetUp() override
{
std::map<ShShaderOutput, std::string> shaderOutputList = {
{SH_SPIRV_VULKAN_OUTPUT, "SH_SPIRV_VULKAN_OUTPUT"}};
Initialize(shaderOutputList);
}
};
class KHRBlendEquationAdvancedES310SuccessTest : public KHRBlendEquationAdvancedSuccessTest
{};
// With extension flag and extension directive, compiling succeeds.
// Also test that the extension directive state is reset correctly.
TEST_P(KHRBlendEquationAdvancedES310SuccessTest, CompileSucceedsWithExtensionAndPragma)
{
SetExtensionEnable(true);
InitializeCompiler();
TestShaderCompile(true, EXTPragma);
// Test reset functionality.
TestShaderCompile(false, "");
TestShaderCompile(true, EXTPragma);
}
// The SL #version 100 shaders that are correct work similarly
// in both GL2 and GL3, with and without the version string.
INSTANTIATE_TEST_SUITE_P(CorrectESSL310Shaders,
KHRBlendEquationAdvancedES310SuccessTest,
Combine(Values(SH_GLES3_1_SPEC),
Values(sh::ESSLVersion310),
Values(ESSL310_Simple,
ESSL310_With_FramebufferFetch,
ESSL310_DeclaredMultiplyScreenSeparately,
ESSL310_DeclaredMultiplyScreenSuccessively)));
class KHRBlendEquationAdvancedEnabledListCheckTest : public KHRBlendEquationAdvancedTest
{
public:
void SetUp() override
{
std::map<ShShaderOutput, std::string> shaderOutputList = {
{SH_SPIRV_VULKAN_OUTPUT, "SH_SPIRV_VULKAN_OUTPUT"}};
Initialize(shaderOutputList);
}
const ShHandle &GetCompilerHandle(const ShShaderOutput outputType) const
{
return mCompilerList.at(outputType);
}
};
class KHRBlendEquationAdvancedEnabledSeparatelyTest
: public KHRBlendEquationAdvancedEnabledListCheckTest
{};
// Test for declaring different blend equations in separate layout declarations
TEST_P(KHRBlendEquationAdvancedEnabledSeparatelyTest, DeclaredEquationSeparately)
{
SetExtensionEnable(true);
InitializeCompiler();
TestShaderCompile(true, EXTPragma);
const ShHandle compilerHandle = GetCompilerHandle(SH_SPIRV_VULKAN_OUTPUT);
gl::BlendEquationBitSet enabledBlendEquation(sh::GetAdvancedBlendEquations(compilerHandle));
EXPECT_TRUE(enabledBlendEquation.test(gl::BlendEquationType::Multiply));
EXPECT_TRUE(enabledBlendEquation.test(gl::BlendEquationType::Screen));
}
INSTANTIATE_TEST_SUITE_P(CorrectESSL310Shaders,
KHRBlendEquationAdvancedEnabledSeparatelyTest,
Combine(Values(SH_GLES3_1_SPEC),
Values(sh::ESSLVersion310),
Values(ESSL310_DeclaredMultiplyScreenSeparately)));
class KHRBlendEquationAdvancedEnabledSuccessivelyTest
: public KHRBlendEquationAdvancedEnabledListCheckTest
{};
// Test for declaring different blend equations in the same layout declaration
TEST_P(KHRBlendEquationAdvancedEnabledSuccessivelyTest, DeclaredEquationSuccessively)
{
SetExtensionEnable(true);
InitializeCompiler();
TestShaderCompile(true, EXTPragma);
const ShHandle compilerHandle = GetCompilerHandle(SH_SPIRV_VULKAN_OUTPUT);
gl::BlendEquationBitSet enabledBlendEquation(sh::GetAdvancedBlendEquations(compilerHandle));
EXPECT_TRUE(enabledBlendEquation.test(gl::BlendEquationType::Multiply));
EXPECT_TRUE(enabledBlendEquation.test(gl::BlendEquationType::Screen));
}
INSTANTIATE_TEST_SUITE_P(CorrectESSL310Shaders,
KHRBlendEquationAdvancedEnabledSuccessivelyTest,
Combine(Values(SH_GLES3_1_SPEC),
Values(sh::ESSLVersion310),
Values(ESSL310_DeclaredMultiplyScreenSuccessively)));
#endif
} // anonymous namespace

0
src/tests/gl_tests/ProgramPipelineTest.cpp Executable file → Normal file
View File