Commit 10c74a62 by Alexis Hetu Committed by Alexis Hétu

Allow out of bounds coordinates in glBlitFramebuffer

Device::stretchRect() now supports out of bounds coordinates. To avoid linear interpolation errors, source coordinates now have to be in floating point rather than integer format. Most changes in this cl are just to accommodate that int->float change for the source rect. Fixes all (28) failures in: dEQP-GLES3.functional.fbo.blit.rect Change-Id: I8fd017e60b61f2d7d6517b0e648b324be441cddd Reviewed-on: https://swiftshader-review.googlesource.com/14648Tested-by: 's avatarAlexis Hétu <sugoi@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent bf3fc254
......@@ -6323,7 +6323,8 @@ namespace D3D9
}
else
{
renderer->blit(source, sRect, dest, dRect, filter >= D3DTEXF_LINEAR);
sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, 0);
renderer->blit(source, sRectF, dest, dRect, filter >= D3DTEXF_LINEAR);
}
}
......
......@@ -621,7 +621,8 @@ namespace gl
}
else
{
blit(source, sRect, dest, dRect, scaling && filter);
sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
blit(source, sRectF, dest, dRect, scaling && filter);
}
return true;
......
......@@ -2852,7 +2852,8 @@ void Context::drawTexture(GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLfloa
void Context::blit(sw::Surface *source, const sw::SliceRect &sRect, sw::Surface *dest, const sw::SliceRect &dRect)
{
device->blit(source, sRect, dest, dRect, false);
sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
device->blit(source, sRectF, dest, dRect, false);
}
void Context::finish()
......
......@@ -506,7 +506,8 @@ namespace es1
}
else
{
blit(source, sRect, dest, dRect, scaling && filter);
sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
blit(source, sRectF, dest, dRect, scaling && filter);
}
return true;
......
......@@ -3331,12 +3331,12 @@ void Context::readPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenum
return error(GL_INVALID_OPERATION);
}
sw::Rect rect = {x, y, x + width, y + height};
sw::Rect dstRect = { 0, 0, width, height };
rect.clip(0, 0, renderTarget->getWidth(), renderTarget->getHeight());
sw::RectF rect((float)x, (float)y, (float)(x + width), (float)(y + height));
sw::Rect dstRect(0, 0, width, height);
rect.clip(0.0f, 0.0f, (float)renderTarget->getWidth(), (float)renderTarget->getHeight());
sw::Surface *externalSurface = sw::Surface::create(width, height, 1, egl::ConvertFormatType(format, type), pixels, outputPitch, outputPitch * outputHeight);
sw::SliceRect sliceRect(rect);
sw::SliceRectF sliceRect(rect);
sw::SliceRect dstSliceRect(dstRect);
device->blit(renderTarget, sliceRect, externalSurface, dstSliceRect, false);
delete externalSurface;
......@@ -3628,7 +3628,8 @@ void Context::drawElements(GLenum mode, GLuint start, GLuint end, GLsizei count,
void Context::blit(sw::Surface *source, const sw::SliceRect &sRect, sw::Surface *dest, const sw::SliceRect &dRect)
{
device->blit(source, sRect, dest, dRect, false);
sw::SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
device->blit(source, sRectF, dest, dRect, false);
}
void Context::finish()
......
......@@ -537,12 +537,16 @@ namespace es2
flipY = (destRect->y0 > destRect->y1);
}
SliceRect sRect;
SliceRectF sRect;
SliceRect dRect;
if(sourceRect)
{
sRect = *sourceRect;
sRect.x0 = (float)(sourceRect->x0);
sRect.x1 = (float)(sourceRect->x1);
sRect.y0 = (float)(sourceRect->y0);
sRect.y1 = (float)(sourceRect->y1);
sRect.slice = sourceRect->slice;
if(sRect.x0 > sRect.x1)
{
......@@ -556,10 +560,10 @@ namespace es2
}
else
{
sRect.y0 = 0;
sRect.x0 = 0;
sRect.y1 = sHeight;
sRect.x1 = sWidth;
sRect.y0 = 0.0f;
sRect.x0 = 0.0f;
sRect.y1 = (float)sHeight;
sRect.x1 = (float)sWidth;
}
if(destRect)
......@@ -584,17 +588,131 @@ namespace es2
dRect.x1 = dWidth;
}
if(sRect.x0 < 0)
{
float ratio = static_cast<float>(dRect.width()) / sRect.width();
float offsetf = roundf(-sRect.x0 * ratio);
int offset = static_cast<int>(offsetf);
if(flipX)
{
dRect.x1 -= offset;
}
else
{
dRect.x0 += offset;
}
sRect.x0 += offsetf / ratio;
}
if(sRect.x1 > sWidth)
{
float ratio = static_cast<float>(dRect.width()) / sRect.width();
float offsetf = roundf((sRect.x1 - (float)sWidth) * ratio);
int offset = static_cast<int>(offsetf);
if(flipX)
{
dRect.x0 += offset;
}
else
{
dRect.x1 -= offset;
}
sRect.x1 -= offsetf / ratio;
}
if(sRect.y0 < 0)
{
float ratio = static_cast<float>(dRect.height()) / sRect.height();
float offsetf = roundf(-sRect.y0 * ratio);
int offset = static_cast<int>(offsetf);
if(flipY)
{
dRect.y1 -= offset;
}
else
{
dRect.y0 += offset;
}
sRect.y0 += offsetf / ratio;
}
if(sRect.y1 > sHeight)
{
float ratio = static_cast<float>(dRect.height()) / sRect.height();
float offsetf = roundf((sRect.y1 - (float)sHeight) * ratio);
int offset = static_cast<int>(offsetf);
if(flipY)
{
dRect.y0 += offset;
}
else
{
dRect.y1 -= offset;
}
sRect.y1 -= offsetf / ratio;
}
if(dRect.x0 < 0)
{
float offset = (static_cast<float>(-dRect.x0) / static_cast<float>(dRect.width())) * sRect.width();
if(flipX)
{
sRect.x1 -= offset;
}
else
{
sRect.x0 += offset;
}
dRect.x0 = 0;
}
if(dRect.x1 > dWidth)
{
float offset = (static_cast<float>(dRect.x1 - dWidth) / static_cast<float>(dRect.width())) * sRect.width();
if(flipX)
{
sRect.x0 += offset;
}
else
{
sRect.x1 -= offset;
}
dRect.x1 = dWidth;
}
if(dRect.y0 < 0)
{
float offset = (static_cast<float>(-dRect.y0) / static_cast<float>(dRect.height())) * sRect.height();
if(flipY)
{
sRect.y1 -= offset;
}
else
{
sRect.y0 += offset;
}
dRect.y0 = 0;
}
if(dRect.y1 > dHeight)
{
float offset = (static_cast<float>(dRect.y1 - dHeight) / static_cast<float>(dRect.height())) * sRect.height();
if(flipY)
{
sRect.y0 += offset;
}
else
{
sRect.y1 -= offset;
}
dRect.y1 = dHeight;
}
if(!validRectangle(&sRect, source) || !validRectangle(&dRect, dest))
{
ERR("Invalid parameters");
return false;
}
bool scaling = (sRect.x1 - sRect.x0 != dRect.x1 - dRect.x0) || (sRect.y1 - sRect.y0 != dRect.y1 - dRect.y0);
bool scaling = (sRect.width() != (float)dRect.width()) || (sRect.height() != (float)dRect.height());
bool equalFormats = source->getInternalFormat() == dest->getInternalFormat();
bool hasQuadLayout = Surface::hasQuadLayout(source->getInternalFormat()) || Surface::hasQuadLayout(dest->getInternalFormat());
bool fullCopy = (sRect.x0 == 0) && (sRect.y0 == 0) && (dRect.x0 == 0) && (dRect.y0 == 0) &&
(sRect.x1 == sWidth) && (sRect.y1 == sHeight) && (dRect.x1 == dWidth) && (dRect.y0 == dHeight);
bool fullCopy = (sRect.x0 == 0.0f) && (sRect.y0 == 0.0f) && (dRect.x0 == 0) && (dRect.y0 == 0) &&
(sRect.x1 == (float)sWidth) && (sRect.y1 == (float)sHeight) && (dRect.x1 == dWidth) && (dRect.y0 == dHeight);
bool isDepth = (flags & Device::DEPTH_BUFFER) && egl::Image::isDepth(source->getInternalFormat());
bool isStencil = (flags & Device::STENCIL_BUFFER) && (egl::Image::isDepth(source->getInternalFormat()) || egl::Image::isStencil(source->getInternalFormat()));
bool isColor = (flags & Device::COLOR_BUFFER) == Device::COLOR_BUFFER;
......@@ -611,7 +729,7 @@ namespace es2
{
if(source->hasDepth() && isDepth)
{
sw::byte *sourceBuffer = (sw::byte*)source->lockInternal(sRect.x0, sRect.y0, 0, LOCK_READONLY, PUBLIC);
sw::byte *sourceBuffer = (sw::byte*)source->lockInternal((int)sRect.x0, (int)sRect.y0, 0, LOCK_READONLY, PUBLIC);
sw::byte *destBuffer = (sw::byte*)dest->lockInternal(dRect.x0, dRect.y0, 0, LOCK_DISCARD, PUBLIC);
copyBuffer(sourceBuffer, destBuffer, dRect.width(), dRect.height(), source->getInternalPitchB(), dest->getInternalPitchB(), egl::Image::bytes(source->getInternalFormat()), flipX, flipY);
......@@ -622,7 +740,7 @@ namespace es2
if(source->hasStencil() && isStencil)
{
sw::byte *sourceBuffer = (sw::byte*)source->lockStencil(sRect.x0, sRect.y0, 0, PUBLIC);
sw::byte *sourceBuffer = (sw::byte*)source->lockStencil((int)sRect.x0, (int)sRect.y0, 0, PUBLIC);
sw::byte *destBuffer = (sw::byte*)dest->lockStencil(dRect.x0, dRect.y0, 0, PUBLIC);
copyBuffer(sourceBuffer, destBuffer, source->getWidth(), source->getHeight(), source->getStencilPitchB(), dest->getStencilPitchB(), egl::Image::bytes(source->getStencilFormat()), flipX, flipY);
......@@ -633,7 +751,7 @@ namespace es2
}
else if((flags & Device::COLOR_BUFFER) && !scaling && equalFormats && (!hasQuadLayout || fullCopy))
{
unsigned char *sourceBytes = (unsigned char*)source->lockInternal(sRect.x0, sRect.y0, sourceRect->slice, LOCK_READONLY, PUBLIC);
unsigned char *sourceBytes = (unsigned char*)source->lockInternal((int)sRect.x0, (int)sRect.y0, sourceRect->slice, LOCK_READONLY, PUBLIC);
unsigned char *destBytes = (unsigned char*)dest->lockInternal(dRect.x0, dRect.y0, destRect->slice, LOCK_READWRITE, PUBLIC);
unsigned int sourcePitch = source->getInternalPitchB();
unsigned int destPitch = dest->getInternalPitchB();
......@@ -667,7 +785,9 @@ namespace es2
{
swap(dRect.y0, dRect.y1);
}
blit(source, sRect, dest, dRect, scaling && (flags & Device::USE_FILTER), isStencil);
SliceRectF sRectF((float)sRect.x0, (float)sRect.y0, (float)sRect.x1, (float)sRect.y1, sRect.slice);
blit(source, sRectF, dest, dRect, scaling && (flags & Device::USE_FILTER), isStencil);
}
else
{
......@@ -886,7 +1006,32 @@ namespace es2
return false;
}
if(rect->x1 > (int)surface->getWidth() || rect->y1 > (int)surface->getHeight())
if(rect->x1 >(int)surface->getWidth() || rect->y1 >(int)surface->getHeight())
{
return false;
}
return true;
}
bool Device::validRectangle(const sw::RectF *rect, sw::Surface *surface)
{
if(!rect)
{
return true;
}
if(rect->x1 <= rect->x0 || rect->y1 <= rect->y0)
{
return false;
}
if(rect->x0 < 0 || rect->y0 < 0)
{
return false;
}
if(rect->x1 >(float)surface->getWidth() || rect->y1 >(float)surface->getHeight())
{
return false;
}
......
......@@ -85,6 +85,7 @@ namespace es2
bool bindViewport(); // Also adjusts for scissoring
bool validRectangle(const sw::Rect *rect, sw::Surface *surface);
bool validRectangle(const sw::RectF *rect, sw::Surface *surface);
void copyBuffer(sw::byte *sourceBuffer, sw::byte *destBuffer, unsigned int width, unsigned int height, unsigned int sourcePitch, unsigned int destPitch, unsigned int bytes, bool flipX, bool flipY);
......
......@@ -39,8 +39,7 @@ namespace sw
sw::Surface *color = sw::Surface::create(1, 1, 1, format, pixel, sw::Surface::bytes(format), sw::Surface::bytes(format));
Blitter::Options clearOptions = static_cast<sw::Blitter::Options>((rgbaMask & 0xF) | CLEAR_OPERATION);
SliceRect sRect(dRect);
sRect.slice = 0;
SliceRectF sRect((float)dRect.x0, (float)dRect.y0, (float)dRect.x1, (float)dRect.y1, 0);
blit(color, sRect, dest, dRect, clearOptions);
delete color;
}
......@@ -127,7 +126,7 @@ namespace sw
return true;
}
void Blitter::blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
void Blitter::blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
{
Blitter::Options options = WRITE_RGBA;
if(filter)
......@@ -141,7 +140,7 @@ namespace sw
blit(source, sRect, dest, dRect, options);
}
void Blitter::blit(Surface *source, const SliceRect &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
void Blitter::blit(Surface *source, const SliceRectF &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
{
if(dest->getInternalFormat() == FORMAT_NULL)
{
......@@ -153,7 +152,7 @@ namespace sw
return;
}
SliceRect sRect = sourceRect;
SliceRectF sRect = sourceRect;
SliceRect dRect = destRect;
bool flipX = destRect.x0 > destRect.x1;
......@@ -170,14 +169,14 @@ namespace sw
swap(sRect.y0, sRect.y1);
}
source->lockInternal(sRect.x0, sRect.y0, sRect.slice, sw::LOCK_READONLY, sw::PUBLIC);
source->lockInternal((int)sRect.x0, (int)sRect.y0, sRect.slice, sw::LOCK_READONLY, sw::PUBLIC);
dest->lockInternal(dRect.x0, dRect.y0, dRect.slice, sw::LOCK_WRITEONLY, sw::PUBLIC);
float w = static_cast<float>(sRect.x1 - sRect.x0) / static_cast<float>(dRect.x1 - dRect.x0);
float h = static_cast<float>(sRect.y1 - sRect.y0) / static_cast<float>(dRect.y1 - dRect.y0);
float w = sRect.width() / dRect.width();
float h = sRect.height() / dRect.height();
const float xStart = (float)sRect.x0 + 0.5f * w;
float y = (float)sRect.y0 + 0.5f * h;
const float xStart = sRect.x0 + 0.5f * w;
float y = sRect.y0 + 0.5f * h;
for(int j = dRect.y0; j < dRect.y1; j++)
{
......@@ -1261,8 +1260,10 @@ namespace sw
Int X0 = Max(Int(x0), 0);
Int Y0 = Max(Int(y0), 0);
Int X1 = IfThenElse(X0 + 1 >= sWidth, X0, X0 + 1);
Int Y1 = IfThenElse(Y0 + 1 >= sHeight, Y0, Y0 + 1);
Int X1 = X0 + 1;
Int Y1 = Y0 + 1;
X1 = IfThenElse(X1 >= sWidth, X0, X1);
Y1 = IfThenElse(Y1 >= sHeight, Y0, Y1);
Pointer<Byte> s00 = source + ComputeOffset(X0, Y0, sPitchB, srcBytes, srcQuadLayout);
Pointer<Byte> s01 = source + ComputeOffset(X1, Y0, sPitchB, srcBytes, srcQuadLayout);
......@@ -1276,11 +1277,11 @@ namespace sw
Float4 fx = Float4(x0 - Float(X0));
Float4 fy = Float4(y0 - Float(Y0));
Float4 ix = Float4(1.0f) - fx;
Float4 iy = Float4(1.0f) - fy;
color = c00 * (Float4(1.0f) - fx) * (Float4(1.0f) - fy) +
c01 * fx * (Float4(1.0f) - fy) +
c10 * (Float4(1.0f) - fx) * fy +
c11 * fx * fy;
color = (c00 * ix + c01 * fx) * iy +
(c10 * ix + c11 * fx) * fy;
}
if(!ApplyScaleAndClamp(color, state) || !write(color, d, state.destFormat, state.options))
......@@ -1299,12 +1300,12 @@ namespace sw
return function(L"BlitRoutine");
}
bool Blitter::blitReactor(Surface *source, const SliceRect &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
bool Blitter::blitReactor(Surface *source, const SliceRectF &sourceRect, Surface *dest, const SliceRect &destRect, const Blitter::Options& options)
{
ASSERT(!(options & CLEAR_OPERATION) || ((source->getWidth() == 1) && (source->getHeight() == 1) && (source->getDepth() == 1)));
Rect dRect = destRect;
Rect sRect = sourceRect;
RectF sRect = sourceRect;
if(destRect.x0 > destRect.x1)
{
swap(dRect.x0, dRect.x1);
......@@ -1358,10 +1359,10 @@ namespace sw
data.sPitchB = isStencil ? source->getStencilPitchB() : source->getPitchB(useSourceInternal);
data.dPitchB = isStencil ? dest->getStencilPitchB() : dest->getPitchB(useDestInternal);
data.w = 1.0f / (dRect.x1 - dRect.x0) * (sRect.x1 - sRect.x0);
data.h = 1.0f / (dRect.y1 - dRect.y0) * (sRect.y1 - sRect.y0);
data.x0 = (float)sRect.x0 + 0.5f * data.w;
data.y0 = (float)sRect.y0 + 0.5f * data.h;
data.w = sRect.width() / dRect.width();
data.h = sRect.height() / dRect.height();
data.x0 = sRect.x0 + 0.5f * data.w;
data.y0 = sRect.y0 + 0.5f * data.h;
data.x0d = dRect.x0;
data.x1d = dRect.x1;
......
......@@ -76,7 +76,7 @@ namespace sw
virtual ~Blitter();
void clear(void* pixel, sw::Format format, Surface *dest, const SliceRect &dRect, unsigned int rgbaMask);
void blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
void blit3D(Surface *source, Surface *dest);
private:
......@@ -89,8 +89,8 @@ namespace sw
static bool GetScale(float4& scale, Format format);
static bool ApplyScaleAndClamp(Float4& value, const BlitState& state);
static Int ComputeOffset(Int& x, Int& y, Int& pitchB, int bytes, bool quadLayout);
void blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
bool blitReactor(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
bool blitReactor(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, const Blitter::Options& options);
Routine *generate(BlitState &state);
RoutineCache<BlitState> *blitCache;
......
......@@ -682,7 +682,7 @@ namespace sw
}
}
void Renderer::blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
void Renderer::blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil)
{
blitter->blit(source, sRect, dest, dRect, filter, isStencil);
}
......
......@@ -322,7 +322,7 @@ namespace sw
void draw(DrawType drawType, unsigned int indexOffset, unsigned int count, bool update = true);
void clear(void *value, Format format, Surface *dest, const Rect &rect, unsigned int rgbaMask);
void blit(Surface *source, const SliceRect &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
void blit(Surface *source, const SliceRectF &sRect, Surface *dest, const SliceRect &dRect, bool filter, bool isStencil = false);
void blit3D(Surface *source, Surface *dest);
void setIndexBuffer(Resource *indexBuffer);
......
......@@ -42,14 +42,6 @@ namespace sw
unsigned int *Surface::palette = 0;
unsigned int Surface::paletteID = 0;
void Rect::clip(int minX, int minY, int maxX, int maxY)
{
x0 = clamp(x0, minX, maxX);
y0 = clamp(y0, minY, maxY);
x1 = clamp(x1, minX, maxX);
y1 = clamp(y1, minY, maxY);
}
void Surface::Buffer::write(int x, int y, int z, const Color<float> &color)
{
void *element = (unsigned char*)buffer + (x + border) * bytes + (y + border) * pitchB + z * sliceB;
......
......@@ -23,31 +23,43 @@ namespace sw
{
class Resource;
struct Rect
template <typename T> struct RectT
{
Rect() {}
Rect(int x0i, int y0i, int x1i, int y1i) : x0(x0i), y0(y0i), x1(x1i), y1(y1i) {}
RectT() {}
RectT(T x0i, T y0i, T x1i, T y1i) : x0(x0i), y0(y0i), x1(x1i), y1(y1i) {}
void clip(int minX, int minY, int maxX, int maxY);
int width() const { return x1 - x0; }
int height() const { return y1 - y0; }
int x0; // Inclusive
int y0; // Inclusive
int x1; // Exclusive
int y1; // Exclusive
void clip(T minX, T minY, T maxX, T maxY)
{
x0 = clamp(x0, minX, maxX);
y0 = clamp(y0, minY, maxY);
x1 = clamp(x1, minX, maxX);
y1 = clamp(y1, minY, maxY);
}
T width() const { return x1 - x0; }
T height() const { return y1 - y0; }
T x0; // Inclusive
T y0; // Inclusive
T x1; // Exclusive
T y1; // Exclusive
};
struct SliceRect : public Rect
typedef RectT<int> Rect;
typedef RectT<float> RectF;
template <typename T> struct SliceRectT : public RectT<T>
{
SliceRect() : slice(0) {}
SliceRect(const Rect& rect) : Rect(rect), slice(0) {}
SliceRect(const Rect& rect, int s) : Rect(rect), slice(s) {}
SliceRect(int x0, int y0, int x1, int y1, int s) : Rect(x0, y0, x1, y1), slice(s) {}
SliceRectT() : slice(0) {}
SliceRectT(const RectT<T>& rect) : RectT<T>(rect), slice(0) {}
SliceRectT(const RectT<T>& rect, int s) : RectT<T>(rect), slice(s) {}
SliceRectT(T x0, T y0, T x1, T y1, int s) : RectT<T>(x0, y0, x1, y1), slice(s) {}
int slice;
};
typedef SliceRectT<int> SliceRect;
typedef SliceRectT<float> SliceRectF;
enum Format : unsigned char
{
FORMAT_NULL,
......
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