Vulkan: Optimize updating blend state in pipeline desc

Updating blend funcs and equations always updated all 8 slots.  Now
that's only done for the attachments that are present.

Bug: angleproject:6298
Change-Id: I58fa7e4dfa27d05fef54cc9d56c7b2aa5ef43dd8
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3202550
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Tim Van Patten <timvp@google.com>
This commit is contained in:
Shahbaz Youssefi 2021-10-18 13:54:00 -04:00 коммит произвёл Angle LUCI CQ
Родитель 7defdb6044
Коммит e637e4c93b
7 изменённых файлов: 292 добавлений и 39 удалений

Просмотреть файл

@ -134,7 +134,7 @@ class FramebufferState final : angle::NonCopyable
bool isDefault() const;
const gl::Offset &getSurfaceTextureOffset() const { return mSurfaceTextureOffset; }
const Offset &getSurfaceTextureOffset() const { return mSurfaceTextureOffset; }
rx::Serial getFramebufferSerial() const { return mFramebufferSerial; }
@ -156,7 +156,7 @@ class FramebufferState final : angle::NonCopyable
FramebufferAttachment mStencilAttachment;
// Tracks all the color buffers attached to this FramebufferDesc
gl::DrawBufferMask mColorAttachmentsMask;
DrawBufferMask mColorAttachmentsMask;
std::vector<GLenum> mDrawBufferStates;
GLenum mReadBufferState;
@ -185,7 +185,7 @@ class FramebufferState final : angle::NonCopyable
// EXT_sRGB_write_control
SrgbWriteControlMode mSrgbWriteControlMode;
gl::Offset mSurfaceTextureOffset;
Offset mSurfaceTextureOffset;
};
class Framebuffer final : public angle::ObserverInterface,
@ -328,7 +328,7 @@ class Framebuffer final : public angle::ObserverInterface,
// Returns the offset into the texture backing the default framebuffer's surface if any. Returns
// zero offset otherwise. The renderer will apply the offset to scissor and viewport rects used
// for draws, clears, and blits.
const gl::Offset &getSurfaceTextureOffset() const;
const Offset &getSurfaceTextureOffset() const;
angle::Result discard(const Context *context, size_t count, const GLenum *attachments);
angle::Result invalidate(const Context *context, size_t count, const GLenum *attachments);
@ -493,8 +493,7 @@ class Framebuffer final : public angle::ObserverInterface,
FramebufferAttachment *getAttachmentFromSubjectIndex(angle::SubjectIndex index);
ANGLE_INLINE void updateFloat32ColorAttachmentBits(size_t index,
const gl::InternalFormat *format)
ANGLE_INLINE void updateFloat32ColorAttachmentBits(size_t index, const InternalFormat *format)
{
mFloat32ColorAttachmentBits.set(index, format->type == GL_FLOAT);
}

Просмотреть файл

