Commit 6cc845bb by Brandon Schade Committed by Commit Bot

Vulkan: Add support for EXT_blend_func_extended

This implementation utilizes vulkan's dualSrcBlend feature. Expose this extension if the underlying vulkan backend allows the use of this feature. Test: angle_end2end_tests --gtest_filter=EXTBlendFuncExtendedDrawTest* Bug: angleproject:5074 Change-Id: I7d2f611df89d65e5cac35158cb5f41a0ebd58aae Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2593151 Commit-Queue: Brandon Schade <b.schade@samsung.com> Commit-Queue: Mohan Maiya <m.maiya@samsung.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org>
parent 926d1cea
...@@ -1005,10 +1005,12 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root, ...@@ -1005,10 +1005,12 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
} }
} }
bool hasGLFragColor = false; bool hasGLFragColor = false;
bool hasGLFragData = false; bool hasGLFragData = false;
bool usePreRotation = (compileOptions & SH_ADD_PRE_ROTATION) != 0; bool usePreRotation = (compileOptions & SH_ADD_PRE_ROTATION) != 0;
bool hasGLSampleMask = false; bool hasGLSampleMask = false;
bool hasGLSecondaryFragColor = false;
bool hasGLSecondaryFragData = false;
for (const ShaderVariable &outputVar : mOutputVariables) for (const ShaderVariable &outputVar : mOutputVariables)
{ {
...@@ -1030,11 +1032,24 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root, ...@@ -1030,11 +1032,24 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
hasGLSampleMask = true; hasGLSampleMask = true;
continue; continue;
} }
else if (outputVar.name == "gl_SecondaryFragColorEXT")
{
ASSERT(!hasGLSecondaryFragColor);
hasGLSecondaryFragColor = true;
continue;
}
else if (outputVar.name == "gl_SecondaryFragDataEXT")
{
ASSERT(!hasGLSecondaryFragData);
hasGLSecondaryFragData = true;
continue;
}
} }
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData // Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
// if it's core profile shaders and they are used. // if it's core profile shaders and they are used.
ASSERT(!(hasGLFragColor && hasGLFragData)); ASSERT(!((hasGLFragColor || hasGLSecondaryFragColor) &&
(hasGLFragData || hasGLSecondaryFragData)));
if (hasGLFragColor) if (hasGLFragColor)
{ {
sink << "layout(location = 0) out vec4 webgl_FragColor;\n"; sink << "layout(location = 0) out vec4 webgl_FragColor;\n";
...@@ -1043,6 +1058,15 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root, ...@@ -1043,6 +1058,15 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
{ {
sink << "layout(location = 0) out vec4 webgl_FragData[gl_MaxDrawBuffers];\n"; sink << "layout(location = 0) out vec4 webgl_FragData[gl_MaxDrawBuffers];\n";
} }
if (hasGLSecondaryFragColor)
{
sink << "layout(location = 0, index = 1) out vec4 angle_SecondaryFragColor;\n";
}
if (hasGLSecondaryFragData)
{
sink << "layout(location = 0, index = 1) out vec4 angle_SecondaryFragData["
<< getResources().MaxDualSourceDrawBuffers << "];\n";
}
if (usesPointCoord) if (usesPointCoord)
{ {
......
...@@ -108,6 +108,12 @@ void LoadBlockMemberInfo(BinaryInputStream *stream, sh::BlockMemberInfo *var); ...@@ -108,6 +108,12 @@ void LoadBlockMemberInfo(BinaryInputStream *stream, sh::BlockMemberInfo *var);
void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var); void WriteShaderVar(BinaryOutputStream *stream, const sh::ShaderVariable &var);
void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var); void LoadShaderVar(BinaryInputStream *stream, sh::ShaderVariable *var);
void WriteInterfaceBlock(BinaryOutputStream *stream, const InterfaceBlock &block);
void LoadInterfaceBlock(BinaryInputStream *stream, InterfaceBlock *block);
void WriteShaderVariableBuffer(BinaryOutputStream *stream, const ShaderVariableBuffer &var);
void LoadShaderVariableBuffer(BinaryInputStream *stream, ShaderVariableBuffer *var);
// Struct used for correlating uniforms/elements of uniform arrays to handles // Struct used for correlating uniforms/elements of uniform arrays to handles
struct VariableLocation struct VariableLocation
{ {
...@@ -233,7 +239,7 @@ class ProgramState final : angle::NonCopyable ...@@ -233,7 +239,7 @@ class ProgramState final : angle::NonCopyable
} }
const UniformBlockBindingMask &getActiveUniformBlockBindingsMask() const const UniformBlockBindingMask &getActiveUniformBlockBindingsMask() const
{ {
return mActiveUniformBlockBindings; return mExecutable->getActiveUniformBlockBindings();
} }
const std::vector<sh::ShaderVariable> &getProgramInputs() const const std::vector<sh::ShaderVariable> &getProgramInputs() const
{ {
...@@ -250,7 +256,7 @@ class ProgramState final : angle::NonCopyable ...@@ -250,7 +256,7 @@ class ProgramState final : angle::NonCopyable
} }
const std::vector<VariableLocation> &getSecondaryOutputLocations() const const std::vector<VariableLocation> &getSecondaryOutputLocations() const
{ {
return mSecondaryOutputLocations; return mExecutable->getSecondaryOutputLocations();
} }
const std::vector<LinkedUniform> &getUniforms() const { return mExecutable->getUniforms(); } const std::vector<LinkedUniform> &getUniforms() const { return mExecutable->getUniforms(); }
const std::vector<VariableLocation> &getUniformLocations() const { return mUniformLocations; } const std::vector<VariableLocation> &getUniformLocations() const { return mUniformLocations; }
...@@ -386,16 +392,10 @@ class ProgramState final : angle::NonCopyable ...@@ -386,16 +392,10 @@ class ProgramState final : angle::NonCopyable
uint32_t mLocationsUsedForXfbExtension; uint32_t mLocationsUsedForXfbExtension;
std::vector<std::string> mTransformFeedbackVaryingNames; std::vector<std::string> mTransformFeedbackVaryingNames;
// For faster iteration on the blocks currently being bound.
UniformBlockBindingMask mActiveUniformBlockBindings;
std::vector<VariableLocation> mUniformLocations; std::vector<VariableLocation> mUniformLocations;
std::vector<BufferVariable> mBufferVariables; std::vector<BufferVariable> mBufferVariables;
RangeUI mAtomicCounterUniformRange; RangeUI mAtomicCounterUniformRange;
// EXT_blend_func_extended secondary outputs (ones with index 1) in ESSL 3.00 shaders.
std::vector<VariableLocation> mSecondaryOutputLocations;
DrawBufferMask mActiveOutputVariables; DrawBufferMask mActiveOutputVariables;
// Fragment output variable base types: FLOAT, INT, or UINT. Ordered by location. // Fragment output variable base types: FLOAT, INT, or UINT. Ordered by location.
......
...@@ -233,8 +233,16 @@ class ProgramExecutable final : public angle::Subject ...@@ -233,8 +233,16 @@ class ProgramExecutable final : public angle::Subject
const std::vector<sh::ShaderVariable> &getProgramInputs() const { return mProgramInputs; } const std::vector<sh::ShaderVariable> &getProgramInputs() const { return mProgramInputs; }
const std::vector<sh::ShaderVariable> &getOutputVariables() const { return mOutputVariables; } const std::vector<sh::ShaderVariable> &getOutputVariables() const { return mOutputVariables; }
const std::vector<VariableLocation> &getOutputLocations() const { return mOutputLocations; } const std::vector<VariableLocation> &getOutputLocations() const { return mOutputLocations; }
const std::vector<VariableLocation> &getSecondaryOutputLocations() const
{
return mSecondaryOutputLocations;
}
const std::vector<LinkedUniform> &getUniforms() const { return mUniforms; } const std::vector<LinkedUniform> &getUniforms() const { return mUniforms; }
const std::vector<InterfaceBlock> &getUniformBlocks() const { return mUniformBlocks; } const std::vector<InterfaceBlock> &getUniformBlocks() const { return mUniformBlocks; }
const UniformBlockBindingMask &getActiveUniformBlockBindings() const
{
return mActiveUniformBlockBindings;
}
const std::vector<SamplerBinding> &getSamplerBindings() const { return mSamplerBindings; } const std::vector<SamplerBinding> &getSamplerBindings() const { return mSamplerBindings; }
const std::vector<ImageBinding> &getImageBindings() const const std::vector<ImageBinding> &getImageBindings() const
{ {
...@@ -392,6 +400,8 @@ class ProgramExecutable final : public angle::Subject ...@@ -392,6 +400,8 @@ class ProgramExecutable final : public angle::Subject
// to uniforms. // to uniforms.
std::vector<sh::ShaderVariable> mOutputVariables; std::vector<sh::ShaderVariable> mOutputVariables;
std::vector<VariableLocation> mOutputLocations; std::vector<VariableLocation> mOutputLocations;
// EXT_blend_func_extended secondary outputs (ones with index 1)
std::vector<VariableLocation> mSecondaryOutputLocations;
bool mYUVOutput; bool mYUVOutput;
// Vertex attributes, Fragment input varyings, etc. // Vertex attributes, Fragment input varyings, etc.
std::vector<sh::ShaderVariable> mProgramInputs; std::vector<sh::ShaderVariable> mProgramInputs;
...@@ -414,6 +424,10 @@ class ProgramExecutable final : public angle::Subject ...@@ -414,6 +424,10 @@ class ProgramExecutable final : public angle::Subject
RangeUI mDefaultUniformRange; RangeUI mDefaultUniformRange;
RangeUI mSamplerUniformRange; RangeUI mSamplerUniformRange;
std::vector<InterfaceBlock> mUniformBlocks; std::vector<InterfaceBlock> mUniformBlocks;
// For faster iteration on the blocks currently being bound.
UniformBlockBindingMask mActiveUniformBlockBindings;
std::vector<AtomicCounterBuffer> mAtomicCounterBuffers; std::vector<AtomicCounterBuffer> mAtomicCounterBuffers;
RangeUI mImageUniformRange; RangeUI mImageUniformRange;
std::vector<InterfaceBlock> mComputeShaderStorageBlocks; std::vector<InterfaceBlock> mComputeShaderStorageBlocks;
......
...@@ -539,13 +539,70 @@ void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable, ...@@ -539,13 +539,70 @@ void AssignAttributeLocations(const gl::ProgramExecutable &programExecutable,
} }
} }
void AssignOutputLocations(const gl::ProgramExecutable &programExecutable, void AssignSecondaryOutputLocations(const gl::ProgramState &programState,
ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{
const auto &secondaryOutputLocations =
programState.getExecutable().getSecondaryOutputLocations();
const auto &outputVariables = programState.getExecutable().getOutputVariables();
// Handle EXT_blend_func_extended secondary outputs (ones with index=1)
for (const gl::VariableLocation &outputLocation : secondaryOutputLocations)
{
if (outputLocation.arrayIndex == 0 && outputLocation.used() && !outputLocation.ignored)
{
const sh::ShaderVariable &outputVar = outputVariables[outputLocation.index];
uint32_t location = 0;
if (outputVar.location != -1)
{
location = outputVar.location;
}
ShaderInterfaceVariableInfo *info =
AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment, outputVar.mappedName,
location, ShaderInterfaceVariableInfo::kInvalid, 0, 0);
// If the shader source has not specified the index, specify it here.
if (outputVar.index == -1)
{
// Index 1 is used to specify that the color be used as the second color input to
// the blend equation
info->index = 1;
}
}
}
// Handle secondary outputs for ESSL version less than 3.00
gl::Shader *fragmentShader = programState.getAttachedShader(gl::ShaderType::Fragment);
if (fragmentShader && fragmentShader->getShaderVersion() == 100)
{
const auto &shaderOutputs = fragmentShader->getActiveOutputVariables();
for (const auto &outputVar : shaderOutputs)
{
if (outputVar.name == "gl_SecondaryFragColorEXT")
{
AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment,
"angle_SecondaryFragColor", 0,
ShaderInterfaceVariableInfo::kInvalid, 0, 0);
}
else if (outputVar.name == "gl_SecondaryFragDataEXT")
{
AddLocationInfo(variableInfoMapOut, gl::ShaderType::Fragment,
"angle_SecondaryFragData", 0, ShaderInterfaceVariableInfo::kInvalid,
0, 0);
}
}
}
}
void AssignOutputLocations(const gl::ProgramState &programState,
const gl::ShaderType shaderType, const gl::ShaderType shaderType,
ShaderInterfaceVariableInfoMap *variableInfoMapOut) ShaderInterfaceVariableInfoMap *variableInfoMapOut)
{ {
// Assign output locations for the fragment shader. // Assign output locations for the fragment shader.
ASSERT(shaderType == gl::ShaderType::Fragment); ASSERT(shaderType == gl::ShaderType::Fragment);
// TODO(syoussefi): Add support for EXT_blend_func_extended. http://anglebug.com/3385
const gl::ProgramExecutable &programExecutable = programState.getExecutable();
const auto &outputLocations = programExecutable.getOutputLocations(); const auto &outputLocations = programExecutable.getOutputLocations();
const auto &outputVariables = programExecutable.getOutputVariables(); const auto &outputVariables = programExecutable.getOutputVariables();
const std::array<std::string, 3> implicitOutputs = {"gl_FragDepth", "gl_SampleMask", const std::array<std::string, 3> implicitOutputs = {"gl_FragDepth", "gl_SampleMask",
...@@ -576,6 +633,8 @@ void AssignOutputLocations(const gl::ProgramExecutable &programExecutable, ...@@ -576,6 +633,8 @@ void AssignOutputLocations(const gl::ProgramExecutable &programExecutable,
} }
} }
AssignSecondaryOutputLocations(programState, variableInfoMapOut);
// When no fragment output is specified by the shader, the translator outputs webgl_FragColor or // When no fragment output is specified by the shader, the translator outputs webgl_FragColor or
// webgl_FragData. Add an entry for these. Even though the translator is already assigning // webgl_FragData. Add an entry for these. Even though the translator is already assigning
// location 0 to these entries, adding an entry for them here allows us to ASSERT that every // location 0 to these entries, adding an entry for them here allows us to ASSERT that every
...@@ -2223,6 +2282,13 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction) ...@@ -2223,6 +2282,13 @@ bool SpirvTransformer::transformDecorate(const uint32_t *instruction)
{spirv::LiteralInteger(info->component)}); {spirv::LiteralInteger(info->component)});
} }
// Add index decoration, if any.
if (info->index != ShaderInterfaceVariableInfo::kInvalid)
{
spirv::WriteDecorate(mSpirvBlobOut, id, spv::DecorationIndex,
{spirv::LiteralInteger(info->index)});
}
// Add Xfb decorations, if any. // Add Xfb decorations, if any.
if (mOptions.isTransformFeedbackStage && if (mOptions.isTransformFeedbackStage &&
info->xfb.buffer != ShaderInterfaceVariableXfbInfo::kInvalid) info->xfb.buffer != ShaderInterfaceVariableXfbInfo::kInvalid)
...@@ -3743,7 +3809,7 @@ void GlslangAssignLocations(const GlslangSourceOptions &options, ...@@ -3743,7 +3809,7 @@ void GlslangAssignLocations(const GlslangSourceOptions &options,
if ((shaderType == gl::ShaderType::Fragment) && if ((shaderType == gl::ShaderType::Fragment) &&
programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment)) programExecutable.hasLinkedShaderStage(gl::ShaderType::Fragment))
{ {
AssignOutputLocations(programExecutable, gl::ShaderType::Fragment, variableInfoMapOut); AssignOutputLocations(programState, gl::ShaderType::Fragment, variableInfoMapOut);
} }
// Assign attributes to the vertex shader, if any. // Assign attributes to the vertex shader, if any.
......
...@@ -97,6 +97,7 @@ struct ShaderInterfaceVariableInfo ...@@ -97,6 +97,7 @@ struct ShaderInterfaceVariableInfo
// locations in their respective slots. // locations in their respective slots.
uint32_t location = kInvalid; uint32_t location = kInvalid;
uint32_t component = kInvalid; uint32_t component = kInvalid;
uint32_t index = kInvalid;
// The stages this shader interface variable is active. // The stages this shader interface variable is active.
gl::ShaderBitSet activeStages; gl::ShaderBitSet activeStages;
// Used for transform feedback extension to decorate vertex shader output. // Used for transform feedback extension to decorate vertex shader output.
......
...@@ -1545,6 +1545,8 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF ...@@ -1545,6 +1545,8 @@ angle::Result RendererVk::initializeDevice(DisplayVk *displayVk, uint32_t queueF
enabledFeatures.features.shaderCullDistance = mPhysicalDeviceFeatures.shaderCullDistance; enabledFeatures.features.shaderCullDistance = mPhysicalDeviceFeatures.shaderCullDistance;
// Used to support tessellation Shader: // Used to support tessellation Shader:
enabledFeatures.features.tessellationShader = mPhysicalDeviceFeatures.tessellationShader; enabledFeatures.features.tessellationShader = mPhysicalDeviceFeatures.tessellationShader;
// Used to support EXT_blend_func_extended
enabledFeatures.features.dualSrcBlend = mPhysicalDeviceFeatures.dualSrcBlend;
if (!vk::CommandBuffer::ExecutesInline()) if (!vk::CommandBuffer::ExecutesInline())
{ {
......
...@@ -90,6 +90,14 @@ uint8_t PackGLBlendFactor(GLenum blendFactor) ...@@ -90,6 +90,14 @@ uint8_t PackGLBlendFactor(GLenum blendFactor)
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR); return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR);
case GL_ONE_MINUS_CONSTANT_ALPHA: case GL_ONE_MINUS_CONSTANT_ALPHA:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA); return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA);
case GL_SRC1_COLOR_EXT:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC1_COLOR);
case GL_SRC1_ALPHA_EXT:
return static_cast<uint8_t>(VK_BLEND_FACTOR_SRC1_ALPHA);
case GL_ONE_MINUS_SRC1_COLOR_EXT:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR);
case GL_ONE_MINUS_SRC1_ALPHA_EXT:
return static_cast<uint8_t>(VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA);
default: default:
UNREACHABLE(); UNREACHABLE();
return 0; return 0;
......
...@@ -997,6 +997,10 @@ void RendererVk::ensureCapsInitialized() const ...@@ -997,6 +997,10 @@ void RendererVk::ensureCapsInitialized() const
mNativeCaps.maxCombinedClipAndCullDistances = limitsVk.maxCombinedClipAndCullDistances; mNativeCaps.maxCombinedClipAndCullDistances = limitsVk.maxCombinedClipAndCullDistances;
} }
} }
// GL_EXT_blend_func_extended
mNativeExtensions.blendFuncExtended = (mPhysicalDeviceFeatures.dualSrcBlend == VK_TRUE);
mNativeExtensions.maxDualSourceDrawBuffers = LimitToInt(limitsVk.maxFragmentDualSrcAttachments);
} }
namespace vk namespace vk
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
// Test EXT_blend_func_extended // Test EXT_blend_func_extended
#include "test_utils/ANGLETest.h" #include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/shader_utils.h" #include "util/shader_utils.h"
...@@ -134,13 +135,28 @@ class EXTBlendFuncExtendedDrawTest : public ANGLETest ...@@ -134,13 +135,28 @@ class EXTBlendFuncExtendedDrawTest : public ANGLETest
ASSERT_NE(0u, mProgram); ASSERT_NE(0u, mProgram);
} }
virtual GLint getVertexAttribLocation(const char *name)
{
return glGetAttribLocation(mProgram, name);
}
virtual GLint getFragmentUniformLocation(const char *name)
{
return glGetUniformLocation(mProgram, name);
}
virtual void setUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3)
{
glUniform4f(location, v0, v1, v2, v3);
}
void drawTest() void drawTest()
{ {
glUseProgram(mProgram); glUseProgram(mProgram);
GLint position = glGetAttribLocation(mProgram, essl1_shaders::PositionAttrib()); GLint position = getVertexAttribLocation(essl1_shaders::PositionAttrib());
GLint src0 = glGetUniformLocation(mProgram, "src0"); GLint src0 = getFragmentUniformLocation("src0");
GLint src1 = glGetUniformLocation(mProgram, "src1"); GLint src1 = getFragmentUniformLocation("src1");
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
glBindBuffer(GL_ARRAY_BUFFER, mVBO); glBindBuffer(GL_ARRAY_BUFFER, mVBO);
...@@ -152,8 +168,8 @@ class EXTBlendFuncExtendedDrawTest : public ANGLETest ...@@ -152,8 +168,8 @@ class EXTBlendFuncExtendedDrawTest : public ANGLETest
static const float kSrc0[4] = {1.0f, 1.0f, 1.0f, 1.0f}; static const float kSrc0[4] = {1.0f, 1.0f, 1.0f, 1.0f};
static const float kSrc1[4] = {0.3f, 0.6f, 0.9f, 0.7f}; static const float kSrc1[4] = {0.3f, 0.6f, 0.9f, 0.7f};
glUniform4f(src0, kSrc0[0], kSrc0[1], kSrc0[2], kSrc0[3]); setUniform4f(src0, kSrc0[0], kSrc0[1], kSrc0[2], kSrc0[3]);
glUniform4f(src1, kSrc1[0], kSrc1[1], kSrc1[2], kSrc1[3]); setUniform4f(src1, kSrc1[0], kSrc1[1], kSrc1[2], kSrc1[3]);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
glEnable(GL_BLEND); glEnable(GL_BLEND);
...@@ -216,7 +232,8 @@ class EXTBlendFuncExtendedDrawTestES3 : public EXTBlendFuncExtendedDrawTest ...@@ -216,7 +232,8 @@ class EXTBlendFuncExtendedDrawTestES3 : public EXTBlendFuncExtendedDrawTest
mIsES31OrNewer = true; mIsES31OrNewer = true;
} }
} }
void checkOutputIndexQuery(const char *name, GLint expectedIndex)
virtual void checkOutputIndexQuery(const char *name, GLint expectedIndex)
{ {
GLint index = glGetFragDataIndexEXT(mProgram, name); GLint index = glGetFragDataIndexEXT(mProgram, name);
EXPECT_EQ(expectedIndex, index); EXPECT_EQ(expectedIndex, index);
...@@ -246,6 +263,109 @@ class EXTBlendFuncExtendedDrawTestES3 : public EXTBlendFuncExtendedDrawTest ...@@ -246,6 +263,109 @@ class EXTBlendFuncExtendedDrawTestES3 : public EXTBlendFuncExtendedDrawTest
bool mIsES31OrNewer; bool mIsES31OrNewer;
}; };
class EXTBlendFuncExtendedDrawTestES31 : public EXTBlendFuncExtendedDrawTestES3
{
protected:
EXTBlendFuncExtendedDrawTestES31()
: EXTBlendFuncExtendedDrawTestES3(), mPipeline(0), mVertexProgram(0), mFragProgram(0)
{}
GLint getVertexAttribLocation(const char *name) override
{
return glGetAttribLocation(mVertexProgram, name);
}
GLint getFragmentUniformLocation(const char *name) override
{
return glGetUniformLocation(mFragProgram, name);
}
void setUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3) override
{
glActiveShaderProgram(mPipeline, mFragProgram);
EXTBlendFuncExtendedDrawTest::setUniform4f(location, v0, v1, v2, v3);
}
void checkOutputIndexQuery(const char *name, GLint expectedIndex) override
{
GLint index = glGetFragDataIndexEXT(mFragProgram, name);
EXPECT_EQ(expectedIndex, index);
index = glGetProgramResourceLocationIndexEXT(mFragProgram, GL_PROGRAM_OUTPUT, name);
EXPECT_EQ(expectedIndex, index);
}
void setupProgramPipeline(const char *vertexSource, const char *fragmentSource)
{
mVertexProgram = createShaderProgram(GL_VERTEX_SHADER, vertexSource);
ASSERT_NE(mVertexProgram, 0u);
mFragProgram = createShaderProgram(GL_FRAGMENT_SHADER, fragmentSource);
ASSERT_NE(mFragProgram, 0u);
// Generate a program pipeline and attach the programs to their respective stages
glGenProgramPipelines(1, &mPipeline);
EXPECT_GL_NO_ERROR();
glUseProgramStages(mPipeline, GL_VERTEX_SHADER_BIT, mVertexProgram);
EXPECT_GL_NO_ERROR();
glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProgram);
EXPECT_GL_NO_ERROR();
glBindProgramPipeline(mPipeline);
EXPECT_GL_NO_ERROR();
}
GLuint createShaderProgram(GLenum type, const GLchar *shaderString)
{
GLShader shader(type);
if (!shader.get())
{
return 0;
}
glShaderSource(shader, 1, &shaderString, nullptr);
glCompileShader(shader);
GLuint program = glCreateProgram();
if (program)
{
GLint compiled;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
glProgramParameteri(program, GL_PROGRAM_SEPARABLE, GL_TRUE);
if (compiled)
{
glAttachShader(program, shader);
glLinkProgram(program);
glDetachShader(program, shader);
}
}
EXPECT_GL_NO_ERROR();
return program;
}
void testTearDown() override
{
EXTBlendFuncExtendedDrawTest::testTearDown();
if (mVertexProgram)
{
glDeleteProgram(mVertexProgram);
}
if (mFragProgram)
{
glDeleteProgram(mFragProgram);
}
if (mPipeline)
{
glDeleteProgramPipelines(1, &mPipeline);
}
ASSERT_GL_NO_ERROR();
}
GLuint mPipeline;
GLuint mVertexProgram;
GLuint mFragProgram;
};
} // namespace } // namespace
// Test EXT_blend_func_extended related gets. // Test EXT_blend_func_extended related gets.
...@@ -287,6 +407,12 @@ TEST_P(EXTBlendFuncExtendedDrawTest, FragData) ...@@ -287,6 +407,12 @@ TEST_P(EXTBlendFuncExtendedDrawTest, FragData)
{ {
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended")); ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
// Suspected VK driver bug http://anglebug.com/5523
ANGLE_SKIP_TEST_IF(IsVulkan() && (IsNVIDIA() || IsPixel2()));
// Suspected AMD VK driver bug http://anglebug.com/5537
ANGLE_SKIP_TEST_IF(IsVulkan() && IsWindows() && IsAMD());
const char *kFragColorShader = const char *kFragColorShader =
"#extension GL_EXT_blend_func_extended : require\n" "#extension GL_EXT_blend_func_extended : require\n"
"precision mediump float;\n" "precision mediump float;\n"
...@@ -396,6 +522,9 @@ TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentArrayOutputLocationsAPI) ...@@ -396,6 +522,9 @@ TEST_P(EXTBlendFuncExtendedDrawTestES3, FragmentArrayOutputLocationsAPI)
// TODO: Investigate this mac-only failure. http://angleproject.com/1085 // TODO: Investigate this mac-only failure. http://angleproject.com/1085
ANGLE_SKIP_TEST_IF(IsOSX()); ANGLE_SKIP_TEST_IF(IsOSX());
// Suspected VK driver bug http://anglebug.com/5523
ANGLE_SKIP_TEST_IF(IsVulkan() && (IsNVIDIA() || IsPixel2()));
constexpr char kFS[] = R"(#version 300 es constexpr char kFS[] = R"(#version 300 es
#extension GL_EXT_blend_func_extended : require #extension GL_EXT_blend_func_extended : require
precision mediump float; precision mediump float;
...@@ -708,8 +837,103 @@ void main() { ...@@ -708,8 +837,103 @@ void main() {
glDeleteProgram(program); glDeleteProgram(program);
} }
// Use a program pipeline with EXT_blend_func_extended
TEST_P(EXTBlendFuncExtendedDrawTestES31, UseProgramPipeline)
{
// Only the Vulkan backend supports PPO
ANGLE_SKIP_TEST_IF(!IsVulkan());
// Create two separable program objects from a
// single source string respectively (vertSrc and fragSrc)
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
const char *kFragColorShader = R"(#version 300 es
#extension GL_EXT_blend_func_extended : require
precision mediump float;
uniform vec4 src0;
uniform vec4 src1;
layout(location = 0, index = 1) out vec4 outSrc1;
layout(location = 0, index = 0) out vec4 outSrc0;
void main() {
outSrc0 = src0;
outSrc1 = src1;
})";
setupProgramPipeline(essl3_shaders::vs::Simple(), kFragColorShader);
checkOutputIndexQuery("outSrc0", 0);
checkOutputIndexQuery("outSrc1", 1);
ASSERT_EQ(mProgram, 0u);
drawTest();
ASSERT_GL_NO_ERROR();
}
// Use program pipeline where the fragment program is changed
TEST_P(EXTBlendFuncExtendedDrawTestES31, UseTwoProgramStages)
{
// Only the Vulkan backend supports PPO
ANGLE_SKIP_TEST_IF(!IsVulkan());
// Create two separable program objects from a
// single source string respectively (vertSrc and fragSrc)
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_blend_func_extended"));
const char *kFragColorShaderFlipped = R"(#version 300 es
#extension GL_EXT_blend_func_extended : require
precision mediump float;
uniform vec4 src0;
uniform vec4 src1;
layout(location = 0, index = 0) out vec4 outSrc1;
layout(location = 0, index = 1) out vec4 outSrc0;
void main() {
outSrc0 = src0;
outSrc1 = src1;
})";
const char *kFragColorShader = R"(#version 300 es
#extension GL_EXT_blend_func_extended : require
precision mediump float;
uniform vec4 src0;
uniform vec4 src1;
layout(location = 0, index = 1) out vec4 outSrc1;
layout(location = 0, index = 0) out vec4 outSrc0;
void main() {
outSrc0 = src0;
outSrc1 = src1;
})";
setupProgramPipeline(essl3_shaders::vs::Simple(), kFragColorShaderFlipped);
// Check index values frag shader with the "flipped" index values
checkOutputIndexQuery("outSrc0", 1);
checkOutputIndexQuery("outSrc1", 0);
GLuint previousProgram = mFragProgram;
mFragProgram = createShaderProgram(GL_FRAGMENT_SHADER, kFragColorShader);
ASSERT_NE(mFragProgram, 0u);
// Change the Fragment program of the pipeline
glUseProgramStages(mPipeline, GL_FRAGMENT_SHADER_BIT, mFragProgram);
EXPECT_GL_NO_ERROR();
checkOutputIndexQuery("outSrc0", 0);
checkOutputIndexQuery("outSrc1", 1);
ASSERT_EQ(mProgram, 0u);
drawTest();
if (previousProgram)
{
glDeleteProgram(previousProgram);
}
ASSERT_GL_NO_ERROR();
}
ANGLE_INSTANTIATE_TEST_ES2(EXTBlendFuncExtendedTest); ANGLE_INSTANTIATE_TEST_ES2(EXTBlendFuncExtendedTest);
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(EXTBlendFuncExtendedTestES3); ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(EXTBlendFuncExtendedTestES3);
ANGLE_INSTANTIATE_TEST_ES2(EXTBlendFuncExtendedDrawTest); ANGLE_INSTANTIATE_TEST_ES2(EXTBlendFuncExtendedDrawTest);
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(EXTBlendFuncExtendedDrawTestES3); ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(EXTBlendFuncExtendedDrawTestES3);
ANGLE_INSTANTIATE_TEST_ES31(EXTBlendFuncExtendedDrawTestES31);
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