Commit 127990f9 by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Use render pass loadOp for scissored clears

At this point, every clear is done through render pass loadOp, except masked color or stencil clears. The only fallback is clearWithDraw, that can clear both color and stencil at the same time. Bug: angleproject:2361 Change-Id: I805fc12475e832ad2f573f665cdfeb766e61a6d0 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1553740 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTobin Ehlis <tobine@google.com>
parent d581f918
...@@ -257,6 +257,11 @@ Rectangle Rectangle::removeReversal() const ...@@ -257,6 +257,11 @@ Rectangle Rectangle::removeReversal() const
return unreversed; return unreversed;
} }
bool Rectangle::encloses(const gl::Rectangle &inside) const
{
return x0() <= inside.x0() && y0() <= inside.y0() && x1() >= inside.x1() && y1() >= inside.y1();
}
bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection) bool ClipRectangle(const Rectangle &source, const Rectangle &clip, Rectangle *intersection)
{ {
int minSourceX, maxSourceX, minSourceY, maxSourceY; int minSourceX, maxSourceX, minSourceY, maxSourceY;
......
...@@ -48,6 +48,8 @@ struct Rectangle ...@@ -48,6 +48,8 @@ struct Rectangle
// Returns a rectangle with the same area but with height and width guaranteed to be positive. // Returns a rectangle with the same area but with height and width guaranteed to be positive.
Rectangle removeReversal() const; Rectangle removeReversal() const;
bool encloses(const gl::Rectangle &inside) const;
int x; int x;
int y; int y;
int width; int width;
......
...@@ -608,4 +608,32 @@ angle::Result GetVertexRangeInfo(const gl::Context *context, ...@@ -608,4 +608,32 @@ angle::Result GetVertexRangeInfo(const gl::Context *context,
} }
return angle::Result::Continue; return angle::Result::Continue;
} }
gl::Rectangle ClipRectToScissor(const gl::State &glState, const gl::Rectangle &rect, bool invertY)
{
if (glState.isScissorTestEnabled())
{
gl::Rectangle clippedRect;
if (!gl::ClipRectangle(glState.getScissor(), rect, &clippedRect))
{
return gl::Rectangle();
}
if (invertY)
{
clippedRect.y = rect.height - clippedRect.y - clippedRect.height;
}
return clippedRect;
}
// If the scissor test isn't enabled, assume it has infinite size. Its intersection with the
// rect would be the rect itself.
//
// Note that on Vulkan, returning this (as opposed to a fixed max-int-sized rect) could lead to
// unnecessary pipeline creations if two otherwise identical pipelines are used on framebuffers
// with different sizes. If such usage is observed in an application, we should investigate
// possible optimizations.
return rect;
}
} // namespace rx } // namespace rx
...@@ -28,6 +28,7 @@ namespace gl ...@@ -28,6 +28,7 @@ namespace gl
{ {
struct FormatType; struct FormatType;
struct InternalFormat; struct InternalFormat;
class State;
} // namespace gl } // namespace gl
namespace egl namespace egl
...@@ -286,6 +287,8 @@ angle::Result GetVertexRangeInfo(const gl::Context *context, ...@@ -286,6 +287,8 @@ angle::Result GetVertexRangeInfo(const gl::Context *context,
GLint baseVertex, GLint baseVertex,
GLint *startVertexOut, GLint *startVertexOut,
size_t *vertexCountOut); size_t *vertexCountOut);
gl::Rectangle ClipRectToScissor(const gl::State &glState, const gl::Rectangle &rect, bool invertY);
} // namespace rx } // namespace rx
#endif // LIBANGLE_RENDERER_RENDERER_UTILS_H_ #endif // LIBANGLE_RENDERER_RENDERER_UTILS_H_
...@@ -208,12 +208,6 @@ angle::Result CommandGraphResource::recordCommands(Context *context, ...@@ -208,12 +208,6 @@ angle::Result CommandGraphResource::recordCommands(Context *context,
return angle::Result::Continue; return angle::Result::Continue;
} }
const gl::Rectangle &CommandGraphResource::getRenderPassRenderArea() const
{
ASSERT(hasStartedRenderPass());
return mCurrentWritingNode->getRenderPassRenderArea();
}
angle::Result CommandGraphResource::beginRenderPass( angle::Result CommandGraphResource::beginRenderPass(
ContextVk *contextVk, ContextVk *contextVk,
const Framebuffer &framebuffer, const Framebuffer &framebuffer,
...@@ -654,11 +648,6 @@ std::string CommandGraphNode::dumpCommandsForDiagnostics(const char *separator) ...@@ -654,11 +648,6 @@ std::string CommandGraphNode::dumpCommandsForDiagnostics(const char *separator)
return result; return result;
} }
const gl::Rectangle &CommandGraphNode::getRenderPassRenderArea() const
{
return mRenderPassRenderArea;
}
// CommandGraph implementation. // CommandGraph implementation.
CommandGraph::CommandGraph(bool enableGraphDiagnostics, angle::PoolAllocator *poolAllocator) CommandGraph::CommandGraph(bool enableGraphDiagnostics, angle::PoolAllocator *poolAllocator)
: mEnableGraphDiagnostics(enableGraphDiagnostics), : mEnableGraphDiagnostics(enableGraphDiagnostics),
......
...@@ -157,7 +157,7 @@ class CommandGraphNode final : angle::NonCopyable ...@@ -157,7 +157,7 @@ class CommandGraphNode final : angle::NonCopyable
uintptr_t getResourceIDForDiagnostics() const { return mResourceID; } uintptr_t getResourceIDForDiagnostics() const { return mResourceID; }
std::string dumpCommandsForDiagnostics(const char *separator) const; std::string dumpCommandsForDiagnostics(const char *separator) const;
const gl::Rectangle &getRenderPassRenderArea() const; const gl::Rectangle &getRenderPassRenderArea() const { return mRenderPassRenderArea; }
CommandGraphNodeFunction getFunction() const { return mFunction; } CommandGraphNodeFunction getFunction() const { return mFunction; }
...@@ -292,21 +292,30 @@ class CommandGraphResource : angle::NonCopyable ...@@ -292,21 +292,30 @@ class CommandGraphResource : angle::NonCopyable
const std::vector<VkClearValue> &clearValues, const std::vector<VkClearValue> &clearValues,
CommandBuffer **commandBufferOut); CommandBuffer **commandBufferOut);
// Checks if we're in a RenderPass, returning true if so. Updates serial internally. // Checks if we're in a RenderPass without children.
// Returns the started command buffer in commandBufferOut. bool hasStartedRenderPass() const
{
return hasChildlessWritingNode() &&
mCurrentWritingNode->getInsideRenderPassCommands()->valid();
}
// Checks if we're in a RenderPass that encompasses renderArea, returning true if so. Updates
// serial internally. Returns the started command buffer in commandBufferOut.
ANGLE_INLINE bool appendToStartedRenderPass(Serial currentQueueSerial, ANGLE_INLINE bool appendToStartedRenderPass(Serial currentQueueSerial,
const gl::Rectangle &renderArea,
CommandBuffer **commandBufferOut) CommandBuffer **commandBufferOut)
{ {
updateQueueSerial(currentQueueSerial); updateQueueSerial(currentQueueSerial);
if (hasStartedRenderPass()) if (hasStartedRenderPass())
{ {
if (mCurrentWritingNode->getRenderPassRenderArea().encloses(renderArea))
{
*commandBufferOut = mCurrentWritingNode->getInsideRenderPassCommands(); *commandBufferOut = mCurrentWritingNode->getInsideRenderPassCommands();
return true; return true;
} }
else
{
return false;
} }
return false;
} }
// Returns true if the render pass is started, but there are no commands yet recorded in it. // Returns true if the render pass is started, but there are no commands yet recorded in it.
...@@ -336,7 +345,11 @@ class CommandGraphResource : angle::NonCopyable ...@@ -336,7 +345,11 @@ class CommandGraphResource : angle::NonCopyable
} }
// Accessor for RenderPass RenderArea. // Accessor for RenderPass RenderArea.
const gl::Rectangle &getRenderPassRenderArea() const; const gl::Rectangle &getRenderPassRenderArea() const
{
ASSERT(hasStartedRenderPass());
return mCurrentWritingNode->getRenderPassRenderArea();
}
// Called when 'this' object changes, but we'd like to start a new command buffer later. // Called when 'this' object changes, but we'd like to start a new command buffer later.
void finishCurrentCommands(RendererVk *renderer); void finishCurrentCommands(RendererVk *renderer);
...@@ -365,13 +378,6 @@ class CommandGraphResource : angle::NonCopyable ...@@ -365,13 +378,6 @@ class CommandGraphResource : angle::NonCopyable
return (mCurrentWritingNode != nullptr && !mCurrentWritingNode->hasChildren()); return (mCurrentWritingNode != nullptr && !mCurrentWritingNode->hasChildren());
} }
// Checks if we're in a RenderPass without children.
bool hasStartedRenderPass() const
{
return hasChildlessWritingNode() &&
mCurrentWritingNode->getInsideRenderPassCommands()->valid();
}
void startNewCommands(RendererVk *renderer); void startNewCommands(RendererVk *renderer);
void onWriteImpl(CommandGraphNode *writingNode, Serial currentSerial); void onWriteImpl(CommandGraphNode *writingNode, Serial currentSerial);
......
...@@ -260,10 +260,13 @@ angle::Result ContextVk::setupDraw(const gl::Context *context, ...@@ -260,10 +260,13 @@ angle::Result ContextVk::setupDraw(const gl::Context *context,
if (!mCommandBuffer) if (!mCommandBuffer)
{ {
mDirtyBits |= mNewCommandBufferDirtyBits; mDirtyBits |= mNewCommandBufferDirtyBits;
gl::Rectangle scissoredRenderArea = mDrawFramebuffer->getScissoredRenderArea(this);
if (!mDrawFramebuffer->appendToStartedRenderPass(mRenderer->getCurrentQueueSerial(), if (!mDrawFramebuffer->appendToStartedRenderPass(mRenderer->getCurrentQueueSerial(),
&mCommandBuffer)) scissoredRenderArea, &mCommandBuffer))
{ {
ANGLE_TRY(mDrawFramebuffer->startNewRenderPass(this, &mCommandBuffer)); ANGLE_TRY(
mDrawFramebuffer->startNewRenderPass(this, scissoredRenderArea, &mCommandBuffer));
} }
} }
...@@ -686,12 +689,11 @@ void ContextVk::updateDepthRange(float nearPlane, float farPlane) ...@@ -686,12 +689,11 @@ void ContextVk::updateDepthRange(float nearPlane, float farPlane)
void ContextVk::updateScissor(const gl::State &glState) void ContextVk::updateScissor(const gl::State &glState)
{ {
FramebufferVk *framebufferVk = vk::GetImpl(glState.getDrawFramebuffer()); FramebufferVk *framebufferVk = vk::GetImpl(glState.getDrawFramebuffer());
gl::Box dimensions = framebufferVk->getState().getDimensions(); gl::Rectangle scissoredRenderArea = framebufferVk->getScissoredRenderArea(this);
gl::Rectangle renderArea(0, 0, dimensions.width, dimensions.height); VkRect2D scissor = gl_vk::GetRect(scissoredRenderArea);
VkRect2D scissor;
gl_vk::GetScissor(glState, isViewportFlipEnabledForDrawFBO(), renderArea, &scissor);
mGraphicsPipelineDesc->updateScissor(&mGraphicsPipelineTransition, scissor); mGraphicsPipelineDesc->updateScissor(&mGraphicsPipelineTransition, scissor);
framebufferVk->onScissorChange(this);
} }
angle::Result ContextVk::syncState(const gl::Context *context, angle::Result ContextVk::syncState(const gl::Context *context,
......
...@@ -218,14 +218,10 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context, ...@@ -218,14 +218,10 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context,
{ {
ContextVk *contextVk = vk::GetImpl(context); ContextVk *contextVk = vk::GetImpl(context);
const gl::State &glState = context->getState(); const gl::Rectangle scissoredRenderArea = getScissoredRenderArea(contextVk);
const gl::Rectangle &scissor = glState.getScissor();
const gl::Rectangle renderArea(0, 0, mState.getDimensions().width,
mState.getDimensions().height);
gl::Rectangle scissorRenderAreaIntersection;
// Discard clear altogether if scissor has 0 width or height. // Discard clear altogether if scissor has 0 width or height.
if (glState.isScissorTestEnabled() && if (scissoredRenderArea.width == 0 || scissoredRenderArea.height == 0)
!gl::ClipRectangle(scissor, renderArea, &scissorRenderAreaIntersection))
{ {
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -268,11 +264,6 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context, ...@@ -268,11 +264,6 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context,
VkClearDepthStencilValue modifiedDepthStencilValue = clearDepthStencilValue; VkClearDepthStencilValue modifiedDepthStencilValue = clearDepthStencilValue;
// If scissor is enabled, but covers the whole of framebuffer, it can be considered disabled for
// the sake of clear.
bool isScissorTestEffectivelyEnabled =
glState.isScissorTestEnabled() && scissorRenderAreaIntersection != renderArea;
// We can use render pass load ops if clearing depth, unmasked color or unmasked stencil. If // We can use render pass load ops if clearing depth, unmasked color or unmasked stencil. If
// there's a depth mask, depth clearing is already disabled. // there's a depth mask, depth clearing is already disabled.
bool maskedClearColor = bool maskedClearColor =
...@@ -287,20 +278,20 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context, ...@@ -287,20 +278,20 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context,
bool clearAnyWithRenderPassLoadOp = bool clearAnyWithRenderPassLoadOp =
clearColorWithRenderPassLoadOp || clearDepth || clearStencilWithRenderPassLoadOp; clearColorWithRenderPassLoadOp || clearDepth || clearStencilWithRenderPassLoadOp;
if (clearAnyWithRenderPassLoadOp && !isScissorTestEffectivelyEnabled) if (clearAnyWithRenderPassLoadOp)
{ {
// Clearing color is indicated by the set bits in this mask. If not clearing colors with // Clearing color is indicated by the set bits in this mask. If not clearing colors with
// render pass loadOp, the default value of all-zeros means the clear is not done in // render pass loadOp, the default value of all-zeros means the clear is not done in
// clearWithRenderPassOp below. // clearWithRenderPassOp below. In that case, only clear depth/stencil with render pass
// loadOp.
gl::DrawBufferMask clearBuffersWithRenderPassLoadOp; gl::DrawBufferMask clearBuffersWithRenderPassLoadOp;
if (clearColorWithRenderPassLoadOp) if (clearColorWithRenderPassLoadOp)
{ {
clearBuffersWithRenderPassLoadOp = clearColorBuffers; clearBuffersWithRenderPassLoadOp = clearColorBuffers;
} }
// If there's a color mask, only clear depth/stencil with render pass loadOp. ANGLE_TRY(clearWithRenderPassOp(
ANGLE_TRY(clearWithRenderPassOp(contextVk, clearBuffersWithRenderPassLoadOp, clearDepth, contextVk, scissoredRenderArea, clearBuffersWithRenderPassLoadOp, clearDepth,
clearStencilWithRenderPassLoadOp, clearColorValue, clearStencilWithRenderPassLoadOp, clearColorValue, modifiedDepthStencilValue));
modifiedDepthStencilValue));
// On some hardware, having inline commands at this point results in corrupted output. In // On some hardware, having inline commands at this point results in corrupted output. In
// that case, end the render pass immediately. http://anglebug.com/2361 // that case, end the render pass immediately. http://anglebug.com/2361
...@@ -328,25 +319,19 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context, ...@@ -328,25 +319,19 @@ angle::Result FramebufferVk::clearImpl(const gl::Context *context,
} }
} }
// Note: if no driver bug workaround is necessary, the clearDepth feature of
// clearWithDraw can be removed.
ASSERT(clearDepth == false);
// The most costly clear mode is when we need to mask out specific color channels or stencil // The most costly clear mode is when we need to mask out specific color channels or stencil
// bits. This can only be done with a draw call. The scissor region however can easily be // bits. This can only be done with a draw call. The scissor region however can easily be
// integrated with this method. // integrated with this method.
// //
// Since we have to have a draw call for the sake of masked color or stencil, we can make sure // Since we have to have a draw call for the sake of masked color or stencil, we can make sure
// everything else is cleared with the draw call at the same time as well. // everything else is cleared with the draw call at the same time as well.
if (maskedClearColor || maskedClearStencil) return clearWithDraw(contextVk, scissoredRenderArea, clearColorBuffers, clearDepth,
{ clearStencil, colorMaskFlags, stencilMask, clearColorValue,
return clearWithDraw(contextVk, clearColorBuffers, clearDepth, clearStencil, colorMaskFlags, modifiedDepthStencilValue);
stencilMask, clearColorValue, modifiedDepthStencilValue);
}
ASSERT(isScissorTestEffectivelyEnabled);
// With scissor test enabled, we clear very differently and we don't need to access
// the image inside each attachment we can just use clearCmdAttachments with our
// scissor region instead.
return clearWithClearAttachments(contextVk, clearColorBuffers, clearDepth, clearStencil,
clearColorValue, modifiedDepthStencilValue);
} }
angle::Result FramebufferVk::clearBufferfv(const gl::Context *context, angle::Result FramebufferVk::clearBufferfv(const gl::Context *context,
...@@ -990,18 +975,27 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe ...@@ -990,18 +975,27 @@ angle::Result FramebufferVk::getFramebuffer(ContextVk *contextVk, vk::Framebuffe
angle::Result FramebufferVk::clearWithRenderPassOp( angle::Result FramebufferVk::clearWithRenderPassOp(
ContextVk *contextVk, ContextVk *contextVk,
const gl::Rectangle &clearArea,
gl::DrawBufferMask clearColorBuffers, gl::DrawBufferMask clearColorBuffers,
bool clearDepth, bool clearDepth,
bool clearStencil, bool clearStencil,
const VkClearColorValue &clearColorValue, const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue) const VkClearDepthStencilValue &clearDepthStencilValue)
{ {
// If render pass hasn't started, start it. If it's started and contains commands, we cannot // Start a new render pass if:
// modify its ops, so start a new render pass. //
if (!mFramebuffer.valid() || !mFramebuffer.renderPassStartedButEmpty()) // - no render pass has started,
// - there is a render pass started but it contains commands; we cannot modify its ops, so new
// render pass is needed,
// - the current render area doesn't match the clear area. We need the render area to be
// exactly as specified by the scissor for the loadOp to clear only that area. See
// onScissorChange for more information.
if (!mFramebuffer.valid() || !mFramebuffer.renderPassStartedButEmpty() ||
mFramebuffer.getRenderPassRenderArea() != clearArea)
{ {
vk::CommandBuffer *commandBuffer; vk::CommandBuffer *commandBuffer;
ANGLE_TRY(startNewRenderPass(contextVk, &commandBuffer)); ANGLE_TRY(startNewRenderPass(contextVk, clearArea, &commandBuffer));
} }
size_t attachmentIndex = 0; size_t attachmentIndex = 0;
...@@ -1047,113 +1041,8 @@ angle::Result FramebufferVk::clearWithRenderPassOp( ...@@ -1047,113 +1041,8 @@ angle::Result FramebufferVk::clearWithRenderPassOp(
return angle::Result::Continue; return angle::Result::Continue;
} }
angle::Result FramebufferVk::clearWithClearAttachments(
ContextVk *contextVk,
gl::DrawBufferMask clearColorBuffers,
bool clearDepth,
bool clearStencil,
const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue)
{
// Trigger a new command node to ensure overlapping writes happen sequentially.
mFramebuffer.finishCurrentCommands(contextVk->getRenderer());
// This command can only happen inside a render pass, so obtain one if its already happening
// or create a new one if not.
vk::CommandBuffer *commandBuffer = nullptr;
vk::RecordingMode mode = vk::RecordingMode::Start;
ANGLE_TRY(getCommandBufferForDraw(contextVk, &commandBuffer, &mode));
// The array layer is offset by the ImageView. So we shouldn't need to set a base array layer.
VkClearRect clearRect = {};
clearRect.baseArrayLayer = 0;
clearRect.layerCount = 1;
// When clearing, the scissor region must be clipped to the renderArea per the validation rules
// in Vulkan.
gl::Rectangle intersection;
if (!gl::ClipRectangle(contextVk->getState().getScissor(),
mFramebuffer.getRenderPassRenderArea(), &intersection))
{
// There is nothing to clear since the scissor is outside of the render area.
return angle::Result::Continue;
}
clearRect.rect = gl_vk::GetRect(intersection);
if (contextVk->isViewportFlipEnabledForDrawFBO())
{
clearRect.rect.offset.y = mFramebuffer.getRenderPassRenderArea().height -
clearRect.rect.offset.y - clearRect.rect.extent.height;
}
gl::AttachmentArray<VkClearAttachment> clearAttachments;
int clearAttachmentIndex = 0;
if (clearColorBuffers.any())
{
RenderTargetVk *renderTarget = getColorReadRenderTarget();
const vk::Format &format = renderTarget->getImageFormat();
VkClearValue modifiedClear = {clearColorValue};
// We need to make sure we are not clearing the alpha channel if we are using a buffer
// format that doesn't have an alpha channel.
if (format.angleFormat().alphaBits == 0)
{
SetEmulatedAlphaValue(format, &modifiedClear.color);
}
// TODO(jmadill): Support gaps in RenderTargets. http://anglebug.com/2394
for (size_t colorIndex : clearColorBuffers)
{
VkClearAttachment &clearAttachment = clearAttachments[clearAttachmentIndex];
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clearAttachment.colorAttachment = static_cast<uint32_t>(colorIndex);
clearAttachment.clearValue = modifiedClear;
++clearAttachmentIndex;
}
}
VkClearValue depthStencilClearValue = {};
depthStencilClearValue.depthStencil = clearDepthStencilValue;
if (clearDepth && clearStencil && mState.getDepthStencilAttachment() != nullptr)
{
// When we have a packed depth/stencil attachment we can do 1 clear for both when it
// applies.
VkClearAttachment &clearAttachment = clearAttachments[clearAttachmentIndex];
clearAttachment.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT | VK_IMAGE_ASPECT_STENCIL_BIT;
clearAttachment.colorAttachment = VK_ATTACHMENT_UNUSED;
clearAttachment.clearValue = depthStencilClearValue;
++clearAttachmentIndex;
}
else
{
if (clearDepth)
{
VkClearAttachment &clearAttachment = clearAttachments[clearAttachmentIndex];
clearAttachment.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
clearAttachment.colorAttachment = VK_ATTACHMENT_UNUSED;
clearAttachment.clearValue = depthStencilClearValue;
++clearAttachmentIndex;
}
if (clearStencil)
{
VkClearAttachment &clearAttachment = clearAttachments[clearAttachmentIndex];
clearAttachment.aspectMask = VK_IMAGE_ASPECT_STENCIL_BIT;
clearAttachment.colorAttachment = VK_ATTACHMENT_UNUSED;
clearAttachment.clearValue = depthStencilClearValue;
++clearAttachmentIndex;
}
}
commandBuffer->clearAttachments(static_cast<uint32_t>(clearAttachmentIndex),
clearAttachments.data(), 1, &clearRect);
return angle::Result::Continue;
}
angle::Result FramebufferVk::clearWithDraw(ContextVk *contextVk, angle::Result FramebufferVk::clearWithDraw(ContextVk *contextVk,
const gl::Rectangle &clearArea,
gl::DrawBufferMask clearColorBuffers, gl::DrawBufferMask clearColorBuffers,
bool clearDepth, bool clearDepth,
bool clearStencil, bool clearStencil,
...@@ -1167,6 +1056,7 @@ angle::Result FramebufferVk::clearWithDraw(ContextVk *contextVk, ...@@ -1167,6 +1056,7 @@ angle::Result FramebufferVk::clearWithDraw(ContextVk *contextVk,
UtilsVk::ClearFramebufferParameters params = {}; UtilsVk::ClearFramebufferParameters params = {};
params.renderPassDesc = &getRenderPassDesc(); params.renderPassDesc = &getRenderPassDesc();
params.renderAreaHeight = mState.getDimensions().height; params.renderAreaHeight = mState.getDimensions().height;
params.clearArea = clearArea;
params.colorClearValue = clearColorValue; params.colorClearValue = clearColorValue;
params.depthStencilClearValue = clearDepthStencilValue; params.depthStencilClearValue = clearDepthStencilValue;
params.stencilMask = stencilMask; params.stencilMask = stencilMask;
...@@ -1214,23 +1104,8 @@ angle::Result FramebufferVk::getSamplePosition(const gl::Context *context, ...@@ -1214,23 +1104,8 @@ angle::Result FramebufferVk::getSamplePosition(const gl::Context *context,
return angle::Result::Stop; return angle::Result::Stop;
} }
angle::Result FramebufferVk::getCommandBufferForDraw(ContextVk *contextVk,
vk::CommandBuffer **commandBufferOut,
vk::RecordingMode *modeOut)
{
RendererVk *renderer = contextVk->getRenderer();
// This will clear the current write operation if it is complete.
if (appendToStartedRenderPass(renderer->getCurrentQueueSerial(), commandBufferOut))
{
*modeOut = vk::RecordingMode::Append;
return angle::Result::Continue;
}
return startNewRenderPass(contextVk, commandBufferOut);
}
angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
const gl::Rectangle &renderArea,
vk::CommandBuffer **commandBufferOut) vk::CommandBuffer **commandBufferOut)
{ {
vk::Framebuffer *framebuffer = nullptr; vk::Framebuffer *framebuffer = nullptr;
...@@ -1273,9 +1148,6 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk, ...@@ -1273,9 +1148,6 @@ angle::Result FramebufferVk::startNewRenderPass(ContextVk *contextVk,
attachmentClearValues.emplace_back(kUninitializedClearValue); attachmentClearValues.emplace_back(kUninitializedClearValue);
} }
gl::Rectangle renderArea =
gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height);
return mFramebuffer.beginRenderPass(contextVk, *framebuffer, renderArea, mRenderPassDesc, return mFramebuffer.beginRenderPass(contextVk, *framebuffer, renderArea, mRenderPassDesc,
renderPassAttachmentOps, attachmentClearValues, renderPassAttachmentOps, attachmentClearValues,
commandBufferOut); commandBufferOut);
...@@ -1362,9 +1234,48 @@ angle::Result FramebufferVk::readPixelsImpl(ContextVk *contextVk, ...@@ -1362,9 +1234,48 @@ angle::Result FramebufferVk::readPixelsImpl(ContextVk *contextVk,
gl::Extents FramebufferVk::getReadImageExtents() const gl::Extents FramebufferVk::getReadImageExtents() const
{ {
ASSERT(getColorReadRenderTarget()->getExtents().width == mState.getDimensions().width);
ASSERT(getColorReadRenderTarget()->getExtents().height == mState.getDimensions().height);
return getColorReadRenderTarget()->getExtents(); return getColorReadRenderTarget()->getExtents();
} }
gl::Rectangle FramebufferVk::getCompleteRenderArea() const
{
return gl::Rectangle(0, 0, mState.getDimensions().width, mState.getDimensions().height);
}
gl::Rectangle FramebufferVk::getScissoredRenderArea(ContextVk *contextVk) const
{
const gl::Rectangle renderArea(0, 0, mState.getDimensions().width,
mState.getDimensions().height);
bool invertViewport = contextVk->isViewportFlipEnabledForDrawFBO();
return ClipRectToScissor(contextVk->getState(), renderArea, invertViewport);
}
void FramebufferVk::onScissorChange(ContextVk *contextVk)
{
gl::Rectangle scissoredRenderArea = getScissoredRenderArea(contextVk);
// If the scissor has grown beyond the previous scissoredRenderArea, make sure the render pass
// is restarted. Otherwise, we can continue using the same renderpass area.
//
// Without a scissor, the render pass area covers the whole of the framebuffer. With a
// scissored clear, the render pass area could be smaller than the framebuffer size. When the
// scissor changes, if the scissor area is completely encompassed by the render pass area, it's
// possible to continue using the same render pass. However, if the current render pass area
// is too small, we need to start a new one. The latter can happen if a scissored clear starts
// a render pass, the scissor is disabled and a draw call is issued to affect the whole
// framebuffer.
mFramebuffer.updateQueueSerial(contextVk->getRenderer()->getCurrentQueueSerial());
if (mFramebuffer.hasStartedRenderPass() &&
!mFramebuffer.getRenderPassRenderArea().encloses(scissoredRenderArea))
{
mFramebuffer.finishCurrentCommands(contextVk->getRenderer());
}
}
RenderTargetVk *FramebufferVk::getFirstRenderTarget() const RenderTargetVk *FramebufferVk::getFirstRenderTarget() const
{ {
for (auto *renderTarget : mRenderTargetCache.getColors()) for (auto *renderTarget : mRenderTargetCache.getColors())
......
...@@ -101,19 +101,28 @@ class FramebufferVk : public FramebufferImpl ...@@ -101,19 +101,28 @@ class FramebufferVk : public FramebufferImpl
void *pixels); void *pixels);
gl::Extents getReadImageExtents() const; gl::Extents getReadImageExtents() const;
gl::Rectangle getCompleteRenderArea() const;
gl::Rectangle getScissoredRenderArea(ContextVk *contextVk) const;
void onScissorChange(ContextVk *contextVk);
const gl::DrawBufferMask &getEmulatedAlphaAttachmentMask() const; const gl::DrawBufferMask &getEmulatedAlphaAttachmentMask() const;
RenderTargetVk *getColorReadRenderTarget() const; RenderTargetVk *getColorReadRenderTarget() const;
// This will clear the current write operation if it is complete. // This will clear the current write operation if it is complete.
bool appendToStartedRenderPass(Serial currentQueueSerial, vk::CommandBuffer **commandBufferOut) bool appendToStartedRenderPass(Serial currentQueueSerial,
const gl::Rectangle &renderArea,
vk::CommandBuffer **commandBufferOut)
{ {
return mFramebuffer.appendToStartedRenderPass(currentQueueSerial, commandBufferOut); return mFramebuffer.appendToStartedRenderPass(currentQueueSerial, renderArea,
commandBufferOut);
} }
vk::FramebufferHelper *getFramebuffer() { return &mFramebuffer; } vk::FramebufferHelper *getFramebuffer() { return &mFramebuffer; }
angle::Result startNewRenderPass(ContextVk *context, vk::CommandBuffer **commandBufferOut); angle::Result startNewRenderPass(ContextVk *context,
const gl::Rectangle &renderArea,
vk::CommandBuffer **commandBufferOut);
RenderTargetVk *getFirstRenderTarget() const; RenderTargetVk *getFirstRenderTarget() const;
GLint getSamples() const; GLint getSamples() const;
...@@ -125,11 +134,6 @@ class FramebufferVk : public FramebufferImpl ...@@ -125,11 +134,6 @@ class FramebufferVk : public FramebufferImpl
const gl::FramebufferState &state, const gl::FramebufferState &state,
WindowSurfaceVk *backbuffer); WindowSurfaceVk *backbuffer);
// Helper for appendToStarted/else startNewRenderPass.
angle::Result getCommandBufferForDraw(ContextVk *contextVk,
vk::CommandBuffer **commandBufferOut,
vk::RecordingMode *modeOut);
// The 'in' rectangles must be clipped to the scissor and FBO. The clipping is done in 'blit'. // The 'in' rectangles must be clipped to the scissor and FBO. The clipping is done in 'blit'.
angle::Result blitWithCommand(ContextVk *contextVk, angle::Result blitWithCommand(ContextVk *contextVk,
const gl::Rectangle &readRectIn, const gl::Rectangle &readRectIn,
...@@ -166,18 +170,14 @@ class FramebufferVk : public FramebufferImpl ...@@ -166,18 +170,14 @@ class FramebufferVk : public FramebufferImpl
const VkClearColorValue &clearColorValue, const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue); const VkClearDepthStencilValue &clearDepthStencilValue);
angle::Result clearWithRenderPassOp(ContextVk *contextVk, angle::Result clearWithRenderPassOp(ContextVk *contextVk,
gl::DrawBufferMask clearColorBuffers, const gl::Rectangle &clearArea,
bool clearDepth,
bool clearStencil,
const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue);
angle::Result clearWithClearAttachments(ContextVk *contextVk,
gl::DrawBufferMask clearColorBuffers, gl::DrawBufferMask clearColorBuffers,
bool clearDepth, bool clearDepth,
bool clearStencil, bool clearStencil,
const VkClearColorValue &clearColorValue, const VkClearColorValue &clearColorValue,
const VkClearDepthStencilValue &clearDepthStencilValue); const VkClearDepthStencilValue &clearDepthStencilValue);
angle::Result clearWithDraw(ContextVk *contextVk, angle::Result clearWithDraw(ContextVk *contextVk,
const gl::Rectangle &clearArea,
gl::DrawBufferMask clearColorBuffers, gl::DrawBufferMask clearColorBuffers,
bool clearDepth, bool clearDepth,
bool clearStencil, bool clearStencil,
......
...@@ -668,10 +668,13 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk, ...@@ -668,10 +668,13 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk,
ANGLE_TRY(ensureImageClearResourcesInitialized(contextVk)); ANGLE_TRY(ensureImageClearResourcesInitialized(contextVk));
const gl::Rectangle &scissoredRenderArea = params.clearArea;
vk::CommandBuffer *commandBuffer; vk::CommandBuffer *commandBuffer;
if (!framebuffer->appendToStartedRenderPass(renderer->getCurrentQueueSerial(), &commandBuffer)) if (!framebuffer->appendToStartedRenderPass(renderer->getCurrentQueueSerial(),
scissoredRenderArea, &commandBuffer))
{ {
ANGLE_TRY(framebuffer->startNewRenderPass(contextVk, &commandBuffer)); ANGLE_TRY(framebuffer->startNewRenderPass(contextVk, scissoredRenderArea, &commandBuffer));
} }
FullScreenQuadParams vsParams; FullScreenQuadParams vsParams;
...@@ -716,17 +719,14 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk, ...@@ -716,17 +719,14 @@ angle::Result UtilsVk::clearFramebuffer(ContextVk *contextVk,
pipelineDesc.setStencilBackWriteMask(params.stencilMask); pipelineDesc.setStencilBackWriteMask(params.stencilMask);
} }
const gl::Rectangle &renderArea = framebuffer->getFramebuffer()->getRenderPassRenderArea();
bool invertViewport = contextVk->isViewportFlipEnabledForDrawFBO();
VkViewport viewport; VkViewport viewport;
gl_vk::GetViewport(renderArea, 0.0f, 1.0f, invertViewport, params.renderAreaHeight, &viewport); gl::Rectangle completeRenderArea = framebuffer->getCompleteRenderArea();
bool invertViewport = contextVk->isViewportFlipEnabledForDrawFBO();
gl_vk::GetViewport(completeRenderArea, 0.0f, 1.0f, invertViewport, params.renderAreaHeight,
&viewport);
pipelineDesc.setViewport(viewport); pipelineDesc.setViewport(viewport);
VkRect2D scissor; pipelineDesc.setScissor(gl_vk::GetRect(params.clearArea));
const gl::State &glState = contextVk->getState();
gl_vk::GetScissor(glState, invertViewport, renderArea, &scissor);
pipelineDesc.setScissor(scissor);
vk::ShaderLibrary &shaderLibrary = renderer->getShaderLibrary(); vk::ShaderLibrary &shaderLibrary = renderer->getShaderLibrary();
vk::RefCounted<vk::ShaderAndSerial> *vertexShader = nullptr; vk::RefCounted<vk::ShaderAndSerial> *vertexShader = nullptr;
...@@ -875,4 +875,18 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk, ...@@ -875,4 +875,18 @@ angle::Result UtilsVk::copyImage(ContextVk *contextVk,
return angle::Result::Continue; return angle::Result::Continue;
} }
UtilsVk::ClearFramebufferParameters::ClearFramebufferParameters()
: renderPassDesc(nullptr),
renderAreaHeight(0),
clearColor(false),
clearDepth(false),
clearStencil(false),
stencilMask(0),
colorMaskFlags(0),
colorAttachmentIndex(0),
colorFormat(nullptr),
colorClearValue{},
depthStencilClearValue{}
{}
} // namespace rx } // namespace rx
...@@ -61,8 +61,13 @@ class UtilsVk : angle::NonCopyable ...@@ -61,8 +61,13 @@ class UtilsVk : angle::NonCopyable
struct ClearFramebufferParameters struct ClearFramebufferParameters
{ {
// Satisfy chromium-style with a constructor that does what = {} was already doing in a
// safer way.
ClearFramebufferParameters();
const vk::RenderPassDesc *renderPassDesc; const vk::RenderPassDesc *renderPassDesc;
GLint renderAreaHeight; GLint renderAreaHeight;
gl::Rectangle clearArea;
bool clearColor; bool clearColor;
bool clearDepth; bool clearDepth;
......
...@@ -33,11 +33,6 @@ VkImageUsageFlags GetStagingBufferUsageFlags(rx::vk::StagingUsage usage) ...@@ -33,11 +33,6 @@ VkImageUsageFlags GetStagingBufferUsageFlags(rx::vk::StagingUsage usage)
return 0; return 0;
} }
} }
constexpr gl::Rectangle kMaxSizedScissor(0,
0,
std::numeric_limits<int>::max(),
std::numeric_limits<int>::max());
} // anonymous namespace } // anonymous namespace
namespace angle namespace angle
...@@ -826,36 +821,5 @@ void GetViewport(const gl::Rectangle &viewport, ...@@ -826,36 +821,5 @@ void GetViewport(const gl::Rectangle &viewport,
viewportOut->height = -viewportOut->height; viewportOut->height = -viewportOut->height;
} }
} }
void GetScissor(const gl::State &glState,
bool invertViewport,
const gl::Rectangle &renderArea,
VkRect2D *scissorOut)
{
if (glState.isScissorTestEnabled())
{
gl::Rectangle clippedRect;
if (!gl::ClipRectangle(glState.getScissor(), renderArea, &clippedRect))
{
memset(scissorOut, 0, sizeof(VkRect2D));
return;
}
*scissorOut = gl_vk::GetRect(clippedRect);
if (invertViewport)
{
scissorOut->offset.y =
renderArea.height - scissorOut->offset.y - scissorOut->extent.height;
}
}
else
{
// If the scissor test isn't enabled, we can simply use a really big scissor that's
// certainly larger than the current surface using the maximum size of a 2D texture
// for the width and height.
*scissorOut = gl_vk::GetRect(kMaxSizedScissor);
}
}
} // namespace gl_vk } // namespace gl_vk
} // namespace rx } // namespace rx
...@@ -529,10 +529,6 @@ void GetViewport(const gl::Rectangle &viewport, ...@@ -529,10 +529,6 @@ void GetViewport(const gl::Rectangle &viewport,
bool invertViewport, bool invertViewport,
GLint renderAreaHeight, GLint renderAreaHeight,
VkViewport *viewportOut); VkViewport *viewportOut);
void GetScissor(const gl::State &glState,
bool invertViewport,
const gl::Rectangle &renderArea,
VkRect2D *scissorOut);
} // namespace gl_vk } // namespace gl_vk
} // namespace rx } // namespace rx
......
...@@ -825,7 +825,7 @@ TEST_P(ClearTestES3, MaskedScissoredClearMultipleAttachments) ...@@ -825,7 +825,7 @@ TEST_P(ClearTestES3, MaskedScissoredClearMultipleAttachments)
glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
glScissor(kSize / 4, kSize / 4, kSize / 2, kSize / 2); glScissor(kSize / 6, kSize / 6, kSize / 3, kSize / 3);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
ASSERT_GL_NO_ERROR(); ASSERT_GL_NO_ERROR();
...@@ -840,8 +840,11 @@ TEST_P(ClearTestES3, MaskedScissoredClearMultipleAttachments) ...@@ -840,8 +840,11 @@ TEST_P(ClearTestES3, MaskedScissoredClearMultipleAttachments)
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, clearColorMasked); EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, clearColorMasked); EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, clearColorMasked); EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(kSize / 3, 2 * kSize / 3, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3, kSize / 3, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3, 2 * kSize / 3, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, clearColorMaskedScissored); EXPECT_PIXEL_COLOR_EQ(kSize / 3, kSize / 3, clearColorMaskedScissored);
} }
// Scissored clear // Scissored clear
...@@ -863,8 +866,11 @@ TEST_P(ClearTestES3, MaskedScissoredClearMultipleAttachments) ...@@ -863,8 +866,11 @@ TEST_P(ClearTestES3, MaskedScissoredClearMultipleAttachments)
EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, clearColorMasked); EXPECT_PIXEL_COLOR_EQ(0, kSize - 1, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, clearColorMasked); EXPECT_PIXEL_COLOR_EQ(kSize - 1, 0, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, clearColorMasked); EXPECT_PIXEL_COLOR_EQ(kSize - 1, kSize - 1, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(kSize / 3, 2 * kSize / 3, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3, kSize / 3, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(2 * kSize / 3, 2 * kSize / 3, clearColorMasked);
EXPECT_PIXEL_COLOR_EQ(kSize / 2, kSize / 2, clearColorScissored); EXPECT_PIXEL_COLOR_EQ(kSize / 3, kSize / 3, clearColorScissored);
} }
} }
...@@ -1239,8 +1245,8 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear( ...@@ -1239,8 +1245,8 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear(
const int w = getWindowWidth(); const int w = getWindowWidth();
const int h = getWindowHeight(); const int h = getWindowHeight();
const int whalf = w >> 1; const int wthird = w / 3;
const int hhalf = h >> 1; const int hthird = h / 3;
constexpr float kPreClearDepth = 0.9f; constexpr float kPreClearDepth = 0.9f;
constexpr float kClearDepth = 0.5f; constexpr float kClearDepth = 0.5f;
...@@ -1283,7 +1289,7 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear( ...@@ -1283,7 +1289,7 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear(
if (scissor) if (scissor)
{ {
glEnable(GL_SCISSOR_TEST); glEnable(GL_SCISSOR_TEST);
glScissor(whalf / 2, hhalf / 2, whalf, hhalf); glScissor(wthird / 2, hthird / 2, wthird, hthird);
} }
// Use color and stencil masks to clear to a second color, 0.5 depth and 0x59 stencil. // Use color and stencil masks to clear to a second color, 0.5 depth and 0x59 stencil.
...@@ -1325,12 +1331,15 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear( ...@@ -1325,12 +1331,15 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear(
GLColor expectedCornerColorRGB = scissor ? color1RGB : expectedCenterColorRGB; GLColor expectedCornerColorRGB = scissor ? color1RGB : expectedCenterColorRGB;
// Verify second clear color mask worked as expected. // Verify second clear color mask worked as expected.
EXPECT_PIXEL_COLOR_NEAR(whalf, hhalf, expectedCenterColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(wthird, hthird, expectedCenterColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(w - 1, 0, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(w - 1, 0, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(0, h - 1, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(0, h - 1, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(wthird, 2 * hthird, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(2 * wthird, hthird, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(2 * wthird, 2 * hthird, expectedCornerColorRGB, 1);
// If there is depth, but depth is not asked to be cleared, the depth buffer contains garbage, // If there is depth, but depth is not asked to be cleared, the depth buffer contains garbage,
// so no particular behavior can be expected. // so no particular behavior can be expected.
...@@ -1356,12 +1365,15 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear( ...@@ -1356,12 +1365,15 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear(
expectedCornerColorRGB = expectedCornerColorRGB =
mHasDepth && scissor && !maskDepth ? expectedCornerColorRGB : GLColor::blue; mHasDepth && scissor && !maskDepth ? expectedCornerColorRGB : GLColor::blue;
EXPECT_PIXEL_COLOR_NEAR(whalf, hhalf, expectedCenterColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(wthird, hthird, expectedCenterColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(w - 1, 0, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(w - 1, 0, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(0, h - 1, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(0, h - 1, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(wthird, 2 * hthird, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(2 * wthird, hthird, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(2 * wthird, 2 * hthird, expectedCornerColorRGB, 1);
} }
// If there is stencil, but it's not asked to be cleared, there is similarly no expectation. // If there is stencil, but it's not asked to be cleared, there is similarly no expectation.
...@@ -1387,12 +1399,15 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear( ...@@ -1387,12 +1399,15 @@ void MaskedScissoredClearTestBase::MaskedScissoredColorDepthStencilClear(
// If there is no stencil, stencil test always passes so the whole image must be green. // If there is no stencil, stencil test always passes so the whole image must be green.
expectedCornerColorRGB = mHasStencil && scissor ? expectedCornerColorRGB : GLColor::green; expectedCornerColorRGB = mHasStencil && scissor ? expectedCornerColorRGB : GLColor::green;
EXPECT_PIXEL_COLOR_NEAR(whalf, hhalf, expectedCenterColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(wthird, hthird, expectedCenterColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(0, 0, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(w - 1, 0, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(w - 1, 0, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(0, h - 1, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(0, h - 1, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, expectedCornerColorRGB, 1); EXPECT_PIXEL_COLOR_NEAR(w - 1, h - 1, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(wthird, 2 * hthird, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(2 * wthird, hthird, expectedCornerColorRGB, 1);
EXPECT_PIXEL_COLOR_NEAR(2 * wthird, 2 * hthird, expectedCornerColorRGB, 1);
} }
} }
......
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