@ -3181,8 +3181,10 @@ SurfaceRotation ContextVk::getRotationReadFramebuffer() const
return mCurrentRotationReadFramebuffer;
}
void ContextVk::updateColorMasks(const gl::BlendStateExt &blendStateExt)
void ContextVk::updateColorMasks()
{
const gl::BlendStateExt &blendStateExt = mState.getBlendStateExt();
mClearColorMasks = blendStateExt.mColorMask;
FramebufferVk *framebufferVk = vk::GetImpl(mState.getDrawFramebuffer());
@ -3191,6 +3193,20 @@ void ContextVk::updateColorMasks(const gl::BlendStateExt &blendStateExt)
framebufferVk->getState().getEnabledDrawBuffers());
}
void ContextVk::updateBlendFuncsAndEquations()
{
const gl::BlendStateExt &blendStateExt = mState.getBlendStateExt();
FramebufferVk *framebufferVk = vk::GetImpl(mState.getDrawFramebuffer());
mCachedDrawFramebufferColorAttachmentMask = framebufferVk->getState().getEnabledDrawBuffers();
mGraphicsPipelineDesc->updateBlendFuncs(&mGraphicsPipelineTransition, blendStateExt,
mCachedDrawFramebufferColorAttachmentMask);
mGraphicsPipelineDesc->updateBlendEquations(&mGraphicsPipelineTransition, blendStateExt,
mCachedDrawFramebufferColorAttachmentMask);
}
void ContextVk::updateSampleMaskWithRasterizationSamples(const uint32_t rasterizationSamples)
{
// FramebufferVk::syncState could have been the origin for this call, at which point the
@ -3511,15 +3527,17 @@ angle::Result ContextVk::syncState(const gl::Context *context,
glState.getBlendColor());
break;
case gl::State::DIRTY_BIT_BLEND_FUNCS:
mGraphicsPipelineDesc->updateBlendFuncs(&mGraphicsPipelineTransition,
glState.getBlendStateExt());
mGraphicsPipelineDesc->updateBlendFuncs(
&mGraphicsPipelineTransition, glState.getBlendStateExt(),
mDrawFramebuffer->getState().getColorAttachmentsMask());
break;
case gl::State::DIRTY_BIT_BLEND_EQUATIONS:
mGraphicsPipelineDesc->updateBlendEquations(&mGraphicsPipelineTransition,
glState.getBlendStateExt());
mGraphicsPipelineDesc->updateBlendEquations(
&mGraphicsPipelineTransition, glState.getBlendStateExt(),
mDrawFramebuffer->getState().getColorAttachmentsMask());
break;
case gl::State::DIRTY_BIT_COLOR_MASK:
updateColorMasks(glState.getBlendStateExt());
updateColorMasks();
break;
case gl::State::DIRTY_BIT_SAMPLE_ALPHA_TO_COVERAGE_ENABLED:
mGraphicsPipelineDesc->updateAlphaToCoverageEnable(
@ -3675,6 +3693,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
// triggered at endRenderPass time.
mHasDeferredFlush = true;
}
gl::Framebuffer *drawFramebuffer = glState.getDrawFramebuffer();
mDrawFramebuffer = vk::GetImpl(drawFramebuffer);
mDrawFramebuffer->setReadOnlyDepthFeedbackLoopMode(false);
@ -3684,7 +3703,7 @@ angle::Result ContextVk::syncState(const gl::Context *context,
updateGraphicsPipelineDescWithSpecConstUsageBits(usageBits);
updateViewport(mDrawFramebuffer, glState.getViewport(), glState.getNearPlane(),
glState.getFarPlane());
updateColorMasks(glState.getBlendStateExt());
updateColorMasks();
updateRasterizationSamples(mDrawFramebuffer->getSamples());
updateRasterizerDiscardEnabled(
mState.isQueryActive(gl::QueryType::PrimitivesGenerated));
@ -3694,6 +3713,16 @@ angle::Result ContextVk::syncState(const gl::Context *context,
isYFlipEnabledForDrawFBO());
updateScissor(glState);
updateDepthStencil(glState);
// Clear the blend funcs/equations for color attachment indices that no longer
// exist.
gl::DrawBufferMask newColorAttachmentMask =
mDrawFramebuffer->getState().getColorAttachmentsMask();
mGraphicsPipelineDesc->resetBlendFuncsAndEquations(
&mGraphicsPipelineTransition, mCachedDrawFramebufferColorAttachmentMask,
newColorAttachmentMask);
mCachedDrawFramebufferColorAttachmentMask = newColorAttachmentMask;
mGraphicsPipelineDesc->resetSubpass(&mGraphicsPipelineTransition);
onDrawFramebufferRenderPassDescChange(mDrawFramebuffer, nullptr);
break;

Просмотреть файл

@ -383,7 +383,8 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
gl::TextureType type,
gl::SamplerFormat format,
gl::Texture **textureOut);
void updateColorMasks(const gl::BlendStateExt &blendStateExt);
void updateColorMasks();
void updateBlendFuncsAndEquations();
void updateSampleMaskWithRasterizationSamples(const uint32_t rasterizationSamples);
void handleError(VkResult errorCode,
@ -1118,6 +1119,12 @@ class ContextVk : public ContextImpl, public vk::Context, public MultisampleText
// A list of gpu events since the last clock sync.
std::vector<GpuEvent> mGpuEvents;
// Cached value of the color attachment mask of the current draw framebuffer. This is used to
// know which attachment indices have their blend state set in |mGraphicsPipelineDesc|, and
// subsequently is used to clear the blend state for attachments that no longer exist when a new
// framebuffer is bound.
gl::DrawBufferMask mCachedDrawFramebufferColorAttachmentMask;
bool mHasDeferredFlush;
// GL_EXT_shader_framebuffer_fetch_non_coherent

Просмотреть файл

@ -1645,8 +1645,6 @@ angle::Result FramebufferVk::invalidateImpl(ContextVk *contextVk,
angle::Result FramebufferVk::updateColorAttachment(const gl::Context *context,
uint32_t colorIndexGL)
{
ContextVk *contextVk = vk::GetImpl(context);
ANGLE_TRY(mRenderTargetCache.updateColorRenderTarget(context, mState, colorIndexGL));
// Update cached masks for masked clears.
@ -1660,8 +1658,6 @@ angle::Result FramebufferVk::updateColorAttachment(const gl::Context *context,
const angle::Format &intendedFormat = renderTarget->getImageIntendedFormat();
mEmulatedAlphaAttachmentMask.set(
colorIndexGL, intendedFormat.alphaBits == 0 && actualFormat.alphaBits > 0);
contextVk->updateColorMasks(context->getState().getBlendStateExt());
}
else
{
@ -1813,7 +1809,7 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
gl::DrawBufferMask dirtyColorAttachments;
bool dirtyDepthStencilAttachment = false;
bool shouldUpdateColorMask = false;
bool shouldUpdateColorMaskAndBlend = false;
bool shouldUpdateLayerCount = false;
bool shouldUpdateSrgbWriteControlMode = false;
@ -1835,8 +1831,8 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
ANGLE_TRY(mRenderTargetCache.update(context, mState, dirtyBits));
break;
case gl::Framebuffer::DIRTY_BIT_DRAW_BUFFERS:
shouldUpdateColorMask = true;
shouldUpdateLayerCount = true;
shouldUpdateColorMaskAndBlend = true;
shouldUpdateLayerCount = true;
break;
case gl::Framebuffer::DIRTY_BIT_DEFAULT_WIDTH:
case gl::Framebuffer::DIRTY_BIT_DEFAULT_HEIGHT:
@ -1871,8 +1867,8 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
ANGLE_TRY(updateColorAttachment(context, colorIndexGL));
shouldUpdateColorMask = true;
shouldUpdateLayerCount = true;
shouldUpdateColorMaskAndBlend = true;
shouldUpdateLayerCount = true;
dirtyColorAttachments.set(colorIndexGL);
break;
@ -1889,9 +1885,10 @@ angle::Result FramebufferVk::syncState(const gl::Context *context,
mRenderPassDesc.setWriteControlMode(newSrgbWriteControlMode);
}
if (shouldUpdateColorMask)
if (shouldUpdateColorMaskAndBlend)
{
contextVk->updateColorMasks(context->getState().getBlendStateExt());
contextVk->updateColorMasks();
contextVk->updateBlendFuncsAndEquations();
}
if (shouldUpdateLayerCount)

Просмотреть файл

@ -2270,12 +2270,12 @@ void GraphicsPipelineDesc::updateBlendColor(GraphicsPipelineTransitionBits *tran
mInputAssemblyAndColorBlendStateInfo.blendConstants[1] = color.green;
mInputAssemblyAndColorBlendStateInfo.blendConstants[2] = color.blue;
mInputAssemblyAndColorBlendStateInfo.blendConstants[3] = color.alpha;
constexpr size_t kSize = sizeof(mInputAssemblyAndColorBlendStateInfo.blendConstants[0]) * 8;
constexpr size_t kSizeBits = sizeof(mInputAssemblyAndColorBlendStateInfo.blendConstants[0]) * 8;
for (int index = 0; index < 4; ++index)
{
const size_t kBit = ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo,
blendConstants, index, kSize);
blendConstants, index, kSizeBits);
transition->set(kBit);
}
}
@ -2290,12 +2290,12 @@ void GraphicsPipelineDesc::updateBlendEnabled(GraphicsPipelineTransitionBits *tr
}
void GraphicsPipelineDesc::updateBlendEquations(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt)
const gl::BlendStateExt &blendStateExt,
gl::DrawBufferMask attachmentMask)
{
constexpr size_t kSize = sizeof(PackedColorBlendAttachmentState) * 8;
constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex = 0; attachmentIndex < blendStateExt.mMaxDrawBuffers;
++attachmentIndex)
for (size_t attachmentIndex : attachmentMask)
{
PackedColorBlendAttachmentState &blendAttachmentState =
mInputAssemblyAndColorBlendStateInfo.attachments[attachmentIndex];
@ -2304,16 +2304,16 @@ void GraphicsPipelineDesc::updateBlendEquations(GraphicsPipelineTransitionBits *
blendAttachmentState.alphaBlendOp =
PackGLBlendOp(blendStateExt.getEquationAlphaIndexed(attachmentIndex));
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo,
attachments, attachmentIndex, kSize));
attachments, attachmentIndex, kSizeBits));
}
}
void GraphicsPipelineDesc::updateBlendFuncs(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt)
const gl::BlendStateExt &blendStateExt,
gl::DrawBufferMask attachmentMask)
{
constexpr size_t kSize = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex = 0; attachmentIndex < blendStateExt.mMaxDrawBuffers;
++attachmentIndex)
constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex : attachmentMask)
{
PackedColorBlendAttachmentState &blendAttachmentState =
mInputAssemblyAndColorBlendStateInfo.attachments[attachmentIndex];
@ -2326,7 +2326,34 @@ void GraphicsPipelineDesc::updateBlendFuncs(GraphicsPipelineTransitionBits *tran
blendAttachmentState.dstAlphaBlendFactor =
PackGLBlendFactor(blendStateExt.getDstAlphaIndexed(attachmentIndex));
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo,
attachments, attachmentIndex, kSize));
attachments, attachmentIndex, kSizeBits));
}
}
void GraphicsPipelineDesc::resetBlendFuncsAndEquations(GraphicsPipelineTransitionBits *transition,
gl::DrawBufferMask previousAttachmentsMask,
gl::DrawBufferMask newAttachmentsMask)
{
// A framebuffer with attachments in P was bound, and now one with attachments in N is bound.
// We need to clear blend funcs and equations for attachments in P that are not in N. That is
// attachments in P&~N.
const gl::DrawBufferMask attachmentsToClear = previousAttachmentsMask & ~newAttachmentsMask;
constexpr size_t kSizeBits = sizeof(PackedColorBlendAttachmentState) * 8;
for (size_t attachmentIndex : attachmentsToClear)
{
PackedColorBlendAttachmentState &blendAttachmentState =
mInputAssemblyAndColorBlendStateInfo.attachments[attachmentIndex];
blendAttachmentState.colorBlendOp = VK_BLEND_OP_ADD;
blendAttachmentState.alphaBlendOp = VK_BLEND_OP_ADD;
blendAttachmentState.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
blendAttachmentState.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
blendAttachmentState.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
blendAttachmentState.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
transition->set(ANGLE_GET_INDEXED_TRANSITION_BIT(mInputAssemblyAndColorBlendStateInfo,
attachments, attachmentIndex, kSizeBits));
}
}

