Fix Blitting in Metal backend.

The Metal backend was clipping in integer space.
If the src and dst are not the same size,
say src is 3 wide and dst is 4 wide, and src
starts at -1, then src will be clipped by one
making the src 2 wide. It got 1/3 smaller so the
dst get 1/3 smaller making it 2.666 pixels wide.
The dst then needs to be expanded to pixels so 3 wide.
But, that means the src also needs to be expanded
0.3333 * 3(originalSrcWidth) / 4(originalDstWidth)
so its new left edge is -0.245 which is not an integer.

Bug: angleproject:6598
Change-Id: I2faa966b18b457f474a3e7f6844ef64bfa66dbe8
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3251683
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
Reviewed-by: Kyle Piddington <kpiddington@apple.com>
Commit-Queue: Gregg Tavares <gman@chromium.org>
This commit is contained in:
Gregg Tavares 2021-10-28 11:01:12 -07:00 коммит произвёл Angle LUCI CQ
Родитель c31855eaf5
Коммит 20ddb8027d
5 изменённых файлов: 253 добавлений и 143 удалений

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

@ -7,6 +7,7 @@
// Implements the class methods for FramebufferMtl.
//
#include "libANGLE/angletypes.h"
#include "libANGLE/renderer/metal/ContextMtl.h"
#include <TargetConditionals.h>
@ -266,6 +267,166 @@ angle::Result FramebufferMtl::readPixels(const gl::Context *context,
return angle::Result::Continue;
}
namespace
{
// Naming FloatRectangle instead of Rectangle because it seems like it would be confused with
// gl::Rectangle
struct FloatRectangle
{
FloatRectangle() : x(0.0f), y(0.0f), width(0.0f), height(0.0f) {}
constexpr FloatRectangle(int x_in, int y_in, int width_in, int height_in)
: x(x_in), y(y_in), width(width_in), height(height_in)
{}
explicit constexpr FloatRectangle(const gl::Rectangle &rect)
: x(rect.x), y(rect.y), width(rect.width), height(rect.height)
{}
explicit constexpr FloatRectangle(const float corners[4])
: x(corners[0]),
y(corners[1]),
width(corners[2] - corners[0]),
height(corners[3] - corners[1])
{}
float x0() const { return x; }
float y0() const { return y; }
float x1() const { return x + width; }
float y1() const { return y + height; }
bool isReversedX() const { return width < 0.0f; }
bool isReversedY() const { return height < 0.0f; }
// Returns a rectangle with the same area but flipped in X, Y, neither or both.
FloatRectangle flip(bool flipX, bool flipY) const;
// Returns a rectangle with the same area but with height and width guaranteed to be positive.
FloatRectangle removeReversal() const;
float x;
float y;
float width;
float height;
};
FloatRectangle FloatRectangle::flip(bool flipX, bool flipY) const
{
FloatRectangle flipped = *this;
if (flipX)
{
flipped.x = flipped.x + flipped.width;
flipped.width = -flipped.width;
}
if (flipY)
{
flipped.y = flipped.y + flipped.height;
flipped.height = -flipped.height;
}
return flipped;
}
FloatRectangle FloatRectangle::removeReversal() const
{
return flip(isReversedX(), isReversedY());
}
float clamp0Max(float v, float max)
{
return std::max(0.0f, std::min(max, v));
}
void ClampToBoundsAndAdjustCorrespondingValue(float a,
float originalASize,
float maxSize,
float b,
float originalBSize,
float *newA,
float *newB)
{
float clippedA = clamp0Max(a, maxSize);
float delta = clippedA - a;
*newA = clippedA;
*newB = b + delta * originalBSize / originalASize;
}
void ClipRectToBoundsAndAdjustCorrespondingRect(const FloatRectangle &a,
const gl::Rectangle &originalA,
const gl::Rectangle &clipDimensions,
const FloatRectangle &b,
const gl::Rectangle &originalB,
FloatRectangle *newA,
FloatRectangle *newB)
{
float newAValues[4];
float newBValues[4];
ClampToBoundsAndAdjustCorrespondingValue(a.x0(), originalA.width, clipDimensions.width, b.x0(),
originalB.width, &newAValues[0], &newBValues[0]);
ClampToBoundsAndAdjustCorrespondingValue(a.y0(), originalA.height, clipDimensions.height,
b.y0(), originalB.height, &newAValues[1],
&newBValues[1]);
ClampToBoundsAndAdjustCorrespondingValue(a.x1(), originalA.width, clipDimensions.width, b.x1(),
originalB.width, &newAValues[2], &newBValues[2]);
ClampToBoundsAndAdjustCorrespondingValue(a.y1(), originalA.height, clipDimensions.height,
b.y1(), originalB.height, &newAValues[3],
&newBValues[3]);
*newA = FloatRectangle(newAValues);
*newB = FloatRectangle(newBValues);
}
void ClipRectsToBoundsAndAdjustCorrespondingRect(const FloatRectangle &a,
const gl::Rectangle &originalA,
const gl::Rectangle &aClipDimensions,
const FloatRectangle &b,
const gl::Rectangle &originalB,
const gl::Rectangle &bClipDimensions,
FloatRectangle *newA,
FloatRectangle *newB)
{
FloatRectangle tempA;
FloatRectangle tempB;
ClipRectToBoundsAndAdjustCorrespondingRect(a, originalA, aClipDimensions, b, originalB, &tempA,
&tempB);
ClipRectToBoundsAndAdjustCorrespondingRect(tempB, originalB, bClipDimensions, tempA, originalA,
newB, newA);
}
void RoundValueAndAdjustCorrespondingValue(float a,
float originalASize,
float b,
float originalBSize,
int *newA,
float *newB)
{
float roundedA = std::round(a);
float delta = roundedA - a;
*newA = static_cast<int>(roundedA);
*newB = b + delta * originalBSize / originalASize;
}
gl::Rectangle RoundRectToPixelsAndAdjustCorrespondingRectToMatch(const FloatRectangle &a,
const gl::Rectangle &originalA,
const FloatRectangle &b,
const gl::Rectangle &originalB,
FloatRectangle *newB)
{
int newAValues[4];
float newBValues[4];
RoundValueAndAdjustCorrespondingValue(a.x0(), originalA.width, b.x0(), originalB.width,
&newAValues[0], &newBValues[0]);
RoundValueAndAdjustCorrespondingValue(a.y0(), originalA.height, b.y0(), originalB.height,
&newAValues[1], &newBValues[1]);
RoundValueAndAdjustCorrespondingValue(a.x1(), originalA.width, b.x1(), originalB.width,
&newAValues[2], &newBValues[2]);
RoundValueAndAdjustCorrespondingValue(a.y1(), originalA.height, b.y1(), originalB.height,
&newAValues[3], &newBValues[3]);
*newB = FloatRectangle(newBValues);
return gl::Rectangle(newAValues[0], newAValues[1], newAValues[2] - newAValues[0],
newAValues[3] - newAValues[1]);
}
} // namespace
angle::Result FramebufferMtl::blit(const gl::Context *context,
const gl::Rectangle &sourceAreaIn,
const gl::Rectangle &destAreaIn,
@ -292,87 +453,38 @@ angle::Result FramebufferMtl::blit(const gl::Context *context,
return angle::Result::Continue;
}
gl::Rectangle sourceArea = sourceAreaIn;
gl::Rectangle destArea = destAreaIn;
const gl::Rectangle srcFramebufferDimensions = srcFrameBuffer->getCompleteRenderArea();
const gl::Rectangle dstFramebufferDimensions = this->getCompleteRenderArea();
// If the destination is flipped in either direction, we will flip the source instead so that
// the destination area is always unflipped.
sourceArea = sourceArea.flip(destArea.isReversedX(), destArea.isReversedY());
destArea = destArea.removeReversal();
FloatRectangle srcRect(sourceAreaIn);
FloatRectangle dstRect(destAreaIn);
// Calculate the stretch factor prior to any clipping, as it needs to remain constant.
const float stretch[2] = {
std::abs(sourceArea.width / static_cast<float>(destArea.width)),
std::abs(sourceArea.height / static_cast<float>(destArea.height)),
};
FloatRectangle clippedSrcRect;
FloatRectangle clippedDstRect;
ClipRectsToBoundsAndAdjustCorrespondingRect(srcRect, sourceAreaIn, srcFramebufferDimensions,
dstRect, destAreaIn, dstFramebufferDimensions,
&clippedSrcRect, &clippedDstRect);
// First, clip the source area to framebuffer. That requires transforming the dest area to
// match the clipped source.
gl::Rectangle absSourceArea = sourceArea.removeReversal();
gl::Rectangle clippedSourceArea;
if (!gl::ClipRectangle(srcFramebufferDimensions, absSourceArea, &clippedSourceArea))
{
return angle::Result::Continue;
}
// Resize the destination area based on the new size of source. Note again that stretch is
// calculated as SrcDimension/DestDimension.
gl::Rectangle srcClippedDestArea;
if (clippedSourceArea == absSourceArea)
{
// If there was no clipping, keep dest area as is.
srcClippedDestArea = destArea;
}
else
{
// Shift dest area's x0,y0,x1,y1 by as much as the source area's got shifted (taking
// stretching into account)
float x0Shift = std::round((clippedSourceArea.x - absSourceArea.x) / stretch[0]);
float y0Shift = std::round((clippedSourceArea.y - absSourceArea.y) / stretch[1]);
float x1Shift = std::round((absSourceArea.x1() - clippedSourceArea.x1()) / stretch[0]);
float y1Shift = std::round((absSourceArea.y1() - clippedSourceArea.y1()) / stretch[1]);
// If the source area was reversed in any direction, the shift should be applied in the
// opposite direction as well.
if (sourceArea.isReversedX())
{
std::swap(x0Shift, x1Shift);
}
if (sourceArea.isReversedY())
{
std::swap(y0Shift, y1Shift);
}
srcClippedDestArea.x = destArea.x0() + static_cast<int>(x0Shift);
srcClippedDestArea.y = destArea.y0() + static_cast<int>(y0Shift);
int x1 = destArea.x1() - static_cast<int>(x1Shift);
int y1 = destArea.y1() - static_cast<int>(y1Shift);
srcClippedDestArea.width = x1 - srcClippedDestArea.x;
srcClippedDestArea.height = y1 - srcClippedDestArea.y;
}
// Flip source area if necessary
clippedSourceArea = srcFrameBuffer->getCorrectFlippedReadArea(context, clippedSourceArea);
bool unpackFlipX = sourceArea.isReversedX();
bool unpackFlipY = sourceArea.isReversedY();
FloatRectangle adjustedSrcRect;
gl::Rectangle srcClippedDestArea = RoundRectToPixelsAndAdjustCorrespondingRectToMatch(
clippedDstRect, destAreaIn, clippedSrcRect, sourceAreaIn, &adjustedSrcRect);
if (srcFrameBuffer->flipY())
{
// The rectangle already flipped by calling getCorrectFlippedReadArea(). So reverse the
// unpackFlipY flag.
unpackFlipY = !unpackFlipY;
adjustedSrcRect.y =
srcFramebufferDimensions.height - adjustedSrcRect.y - adjustedSrcRect.height;
adjustedSrcRect = adjustedSrcRect.flip(false, true);
}
ASSERT(!destArea.isReversedX() && !destArea.isReversedY());
// If the destination is flipped in either direction, we will flip the source instead so that
// the destination area is always unflipped.
adjustedSrcRect =
adjustedSrcRect.flip(srcClippedDestArea.isReversedX(), srcClippedDestArea.isReversedY());
srcClippedDestArea = srcClippedDestArea.removeReversal();
// Clip the destination area to the framebuffer size and scissor.
gl::Rectangle scissoredDestArea;
if (!gl::ClipRectangle(ClipRectToScissor(glState, this->getCompleteRenderArea(), false),
if (!gl::ClipRectangle(ClipRectToScissor(glState, dstFramebufferDimensions, false),
srcClippedDestArea, &scissoredDestArea))
{
return angle::Result::Continue;
@ -380,17 +492,20 @@ angle::Result FramebufferMtl::blit(const gl::Context *context,
// Use blit with draw
mtl::BlitParams baseParams;
baseParams.dstTextureSize = mState.getExtents();
baseParams.dstTextureSize =
gl::Extents(dstFramebufferDimensions.width, dstFramebufferDimensions.height, 1);
baseParams.dstRect = srcClippedDestArea;
baseParams.dstScissorRect = scissoredDestArea;
baseParams.dstFlipY = this->flipY();
baseParams.srcRect = clippedSourceArea;
baseParams.srcNormalizedCoords =
mtl::NormalizedCoords(adjustedSrcRect.x, adjustedSrcRect.y, adjustedSrcRect.width,
adjustedSrcRect.height, srcFramebufferDimensions);
// This flag is for auto flipping the rect inside RenderUtils. Since we already flip it using
// getCorrectFlippedReadArea(). This flag is not needed.
baseParams.srcYFlipped = false;
baseParams.unpackFlipX = unpackFlipX;
baseParams.unpackFlipY = unpackFlipY;
baseParams.unpackFlipX = false;
baseParams.unpackFlipY = false;
return blitWithDraw(context, srcFrameBuffer, blitColorBuffer, blitDepthBuffer,
blitStencilBuffer, filter, baseParams);

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

@ -2142,10 +2142,12 @@ angle::Result TextureMtl::copySubImageWithDraw(const gl::Context *context,
blitParams.enabledBuffers.set(0);
blitParams.src = colorReadRT->getTexture();
blitParams.srcLevel = colorReadRT->getLevelIndex();
blitParams.srcLayer = colorReadRT->getLayerIndex();
blitParams.srcRect = clippedSourceArea;
blitParams.src = colorReadRT->getTexture();
blitParams.srcLevel = colorReadRT->getLevelIndex();
blitParams.srcLayer = colorReadRT->getLayerIndex();
blitParams.srcNormalizedCoords = mtl::NormalizedCoords(
clippedSourceArea, colorReadRT->getTexture()->size(blitParams.srcLevel));
blitParams.srcYFlipped = source->flipY();
blitParams.dstLuminance = internalFormat.isLUMA();
@ -2291,7 +2293,8 @@ angle::Result TextureMtl::copySubTextureWithDraw(const gl::Context *context,
blitParams.src = sourceTexture;
blitParams.srcLevel = sourceNativeLevel;
blitParams.srcLayer = 0;
blitParams.srcRect = gl::Rectangle(sourceBox.x, sourceBox.y, sourceBox.width, sourceBox.height);
blitParams.srcNormalizedCoords =
mtl::NormalizedCoords(sourceBox.toRect(), sourceTexture->size(sourceNativeLevel));
blitParams.srcYFlipped = false;
blitParams.dstLuminance = internalFormat.isLUMA();
blitParams.unpackFlipY = unpackFlipY;

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

@ -50,6 +50,14 @@ struct ClearRectParams
bool flipY = false;
};
struct NormalizedCoords
{
NormalizedCoords();
NormalizedCoords(float x, float y, float width, float height, const gl::Rectangle &rect);
NormalizedCoords(const gl::Rectangle &rect, const gl::Extents &extents);
float v[4];
};
struct BlitParams
{
gl::Extents dstTextureSize;
@ -71,7 +79,7 @@ struct BlitParams
// Source rectangle:
// NOTE: if srcYFlipped=true, this rectangle will be converted internally to flipped rect before
// blitting.
gl::Rectangle srcRect;
NormalizedCoords srcNormalizedCoords;
bool srcYFlipped = false; // source texture has data flipped in Y direction
bool unpackFlipX = false; // flip texture data copying process in X direction

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

@ -191,9 +191,7 @@ struct ScopedDisableOcclusionQuery
angle::Result *mResultOut;
};
void GetBlitTexCoords(uint32_t srcWidth,
uint32_t srcHeight,
const gl::Rectangle &srcRect,
void GetBlitTexCoords(const NormalizedCoords &normalizedCoords,
bool srcYFlipped,
bool unpackFlipX,
bool unpackFlipY,
@ -202,33 +200,26 @@ void GetBlitTexCoords(uint32_t srcWidth,
float *u1,
float *v1)
{
int x0 = srcRect.x0(); // left
int x1 = srcRect.x1(); // right
int y0 = srcRect.y0(); // lower
int y1 = srcRect.y1(); // upper
*u0 = normalizedCoords.v[0];
*v0 = normalizedCoords.v[1];
*u1 = normalizedCoords.v[2];
*v1 = normalizedCoords.v[3];
if (srcYFlipped)
{
// If source's Y has been flipped, such as default framebuffer, then adjust the real source
// rectangle.
y0 = srcHeight - y1;
y1 = y0 + srcRect.height;
std::swap(y0, y1);
*v0 = 1.0 - *v0;
*v1 = 1.0 - *v1;
}
if (unpackFlipX)
{
std::swap(x0, x1);
std::swap(*u0, *u1);
}
if (unpackFlipY)
{
std::swap(y0, y1);
std::swap(*v0, *v1);
}
*u0 = static_cast<float>(x0) / srcWidth;
*u1 = static_cast<float>(x1) / srcWidth;
*v0 = static_cast<float>(y0) / srcHeight;
*v1 = static_cast<float>(y1) / srcHeight;
}
template <typename T>
@ -772,27 +763,8 @@ void SetupBlitWithDrawUniformData(RenderCommandEncoder *cmdEncoder,
uniformParams.dstLuminance = colorParams->dstLuminance ? 1 : 0;
}
// Compute source texCoords
uint32_t srcWidth = 0, srcHeight = 0;
if (params.src)
{
srcWidth = params.src->width(params.srcLevel);
srcHeight = params.src->height(params.srcLevel);
}
else if (!isColorBlit)
{
const DepthStencilBlitParams *dsParams =
static_cast<const DepthStencilBlitParams *>(&params);
srcWidth = dsParams->srcStencil->width(dsParams->srcLevel);
srcHeight = dsParams->srcStencil->height(dsParams->srcLevel);
}
else
{
UNREACHABLE();
}
float u0, v0, u1, v1;
GetBlitTexCoords(srcWidth, srcHeight, params.srcRect, params.srcYFlipped, params.unpackFlipX,
GetBlitTexCoords(params.srcNormalizedCoords, params.srcYFlipped, params.unpackFlipX,
params.unpackFlipY, &u0, &v0, &u1, &v1);
float du = u1 - u0;
@ -897,20 +869,44 @@ ANGLE_INLINE void SetPipelineState(ComputeCommandEncoder *encoder,
} // namespace
NormalizedCoords::NormalizedCoords() : v{0.0f, 0.0f, 1.0f, 1.0f} {}
NormalizedCoords::NormalizedCoords(float x,
float y,
float width,
float height,
const gl::Rectangle &rect)
: v{
x / rect.width,
y / rect.height,
(x + width) / rect.width,
(y + height) / rect.height,
}
{}
NormalizedCoords::NormalizedCoords(const gl::Rectangle &rect, const gl::Extents &extents)
: v{
static_cast<float>(rect.x0()) / extents.width,
static_cast<float>(rect.y0()) / extents.height,
static_cast<float>(rect.x1()) / extents.width,
static_cast<float>(rect.y1()) / extents.height,
}
{}
// StencilBlitViaBufferParams implementation
StencilBlitViaBufferParams::StencilBlitViaBufferParams() {}
StencilBlitViaBufferParams::StencilBlitViaBufferParams(const DepthStencilBlitParams &srcParams)
{
dstTextureSize = srcParams.dstTextureSize;
dstRect = srcParams.dstRect;
dstScissorRect = srcParams.dstScissorRect;
dstFlipY = srcParams.dstFlipY;
dstFlipX = srcParams.dstFlipX;
srcRect = srcParams.srcRect;
srcYFlipped = srcParams.srcYFlipped;
unpackFlipX = srcParams.unpackFlipX;
unpackFlipY = srcParams.unpackFlipY;
dstTextureSize = srcParams.dstTextureSize;
dstRect = srcParams.dstRect;
dstScissorRect = srcParams.dstScissorRect;
dstFlipY = srcParams.dstFlipY;
dstFlipX = srcParams.dstFlipX;
srcNormalizedCoords = srcParams.srcNormalizedCoords;
srcYFlipped = srcParams.srcYFlipped;
unpackFlipX = srcParams.unpackFlipX;
unpackFlipY = srcParams.unpackFlipY;
srcStencil = srcParams.srcStencil;
}
@ -1025,8 +1021,9 @@ angle::Result RenderUtils::blitColorWithDraw(const gl::Context *context,
params.dstTextureSize = gl::Extents(static_cast<int>(srcTexture->widthAt0()),
static_cast<int>(srcTexture->heightAt0()),
static_cast<int>(srcTexture->depthAt0()));
params.dstRect = params.dstScissorRect = params.srcRect =
params.dstRect = params.dstScissorRect =
gl::Rectangle(0, 0, params.dstTextureSize.width, params.dstTextureSize.height);
params.srcNormalizedCoords = NormalizedCoords();
return blitColorWithDraw(context, cmdEncoder, srcAngleFormat, params);
}
@ -1839,9 +1836,6 @@ angle::Result DepthStencilBlitUtils::blitStencilViaCopyBuffer(
cmdEncoder->setComputePipelineState(pipeline);
uint32_t srcWidth = params.srcStencil->width(params.srcLevel);
uint32_t srcHeight = params.srcStencil->height(params.srcLevel);
float u0, v0, u1, v1;
bool unpackFlipX = params.unpackFlipX;
bool unpackFlipY = params.unpackFlipY;
@ -1853,8 +1847,8 @@ angle::Result DepthStencilBlitUtils::blitStencilViaCopyBuffer(
{
unpackFlipY = !unpackFlipY;
}
GetBlitTexCoords(srcWidth, srcHeight, params.srcRect, params.srcYFlipped, unpackFlipX,
unpackFlipY, &u0, &v0, &u1, &v1);
GetBlitTexCoords(params.srcNormalizedCoords, params.srcYFlipped, unpackFlipX, unpackFlipY, &u0,
&v0, &u1, &v1);
BlitStencilToBufferParamsUniform uniform;
uniform.srcTexCoordSteps[0] = (u1 - u0) / params.dstRect.width;

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

@ -572,16 +572,6 @@
// FBO with distinct attachment's sizes is not supported yet
6467 MAC METAL : dEQP-GLES3.functional.fbo.completeness.size.distinct = FAIL
// FBO blit bugs (failed a few pixels)
6467 MAC METAL : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_linear = FAIL
6467 MAC METAL : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_dst_x_linear = FAIL
6467 MAC METAL : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_dst_y_linear = FAIL
6467 MAC METAL : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_dst_x_linear = FAIL
6467 MAC METAL : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_dst_y_linear = FAIL
6467 MAC METAL : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_x_linear = FAIL
6467 MAC METAL : dEQP-GLES3.functional.fbo.blit.rect.out_of_bounds_reverse_src_y_linear = FAIL
// Wide point out of clip space issue on AMD
6589 MAC METAL AMD : dEQP-GLES3.functional.clipping.point.wide_point_clip = FAIL
6589 MAC METAL AMD : dEQP-GLES3.functional.clipping.point.wide_point_clip_viewport_center = FAIL