Commit 0716ce0a by Jonah Ryan-Davis Committed by Commit Bot

Re-add case to blitframebuffer workaround where src is outside of bounds.

On Mac, blitFramebuffer calls fail if the source region is not enclosed by the framebuffer. In this case, we must naively clip the source region and adjust the dest region accordingly. This is slightly different behavior and may cause issues with scaling so we use a separate workaround. Also, Windows NVIDIA has a driver bug that affects Vulkan device creation after blitting large textures, so it should be included in the original workaround. This CL cleans up the workaround to use more helpers from ANGLE and to generally improve readability. Bug: chromium:830046 Change-Id: I50bd97449725b738036e6bd3af82362020d7eda8 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1713090 Commit-Queue: Jonah Ryan-Davis <jonahr@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent f002a637
...@@ -331,6 +331,13 @@ struct FeaturesGL : FeatureSetBase ...@@ -331,6 +331,13 @@ struct FeaturesGL : FeatureSetBase
"adjust_src_dst_region_for_blitframebuffer", FeatureCategory::OpenGLWorkarounds, "adjust_src_dst_region_for_blitframebuffer", FeatureCategory::OpenGLWorkarounds,
"Many platforms have issues with blitFramebuffer when the parameters are large.", &members, "Many platforms have issues with blitFramebuffer when the parameters are large.", &members,
"http://crbug.com/830046"}; "http://crbug.com/830046"};
// BlitFramebuffer has issues on Mac when the source bounds aren't enclosed by the framebuffer.
// This workaround clips the source region and adjust the dest region proportionally.
Feature clipSrcRegionBlitFramebuffer = {
"clip_src_region_for_blitframebuffer", FeatureCategory::OpenGLWorkarounds,
"Mac has issues with blitFramebuffer when the parameters don't match the framebuffer size.",
&members, "http://crbug.com/830046"};
}; };
inline FeaturesGL::FeaturesGL() = default; inline FeaturesGL::FeaturesGL() = default;
......
...@@ -37,6 +37,42 @@ namespace rx ...@@ -37,6 +37,42 @@ namespace rx
namespace namespace
{ {
struct BlitFramebufferBounds
{
gl::Rectangle sourceBounds;
gl::Rectangle sourceRegion;
gl::Rectangle destBounds;
gl::Rectangle destRegion;
bool xFlipped;
bool yFlipped;
};
static BlitFramebufferBounds GetBlitFramebufferBounds(const gl::Context *context,
const gl::Rectangle &sourceArea,
const gl::Rectangle &destArea)
{
BlitFramebufferBounds bounds;
const Framebuffer *sourceFramebuffer = context->getState().getReadFramebuffer();
const Framebuffer *destFramebuffer = context->getState().getDrawFramebuffer();
gl::Extents readSize = sourceFramebuffer->getExtents();
gl::Extents drawSize = destFramebuffer->getExtents();
bounds.sourceBounds = gl::Rectangle(0, 0, readSize.width, readSize.height);
bounds.sourceRegion = sourceArea.removeReversal();
bounds.destBounds = gl::Rectangle(0, 0, drawSize.width, drawSize.height);
bounds.destRegion = destArea.removeReversal();
bounds.xFlipped = sourceArea.isReversedX() != destArea.isReversedX();
bounds.yFlipped = sourceArea.isReversedY() != destArea.isReversedY();
return bounds;
}
void BindFramebufferAttachment(const FunctionsGL *functions, void BindFramebufferAttachment(const FunctionsGL *functions,
GLenum attachmentPoint, GLenum attachmentPoint,
const FramebufferAttachment *attachment) const FramebufferAttachment *attachment)
...@@ -584,26 +620,32 @@ angle::Result FramebufferGL::blit(const gl::Context *context, ...@@ -584,26 +620,32 @@ angle::Result FramebufferGL::blit(const gl::Context *context,
stateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID()); stateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, sourceFramebufferGL->getFramebufferID());
stateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID); stateManager->bindFramebuffer(GL_DRAW_FRAMEBUFFER, mFramebufferID);
gl::Rectangle finalSourceArea(sourceArea);
gl::Rectangle finalDestArea(destArea);
if (features.adjustSrcDstRegionBlitFramebuffer.enabled) if (features.adjustSrcDstRegionBlitFramebuffer.enabled)
{ {
gl::Rectangle newSourceArea; angle::Result result =
gl::Rectangle newDestArea; adjustSrcDstRegion(context, sourceArea, destArea, &finalSourceArea, &finalDestArea);
// This workaround is taken from chromium: http://crbug.com/830046 if (result != angle::Result::Continue)
if (adjustSrcDstRegion(context, sourceArea, destArea, &newSourceArea, &newDestArea) ==
angle::Result::Continue)
{ {
functions->blitFramebuffer(newSourceArea.x, newSourceArea.y, newSourceArea.x1(), return result;
newSourceArea.y1(), newDestArea.x, newDestArea.y,
newDestArea.x1(), newDestArea.y1(), blitMask, filter);
} }
} }
else if (features.clipSrcRegionBlitFramebuffer.enabled)
{ {
functions->blitFramebuffer(sourceArea.x, sourceArea.y, sourceArea.x1(), sourceArea.y1(), angle::Result result =
destArea.x, destArea.y, destArea.x1(), destArea.y1(), blitMask, clipSrcRegion(context, sourceArea, destArea, &finalSourceArea, &finalDestArea);
filter); if (result != angle::Result::Continue)
{
return result;
}
} }
functions->blitFramebuffer(finalSourceArea.x, finalSourceArea.y, finalSourceArea.x1(),
finalSourceArea.y1(), finalDestArea.x, finalDestArea.y,
finalDestArea.x1(), finalDestArea.y1(), blitMask, filter);
return angle::Result::Continue; return angle::Result::Continue;
} }
...@@ -613,54 +655,19 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -613,54 +655,19 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
gl::Rectangle *newSourceArea, gl::Rectangle *newSourceArea,
gl::Rectangle *newDestArea) gl::Rectangle *newDestArea)
{ {
const Framebuffer *sourceFramebuffer = context->getState().getReadFramebuffer(); BlitFramebufferBounds bounds = GetBlitFramebufferBounds(context, sourceArea, destArea);
const Framebuffer *destFramebuffer = context->getState().getDrawFramebuffer();
gl::Extents readSize = sourceFramebuffer->getExtents();
gl::Extents drawSize = destFramebuffer->getExtents();
CheckedNumeric<GLint> sourceWidthTemp = sourceArea.x1(); if (bounds.destRegion.width == 0 || bounds.sourceRegion.width == 0 ||
sourceWidthTemp -= sourceArea.x; bounds.destRegion.height == 0 || bounds.sourceRegion.height == 0)
CheckedNumeric<GLint> sourceHeightTemp = sourceArea.y1();
sourceHeightTemp -= sourceArea.y;
CheckedNumeric<GLint> destWidthTemp = destArea.x1();
destWidthTemp -= destArea.x;
CheckedNumeric<GLint> destHeightTemp = destArea.y1();
destHeightTemp -= destArea.y;
GLint sourceX = sourceArea.x1() > sourceArea.x ? sourceArea.x : sourceArea.x1();
GLint sourceY = sourceArea.y1() > sourceArea.y ? sourceArea.y : sourceArea.y1();
GLuint sourceWidth = angle::base::checked_cast<GLuint>(sourceWidthTemp.Abs().ValueOrDefault(0));
GLuint sourceHeight =
angle::base::checked_cast<GLuint>(sourceHeightTemp.Abs().ValueOrDefault(0));
GLint destX = destArea.x1() > destArea.x ? destArea.x : destArea.x1();
GLint destY = destArea.y1() > destArea.y ? destArea.y : destArea.y1();
GLuint destWidth = angle::base::checked_cast<GLuint>(destWidthTemp.Abs().ValueOrDefault(0));
GLuint destHeight = angle::base::checked_cast<GLuint>(destHeightTemp.Abs().ValueOrDefault(0));
if (destWidth == 0 || sourceWidth == 0 || destHeight == 0 || sourceHeight == 0)
{ {
return angle::Result::Stop; return angle::Result::Stop;
} }
if (!ClipRectangle(bounds.destBounds, bounds.destRegion, nullptr))
gl::Rectangle sourceBounds(0, 0, readSize.width, readSize.height);
gl::Rectangle sourceRegion(sourceX, sourceY, sourceWidth, sourceHeight);
gl::Rectangle destBounds(0, 0, drawSize.width, drawSize.height);
gl::Rectangle destRegion(destX, destY, destWidth, destHeight);
if (!ClipRectangle(destBounds, destRegion, nullptr))
{ {
return angle::Result::Stop; return angle::Result::Stop;
} }
bool xFlipped = ((sourceArea.x1() > sourceArea.x) && (destArea.x1() < destArea.x)) || if (!bounds.destBounds.encloses(bounds.destRegion))
((sourceArea.x1() < sourceArea.x) && (destArea.x1() > destArea.x));
bool yFlipped = ((sourceArea.y1() > sourceArea.y) && (destArea.y1() < destArea.y)) ||
((sourceArea.y1() < sourceArea.y) && (destArea.y1() > destArea.y));
if (!destBounds.encloses(destRegion))
{ {
// destRegion is not within destBounds. We want to adjust it to a // destRegion is not within destBounds. We want to adjust it to a
// reasonable size. This is done by halving the destRegion until it is at // reasonable size. This is done by halving the destRegion until it is at
...@@ -672,18 +679,18 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -672,18 +679,18 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
GLuint destXHalvings = 0; GLuint destXHalvings = 0;
GLuint destYHalvings = 0; GLuint destYHalvings = 0;
GLint destOriginX = destX; GLint destOriginX = bounds.destRegion.x;
GLint destOriginY = destY; GLint destOriginY = bounds.destRegion.y;
GLint destClippedWidth = destRegion.width; GLint destClippedWidth = bounds.destRegion.width;
while (destClippedWidth > 2 * destBounds.width) while (destClippedWidth > 2 * bounds.destBounds.width)
{ {
destClippedWidth = destClippedWidth / 2; destClippedWidth = destClippedWidth / 2;
destXHalvings++; destXHalvings++;
} }
GLint destClippedHeight = destRegion.height; GLint destClippedHeight = bounds.destRegion.height;
while (destClippedHeight > 2 * destBounds.height) while (destClippedHeight > 2 * bounds.destBounds.height)
{ {
destClippedHeight = destClippedHeight / 2; destClippedHeight = destClippedHeight / 2;
destYHalvings++; destYHalvings++;
...@@ -694,18 +701,18 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -694,18 +701,18 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
// scaled dimensions but the new region has the same intersection as the // scaled dimensions but the new region has the same intersection as the
// original region. // original region.
GLint left = destRegion.x0(); GLint left = bounds.destRegion.x0();
GLint right = destRegion.x1(); GLint right = bounds.destRegion.x1();
GLint top = destRegion.y0(); GLint top = bounds.destRegion.y0();
GLint bottom = destRegion.y1(); GLint bottom = bounds.destRegion.y1();
GLint extraXOffset = 0; GLint extraXOffset = 0;
if (left >= 0 && left < destBounds.width) if (left >= 0 && left < bounds.destBounds.width)
{ {
// Left edge is in-bounds // Left edge is in-bounds
destOriginX = destX; destOriginX = bounds.destRegion.x;
} }
else if (right > 0 && right <= destBounds.width) else if (right > 0 && right <= bounds.destBounds.width)
{ {
// Right edge is in-bounds // Right edge is in-bounds
destOriginX = right - destClippedWidth; destOriginX = right - destClippedWidth;
...@@ -713,17 +720,17 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -713,17 +720,17 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
else else
{ {
// Region completely spans bounds // Region completely spans bounds
extraXOffset = (destRegion.width - destClippedWidth) / 2; extraXOffset = (bounds.destRegion.width - destClippedWidth) / 2;
destOriginX = destX + extraXOffset; destOriginX = bounds.destRegion.x + extraXOffset;
} }
GLint extraYOffset = 0; GLint extraYOffset = 0;
if (top >= 0 && top < destBounds.height) if (top >= 0 && top < bounds.destBounds.height)
{ {
// Top edge is in-bounds // Top edge is in-bounds
destOriginY = destY; destOriginY = bounds.destRegion.y;
} }
else if (bottom > 0 && bottom <= destBounds.height) else if (bottom > 0 && bottom <= bounds.destBounds.height)
{ {
// Bottom edge is in-bounds // Bottom edge is in-bounds
destOriginY = bottom - destClippedHeight; destOriginY = bottom - destClippedHeight;
...@@ -731,29 +738,29 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -731,29 +738,29 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
else else
{ {
// Region completely spans bounds // Region completely spans bounds
extraYOffset = (destRegion.height - destClippedHeight) / 2; extraYOffset = (bounds.destRegion.height - destClippedHeight) / 2;
destOriginY = destY + extraYOffset; destOriginY = bounds.destRegion.y + extraYOffset;
} }
destRegion = gl::Rectangle(destOriginX, destOriginY, destClippedWidth, destClippedHeight);
// Offsets from the bottom left corner of the original region to // Offsets from the bottom left corner of the original region to
// the bottom left corner of the clipped region. // the bottom left corner of the clipped region.
// This value (after it is scaled) is the respective offset we will apply // This value (after it is scaled) is the respective offset we will apply
// to the src origin. // to the src origin.
CheckedNumeric<GLuint> checkedXOffset(destRegion.x - destX - extraXOffset / 2); CheckedNumeric<GLuint> checkedXOffset(destOriginX - bounds.destRegion.x - extraXOffset / 2);
CheckedNumeric<GLuint> checkedYOffset(destRegion.y - destY - extraYOffset / 2); CheckedNumeric<GLuint> checkedYOffset(destOriginY - bounds.destRegion.y - extraYOffset / 2);
// if X/Y is reversed, use the top/right out-of-bounds region to compute // if X/Y is reversed, use the top/right out-of-bounds region to compute
// the origin offset instead of the left/bottom out-of-bounds region // the origin offset instead of the left/bottom out-of-bounds region
if (xFlipped) if (bounds.xFlipped)
{ {
checkedXOffset = (destX + destWidth - destRegion.x1() + extraXOffset / 2); checkedXOffset =
(bounds.destRegion.x1() - (destOriginX + destClippedWidth) + extraXOffset / 2);
} }
if (yFlipped) if (bounds.yFlipped)
{ {
checkedYOffset = (destY + destHeight - destRegion.y1() + extraYOffset / 2); checkedYOffset =
(bounds.destRegion.y1() - (destOriginY + destClippedHeight) + extraYOffset / 2);
} }
// These offsets should never overflow // These offsets should never overflow
...@@ -764,23 +771,27 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -764,23 +771,27 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
return angle::Result::Stop; return angle::Result::Stop;
} }
bounds.destRegion =
gl::Rectangle(destOriginX, destOriginY, destClippedWidth, destClippedHeight);
// Adjust the src region by the same factor // Adjust the src region by the same factor
sourceRegion = gl::Rectangle( bounds.sourceRegion = gl::Rectangle(bounds.sourceRegion.x + (xOffset >> destXHalvings),
sourceX + (xOffset >> destXHalvings), sourceY + (yOffset >> destYHalvings), bounds.sourceRegion.y + (yOffset >> destYHalvings),
sourceRegion.width >> destXHalvings, sourceRegion.height >> destYHalvings); bounds.sourceRegion.width >> destXHalvings,
bounds.sourceRegion.height >> destYHalvings);
// if the src was scaled to 0, set it to 1 so the src is non-empty // if the src was scaled to 0, set it to 1 so the src is non-empty
if (sourceRegion.width == 0) if (bounds.sourceRegion.width == 0)
{ {
sourceRegion.width = 1; bounds.sourceRegion.width = 1;
} }
if (sourceRegion.height == 0) if (bounds.sourceRegion.height == 0)
{ {
sourceRegion.height = 1; bounds.sourceRegion.height = 1;
} }
} }
if (!sourceBounds.encloses(sourceRegion)) if (!bounds.sourceBounds.encloses(bounds.sourceRegion))
{ {
// sourceRegion is not within sourceBounds. We want to adjust it to a // sourceRegion is not within sourceBounds. We want to adjust it to a
// reasonable size. This is done by halving the sourceRegion until it is at // reasonable size. This is done by halving the sourceRegion until it is at
...@@ -792,18 +803,18 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -792,18 +803,18 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
GLuint sourceXHalvings = 0; GLuint sourceXHalvings = 0;
GLuint sourceYHalvings = 0; GLuint sourceYHalvings = 0;
GLint sourceOriginX = sourceX; GLint sourceOriginX = bounds.sourceRegion.x;
GLint sourceOriginY = sourceY; GLint sourceOriginY = bounds.sourceRegion.y;
GLint sourceClippedWidth = sourceRegion.width; GLint sourceClippedWidth = bounds.sourceRegion.width;
while (sourceClippedWidth > 2 * sourceBounds.width) while (sourceClippedWidth > 2 * bounds.sourceBounds.width)
{ {
sourceClippedWidth = sourceClippedWidth / 2; sourceClippedWidth = sourceClippedWidth / 2;
sourceXHalvings++; sourceXHalvings++;
} }
GLint sourceClippedHeight = sourceRegion.height; GLint sourceClippedHeight = bounds.sourceRegion.height;
while (sourceClippedHeight > 2 * sourceBounds.height) while (sourceClippedHeight > 2 * bounds.sourceBounds.height)
{ {
sourceClippedHeight = sourceClippedHeight / 2; sourceClippedHeight = sourceClippedHeight / 2;
sourceYHalvings++; sourceYHalvings++;
...@@ -814,18 +825,18 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -814,18 +825,18 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
// scaled dimensions but the new region has the same intersection as the // scaled dimensions but the new region has the same intersection as the
// original region. // original region.
GLint left = sourceRegion.x0(); GLint left = bounds.sourceRegion.x0();
GLint right = sourceRegion.x1(); GLint right = bounds.sourceRegion.x1();
GLint top = sourceRegion.y0(); GLint top = bounds.sourceRegion.y0();
GLint bottom = sourceRegion.y1(); GLint bottom = bounds.sourceRegion.y1();
GLint extraXOffset = 0; GLint extraXOffset = 0;
if (left >= 0 && left < sourceBounds.width) if (left >= 0 && left < bounds.sourceBounds.width)
{ {
// Left edge is in-bounds // Left edge is in-bounds
sourceOriginX = sourceX; sourceOriginX = bounds.sourceRegion.x;
} }
else if (right > 0 && right <= sourceBounds.width) else if (right > 0 && right <= bounds.sourceBounds.width)
{ {
// Right edge is in-bounds // Right edge is in-bounds
sourceOriginX = right - sourceClippedWidth; sourceOriginX = right - sourceClippedWidth;
...@@ -833,17 +844,17 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -833,17 +844,17 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
else else
{ {
// Region completely spans bounds // Region completely spans bounds
extraXOffset = (sourceRegion.width - sourceClippedWidth) / 2; extraXOffset = (bounds.sourceRegion.width - sourceClippedWidth) / 2;
sourceOriginX = sourceX + extraXOffset; sourceOriginX = bounds.sourceRegion.x + extraXOffset;
} }
GLint extraYOffset = 0; GLint extraYOffset = 0;
if (top >= 0 && top < sourceBounds.height) if (top >= 0 && top < bounds.sourceBounds.height)
{ {
// Top edge is in-bounds // Top edge is in-bounds
sourceOriginY = sourceY; sourceOriginY = bounds.sourceRegion.y;
} }
else if (bottom > 0 && bottom <= sourceBounds.height) else if (bottom > 0 && bottom <= bounds.sourceBounds.height)
{ {
// Bottom edge is in-bounds // Bottom edge is in-bounds
sourceOriginY = bottom - sourceClippedHeight; sourceOriginY = bottom - sourceClippedHeight;
...@@ -851,30 +862,31 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -851,30 +862,31 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
else else
{ {
// Region completely spans bounds // Region completely spans bounds
extraYOffset = (sourceRegion.height - sourceClippedHeight) / 2; extraYOffset = (bounds.sourceRegion.height - sourceClippedHeight) / 2;
sourceOriginY = sourceY + extraYOffset; sourceOriginY = bounds.sourceRegion.y + extraYOffset;
} }
sourceRegion =
gl::Rectangle(sourceOriginX, sourceOriginY, sourceClippedWidth, sourceClippedHeight);
// Offsets from the bottom left corner of the original region to // Offsets from the bottom left corner of the original region to
// the bottom left corner of the clipped region. // the bottom left corner of the clipped region.
// This value (after it is scaled) is the respective offset we will apply // This value (after it is scaled) is the respective offset we will apply
// to the dest origin. // to the dest origin.
CheckedNumeric<GLuint> checkedXOffset(sourceRegion.x - sourceX - extraXOffset / 2); CheckedNumeric<GLuint> checkedXOffset(sourceOriginX - bounds.sourceRegion.x -
CheckedNumeric<GLuint> checkedYOffset(sourceRegion.y - sourceY - extraYOffset / 2); extraXOffset / 2);
CheckedNumeric<GLuint> checkedYOffset(sourceOriginY - bounds.sourceRegion.y -
extraYOffset / 2);
// if X/Y is reversed, use the top/right out-of-bounds region to compute // if X/Y is reversed, use the top/right out-of-bounds region to compute
// the origin offset instead of the left/bottom out-of-bounds region // the origin offset instead of the left/bottom out-of-bounds region
if (xFlipped) if (bounds.xFlipped)
{ {
checkedXOffset = (sourceX + sourceWidth - sourceRegion.x1() + extraXOffset / 2); checkedXOffset = (bounds.sourceRegion.x1() - (sourceOriginX + sourceClippedWidth) +
extraXOffset / 2);
} }
if (yFlipped) if (bounds.yFlipped)
{ {
checkedYOffset = (sourceY + sourceHeight - sourceRegion.y1() + extraYOffset / 2); checkedYOffset = (bounds.sourceRegion.y1() - (sourceOriginY + sourceClippedHeight) +
extraYOffset / 2);
} }
// These offsets should never overflow // These offsets should never overflow
...@@ -885,24 +897,90 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context, ...@@ -885,24 +897,90 @@ angle::Result FramebufferGL::adjustSrcDstRegion(const gl::Context *context,
return angle::Result::Stop; return angle::Result::Stop;
} }
bounds.sourceRegion =
gl::Rectangle(sourceOriginX, sourceOriginY, sourceClippedWidth, sourceClippedHeight);
// Adjust the dest region by the same factor // Adjust the dest region by the same factor
destRegion = gl::Rectangle( bounds.destRegion = gl::Rectangle(bounds.destRegion.x + (xOffset >> sourceXHalvings),
destX + (xOffset >> sourceXHalvings), destY + (yOffset >> sourceYHalvings), bounds.destRegion.y + (yOffset >> sourceYHalvings),
destRegion.width >> sourceXHalvings, destRegion.height >> sourceYHalvings); bounds.destRegion.width >> sourceXHalvings,
bounds.destRegion.height >> sourceYHalvings);
}
// Set the src and dst endpoints. If they were previously flipped,
// set them as flipped.
*newSourceArea = bounds.sourceRegion.flip(sourceArea.isReversedX(), sourceArea.isReversedY());
*newDestArea = bounds.destRegion.flip(destArea.isReversedX(), destArea.isReversedY());
return angle::Result::Continue;
}
angle::Result FramebufferGL::clipSrcRegion(const gl::Context *context,
const gl::Rectangle &sourceArea,
const gl::Rectangle &destArea,
gl::Rectangle *newSourceArea,
gl::Rectangle *newDestArea)
{
BlitFramebufferBounds bounds = GetBlitFramebufferBounds(context, sourceArea, destArea);
if (bounds.destRegion.width == 0 || bounds.sourceRegion.width == 0 ||
bounds.destRegion.height == 0 || bounds.sourceRegion.height == 0)
{
return angle::Result::Stop;
}
if (!ClipRectangle(bounds.destBounds, bounds.destRegion, nullptr))
{
return angle::Result::Stop;
}
if (!bounds.sourceBounds.encloses(bounds.sourceRegion))
{
// If pixels lying outside the read framebuffer, adjust src region
// and dst region to appropriate in-bounds regions respectively.
gl::Rectangle realSourceRegion;
ClipRectangle(bounds.sourceRegion, bounds.sourceBounds, &realSourceRegion);
GLuint xOffset = realSourceRegion.x - bounds.sourceRegion.x;
GLuint yOffset = realSourceRegion.y - bounds.sourceRegion.y;
// if X/Y is reversed, use the top/right out-of-bounds region for mapping
// to dst region, instead of left/bottom out-of-bounds region for mapping.
if (bounds.xFlipped)
{
xOffset = bounds.sourceRegion.x1() - realSourceRegion.x1();
}
if (bounds.yFlipped)
{
yOffset = bounds.sourceRegion.y1() - realSourceRegion.y1();
}
GLfloat destMappingWidth = static_cast<GLfloat>(realSourceRegion.width) *
bounds.destRegion.width / bounds.sourceRegion.width;
GLfloat destMappingHeight = static_cast<GLfloat>(realSourceRegion.height) *
bounds.destRegion.height / bounds.sourceRegion.height;
GLfloat destMappingXOffset =
static_cast<GLfloat>(xOffset) * bounds.destRegion.width / bounds.sourceRegion.width;
GLfloat destMappingYOffset =
static_cast<GLfloat>(yOffset) * bounds.destRegion.height / bounds.sourceRegion.height;
GLuint destMappingX0 =
static_cast<GLuint>(std::round(bounds.destRegion.x + destMappingXOffset));
GLuint destMappingY0 =
static_cast<GLuint>(std::round(bounds.destRegion.y + destMappingYOffset));
GLuint destMappingX1 = static_cast<GLuint>(
std::round(bounds.destRegion.x + destMappingXOffset + destMappingWidth));
GLuint destMappingY1 = static_cast<GLuint>(
std::round(bounds.destRegion.y + destMappingYOffset + destMappingHeight));
bounds.destRegion =
gl::Rectangle(destMappingX0, destMappingY0, destMappingX1 - destMappingX0,
destMappingY1 - destMappingY0);
bounds.sourceRegion = realSourceRegion;
} }
// Set the src and dst endpoints. If they were previously flipped, // Set the src and dst endpoints. If they were previously flipped,
// set them as flipped. // set them as flipped.
*newSourceArea = gl::Rectangle( *newSourceArea = bounds.sourceRegion.flip(sourceArea.isReversedX(), sourceArea.isReversedY());
sourceArea.x0() < sourceArea.x1() ? sourceRegion.x0() : sourceRegion.x1(), *newDestArea = bounds.destRegion.flip(destArea.isReversedX(), destArea.isReversedY());
sourceArea.y0() < sourceArea.y1() ? sourceRegion.y0() : sourceRegion.y1(),
sourceArea.x0() < sourceArea.x1() ? sourceRegion.width : -sourceRegion.width,
sourceArea.y0() < sourceArea.y1() ? sourceRegion.height : -sourceRegion.height);
*newDestArea =
gl::Rectangle(destArea.x0() < destArea.x1() ? destRegion.x0() : destRegion.x1(),
destArea.y0() < destArea.y1() ? destRegion.y0() : destRegion.y1(),
destArea.x0() < destArea.x1() ? destRegion.width : -destRegion.width,
destArea.y0() < destArea.y1() ? destRegion.height : -destRegion.height);
return angle::Result::Continue; return angle::Result::Continue;
} }
......
...@@ -121,6 +121,12 @@ class FramebufferGL : public FramebufferImpl ...@@ -121,6 +121,12 @@ class FramebufferGL : public FramebufferImpl
gl::Rectangle *newSourceArea, gl::Rectangle *newSourceArea,
gl::Rectangle *newDestArea); gl::Rectangle *newDestArea);
angle::Result clipSrcRegion(const gl::Context *context,
const gl::Rectangle &sourceArea,
const gl::Rectangle &destArea,
gl::Rectangle *newSourceArea,
gl::Rectangle *newDestArea);
GLuint mFramebufferID; GLuint mFramebufferID;
bool mIsDefault; bool mIsDefault;
......
...@@ -1533,7 +1533,9 @@ void InitializeFeatures(const FunctionsGL *functions, angle::FeaturesGL *feature ...@@ -1533,7 +1533,9 @@ void InitializeFeatures(const FunctionsGL *functions, angle::FeaturesGL *feature
IsApple() && IsIntel(vendor) && GetMacOSVersion() < OSVersion(10, 12, 6); IsApple() && IsIntel(vendor) && GetMacOSVersion() < OSVersion(10, 12, 6);
features->adjustSrcDstRegionBlitFramebuffer.enabled = features->adjustSrcDstRegionBlitFramebuffer.enabled =
IsApple() || IsLinux() || (IsAndroid() && IsNvidia(vendor)); IsLinux() || (IsAndroid() && IsNvidia(vendor)) || (IsWindows() && IsNvidia(vendor));
features->clipSrcRegionBlitFramebuffer.enabled = IsApple();
} }
void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features) void InitializeFrontendFeatures(const FunctionsGL *functions, angle::FrontendFeatures *features)
......
...@@ -1750,6 +1750,105 @@ TEST_P(BlitFramebufferTest, BlitSRGBToRGBOversizedSourceArea) ...@@ -1750,6 +1750,105 @@ TEST_P(BlitFramebufferTest, BlitSRGBToRGBOversizedSourceArea)
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::blue); EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::blue);
} }
// Blit an SRGB framebuffer with an oversized dest area (even though the result is clipped, it
// should be scaled as if the whole dest area was used).
TEST_P(BlitFramebufferTest, BlitSRGBToRGBOversizedDestArea)
{
constexpr const GLsizei kWidth = 256;
constexpr const GLsizei kHeight = 256;
GLRenderbuffer sourceRBO, targetRBO;
GLFramebuffer sourceFBO, targetFBO;
initColorFBOWithCheckerPattern(&sourceFBO, &sourceRBO, GL_SRGB8_ALPHA8, kWidth, kHeight);
initColorFBO(&targetFBO, &targetRBO, GL_RGBA8, kWidth, kHeight);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Dest is oversized but centered the same as source
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glBlitFramebuffer(0, 0, kWidth, kHeight, -kWidth / 2, -kHeight / 2, 3 * kWidth / 2,
3 * kHeight / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 - 1, kHeight / 2 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(1, kWidth - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 - 1, kHeight / 2 + 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight / 2 - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight / 2 + 1, GLColor::red);
// Dest is oversized in the negative direction
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glBlitFramebuffer(0, 0, kWidth, kHeight, -kWidth / 2, -kHeight / 2, kWidth, kHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 - 1, kHeight / 2 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(1, kWidth - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 - 1, kHeight / 2 + 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight / 2 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, 3 * kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight / 2 + 1, GLColor::red);
// Dest is oversized in the positive direction
glBindFramebuffer(GL_READ_FRAMEBUFFER, sourceFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, targetFBO);
glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, 3 * kWidth / 2, 3 * kHeight / 2,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, targetFBO);
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, kHeight / 4, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 - 1, kHeight / 2 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(1, kWidth - 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 4, 3 * kHeight / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 - 1, kHeight / 2 + 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, 1, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(3 * kWidth / 4, kHeight / 4, GLColor::green);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight / 2 - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth - 1, kHeight - 1, GLColor::red);
EXPECT_PIXEL_COLOR_EQ(kWidth / 2 + 1, kHeight / 2 + 1, GLColor::red);
}
// Test blitFramebuffer size overflow checks. WebGL 2.0 spec section 5.41. We do validation for // Test blitFramebuffer size overflow checks. WebGL 2.0 spec section 5.41. We do validation for
// overflows also in non-WebGL mode to avoid triggering driver bugs. // overflows also in non-WebGL mode to avoid triggering driver bugs.
TEST_P(BlitFramebufferTest, BlitFramebufferSizeOverflow) TEST_P(BlitFramebufferTest, BlitFramebufferSizeOverflow)
...@@ -1805,6 +1904,91 @@ TEST_P(BlitFramebufferTest, BlitFramebufferSizeOverflow) ...@@ -1805,6 +1904,91 @@ TEST_P(BlitFramebufferTest, BlitFramebufferSizeOverflow)
EXPECT_GL_ERROR(GL_INVALID_VALUE); EXPECT_GL_ERROR(GL_INVALID_VALUE);
} }
// Test blitFramebuffer size overflow checks. WebGL 2.0 spec section 5.41. Similar to above test,
// but this test more accurately duplicates the behavior of the WebGL test
// conformance2/rendering/blitframebuffer-size-overflow.html, which covers a few more edge cases.
TEST_P(BlitFramebufferTest, BlitFramebufferSizeOverflow2)
{
GLTexture textures[2];
glBindTexture(GL_TEXTURE_2D, textures[0]);
glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 4, 4);
glBindTexture(GL_TEXTURE_2D, textures[1]);
glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 4, 4);
GLFramebuffer framebuffers[2];
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffers[0]);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffers[1]);
ASSERT_GL_NO_ERROR();
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[0],
0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textures[1],
0);
ASSERT_GL_NO_ERROR();
GLint width = 8;
GLint height = 8;
GLTexture tex0;
glBindTexture(GL_TEXTURE_2D, tex0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
GLFramebuffer fb0;
glBindFramebuffer(GL_READ_FRAMEBUFFER, fb0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex0, 0);
GLTexture tex1;
glBindTexture(GL_TEXTURE_2D, tex1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
GLFramebuffer fb1;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb1);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex1, 0);
GLint max = std::numeric_limits<GLint>::max();
// Using max 32-bit integer as blitFramebuffer parameter should succeed.
glBlitFramebuffer(0, 0, max, max, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBlitFramebuffer(0, 0, width, height, 0, 0, max, max, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBlitFramebuffer(0, 0, max, max, 0, 0, max, max, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_NO_ERROR();
// Using blitFramebuffer parameters where calculated width/height matches max 32-bit integer
// should succeed
glBlitFramebuffer(-1, -1, max - 1, max - 1, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBlitFramebuffer(0, 0, width, height, -1, -1, max - 1, max - 1, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBlitFramebuffer(-1, -1, max - 1, max - 1, -1, -1, max - 1, max - 1, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
EXPECT_GL_NO_ERROR();
// Using source width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(-1, -1, max, max, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using source width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(max, max, -1, -1, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using destination width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(0, 0, width, height, -1, -1, max, max, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using destination width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(0, 0, width, height, max, max, -1, -1, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using both source and destination width/height greater than max 32-bit integer should fail.
glBlitFramebuffer(-1, -1, max, max, -1, -1, max, max, GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
// Using minimum and maximum integers for all boundaries should fail.
glBlitFramebuffer(-max - 1, -max - 1, max, max, -max - 1, -max - 1, max, max,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
EXPECT_GL_ERROR(GL_INVALID_VALUE);
}
// Use this to select which configurations (e.g. which renderer, which GLES major version) these // Use this to select which configurations (e.g. which renderer, which GLES major version) these
// tests should be run against. // tests should be run against.
ANGLE_INSTANTIATE_TEST(BlitFramebufferANGLETest, ANGLE_INSTANTIATE_TEST(BlitFramebufferANGLETest,
......
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