Просмотреть файл

@ -626,9 +626,14 @@ class GraphicsPipelineDesc final
gl::DrawBufferMask blendEnabledMask);
void updateBlendColor(GraphicsPipelineTransitionBits *transition, const gl::ColorF &color);
void updateBlendFuncs(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt);
const gl::BlendStateExt &blendStateExt,
gl::DrawBufferMask attachmentMask);
void updateBlendEquations(GraphicsPipelineTransitionBits *transition,
const gl::BlendStateExt &blendStateExt);
const gl::BlendStateExt &blendStateExt,
gl::DrawBufferMask attachmentMask);
void resetBlendFuncsAndEquations(GraphicsPipelineTransitionBits *transition,
gl::DrawBufferMask previousAttachmentsMask,
gl::DrawBufferMask newAttachmentsMask);
void setColorWriteMasks(gl::BlendStateExt::ColorMaskStorage::Type colorMasks,
const gl::DrawBufferMask &alphaMask,
const gl::DrawBufferMask &enabledDrawBuffers);

Просмотреть файл

@ -816,6 +816,195 @@ TEST_P(DrawBuffersTestES3, 2DArrayTextures)
glDeleteProgram(program);
}
// Test that blend works when draw buffers and framebuffers change.
TEST_P(DrawBuffersTestES3, BlendWithDrawBufferAndFramebufferChanges)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_draw_buffers_indexed"));
// Qualcomm driver crashes in the presence of VK_ATTACHMENT_UNUSED.
// http://anglebug.com/3423
ANGLE_SKIP_TEST_IF(IsVulkan() && IsAndroid());
// Fails on Intel Ubuntu 19.04 Mesa 19.0.2 Vulkan. http://anglebug.com/3616
ANGLE_SKIP_TEST_IF(IsLinux() && IsIntel() && IsVulkan());
// http://anglebug.com/5154
ANGLE_SKIP_TEST_IF(IsOSX() && IsIntel() && IsDesktopOpenGL());
// Create two framebuffers, one with 3 attachments (fbo3), one with 4 (fbo4). The test issues
// draw calls on fbo3 with different attachments enabled, then switches to fbo4 (without
// dirtying blend state) and draws to other attachments. It ensures that blend state is
// appropriately set on framebuffer change.
GLenum bufs[4] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2,
GL_COLOR_ATTACHMENT3};
GLFramebuffer fbo[2];
GLTexture tex[7];
constexpr GLfloat kClearValue[] = {1, 1, 1, 1};
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
for (uint32_t texIndex = 0; texIndex < 7; ++texIndex)
{
size_t colorAttachmentIndex = texIndex >= 3 ? texIndex - 3 : texIndex;
if (texIndex == 3)
{
glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]);
}
glBindTexture(GL_TEXTURE_2D, tex[texIndex]);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + colorAttachmentIndex,
GL_TEXTURE_2D, tex[texIndex], 0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glDrawBuffers(4, bufs);
glClearBufferfv(GL_COLOR, colorAttachmentIndex, kClearValue);
}
ASSERT_GL_NO_ERROR();
glEnablei(GL_BLEND, 0);
glEnablei(GL_BLEND, 1);
glEnablei(GL_BLEND, 2);
glEnablei(GL_BLEND, 3);
glBlendEquationi(0, GL_FUNC_REVERSE_SUBTRACT);
glBlendEquationi(1, GL_MIN);
glBlendEquationi(2, GL_FUNC_REVERSE_SUBTRACT);
glBlendEquationi(3, GL_FUNC_REVERSE_SUBTRACT);
glBlendFunci(0, GL_ONE, GL_ONE);
glBlendFunci(1, GL_DST_ALPHA, GL_DST_ALPHA);
glBlendFunci(2, GL_SRC_ALPHA, GL_SRC_ALPHA);
glBlendFunci(3, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA);
bufs[0] = GL_NONE;
bufs[2] = GL_NONE;
glDrawBuffers(4, bufs);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
bufs[2] = GL_COLOR_ATTACHMENT2;
glDrawBuffers(3, bufs);
constexpr char kFS[] = R"(#version 300 es
precision highp float;
uniform vec4 value0;
uniform vec4 value1;
uniform vec4 value2;
uniform vec4 value3;
layout(location = 0) out vec4 color0;
layout(location = 1) out vec4 color1;
layout(location = 2) out vec4 color2;
layout(location = 3) out vec4 color3;
void main()
{
color0 = value0;
color1 = value1;
color2 = value2;
color3 = value3;
}
)";
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
glUseProgram(program);
GLint uniforms[4];
for (uint32_t attachmentIndex = 0; attachmentIndex < 4; ++attachmentIndex)
{
char uniformName[20];
snprintf(uniformName, sizeof uniformName, "value%u", attachmentIndex);
uniforms[attachmentIndex] = glGetUniformLocation(program, uniformName);
ASSERT_NE(uniforms[attachmentIndex], -1);
}
// Currently, fbo3 is bound. The attachment states are:
//
// 0: DISABLED Color: (1, 1, 1, 1), Blend: reverse subtract, ONE/ONE
// 1: Color: (1, 1, 1, 1), Blend: min, DST_ALPHA/DST_ALPHA
// 2: Color: (1, 1, 1, 1), Blend: reverse subtract, SRC_ALPHA/SRC_ALPHA
//
// Draw:
//
// 0: Color: don't care
// 1: Color: (0.75, 0.5, 0.25, 0.5) -> Result after blend is: (0.75, 0.5, 0.25, 0.5)
// 2: Color: (0.25, 0.5, 0.75, 0.5) -> Result after blend is: (0.375, 0.25, 0.125, 0.25)
// Draws green into attachment 1
glUniform4f(uniforms[1], 0.75, 0.5, 0.25, 0.5);
glUniform4f(uniforms[2], 0.25, 0.5, 0.75, 0.5);
drawQuad(program, positionAttrib(), 0.5);
ASSERT_GL_NO_ERROR();
bufs[0] = GL_COLOR_ATTACHMENT0;
bufs[1] = GL_NONE;
glDrawBuffers(3, bufs);
// Currently, fbo3 is bound. The attachment states are:
//
// 0: Color: (1, 1, 1, 1), Blend: reverse subtract, ONE/ONE
// 1: DISABLED Color: (0.75, 0.5, 0.25, 0.5), Blend: min, DST_ALPHA/DST_ALPHA
// 2: Color: (0.375, 0.25, 0.125, 0.25), Blend: reverse subtract,
// SRC_ALPHA/SRC_ALPHA
//
// Draw:
//
// 0: Color: (0.5, 0.25, 0.75, 0.25) -> Result after blend is: (0.5, 0.75, 0.25, 0.75)
// 1: Color: don't care
// 2: Color: (0.125, 0, 0, 1) -> Result after blend is: (0.25, 0.25, 0.125, 0)
// Clear with red
glUniform4f(uniforms[0], 0.5, 0.25, 0.75, 0.25);
glUniform4f(uniforms[2], 0.125, 0, 0, 1);
drawQuad(program, positionAttrib(), 0.5);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]);
// Currently, fbo4 is bound. The attachment states are:
//
// 0: DISABLED Color: (1, 1, 1, 1), Blend: reverse subtract, ONE/ONE
// 1: Color: (1, 1, 1, 1), Blend: min, DST_ALPHA/DST_ALPHA
// 2: DISABLED Color: (1, 1, 1, 1), Blend: reverse subtract, SRC_ALPHA/SRC_ALPHA
// 3: Color: (1, 1, 1, 1), Blend: reverse subtract, ONE_MINUS_SRC_ALPHA/SRC_ALPHA
//
// Draw:
//
// 0: Color: don't care
// 1: Color: (0.125, 0.5, 0.625, 0.25) -> Result after blend is: (0.125, 0.5, 0.625, 0.25)
// 2: Color: don't care
// 3: Color: (0.75, 0.25, 0.5, 0.75) -> Result after blend is:
// (0.5625, 0.6875, 0.625, 0.5625)
glUniform4f(uniforms[1], 0.125, 0.5, 0.625, 0.25);
glUniform4f(uniforms[3], 0.75, 0.25, 0.5, 0.75);
drawQuad(program, positionAttrib(), 0.5);
ASSERT_GL_NO_ERROR();
// Verify results
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
glReadBuffer(GL_COLOR_ATTACHMENT0);
EXPECT_PIXEL_NEAR(0, 0, 127, 191, 63, 191, 1);
glReadBuffer(GL_COLOR_ATTACHMENT1);
EXPECT_PIXEL_NEAR(0, 0, 191, 127, 63, 127, 1);
glReadBuffer(GL_COLOR_ATTACHMENT2);
EXPECT_PIXEL_NEAR(0, 0, 63, 63, 31, 0, 1);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]);
glReadBuffer(GL_COLOR_ATTACHMENT0);
EXPECT_PIXEL_NEAR(0, 0, 255, 255, 255, 255, 1);
glReadBuffer(GL_COLOR_ATTACHMENT1);
EXPECT_PIXEL_NEAR(0, 0, 31, 127, 159, 63, 1);
glReadBuffer(GL_COLOR_ATTACHMENT2);
EXPECT_PIXEL_NEAR(0, 0, 255, 255, 255, 255, 1);
glReadBuffer(GL_COLOR_ATTACHMENT3);
EXPECT_PIXEL_NEAR(0, 0, 143, 175, 159, 143, 1);
}
// Vulkan backend is setting per buffer color mask to false for draw buffers that set to GL_NONE.
// These set of tests are to test draw buffer change followed by draw/clear/blit and followed by
// draw buffer change are behaving correctly.