Commit 73771b1f by Alexis Hetu Committed by Alexis Hétu

Allow blitting with out of bounds source coordinates

Removed early return on source rectangle validation, in order to allow out of bounds coordinates as an input to the blitter. Fixes 31 *out_of_bounds* failures in: dEQP-GLES3.functional.fbo.blit.default_framebuffer Also fixes 3 scissored blit failures in: dEQP-GLES3.functional.fragment_ops.scissor Change-Id: I0751678153aa0fc58bb7aa3a0270c358efb61330 Reviewed-on: https://swiftshader-review.googlesource.com/15388Tested-by: 's avatarAlexis Hétu <sugoi@google.com> Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com>
parent bc648b9e
......@@ -3947,7 +3947,7 @@ void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1
sw::SliceRect sourceRect;
sw::SliceRect destRect;
bool flipX = (srcX0 < srcX1) ^ (dstX0 < dstX1);
bool flipy = (srcY0 < srcY1) ^ (dstY0 < dstY1);
bool flipY = (srcY0 < srcY1) ^ (dstY0 < dstY1);
if(srcX0 < srcX1)
{
......@@ -3993,100 +3993,26 @@ void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1
destRect.y1 = dstY0;
}
sw::Rect sourceScissoredRect = sourceRect;
sw::RectF sourceScissoredRect(static_cast<float>(sourceRect.x0), static_cast<float>(sourceRect.y0),
static_cast<float>(sourceRect.x1), static_cast<float>(sourceRect.y1));
sw::Rect destScissoredRect = destRect;
if(mState.scissorTestEnabled) // Only write to parts of the destination framebuffer which pass the scissor test
{
if(destRect.x0 < mState.scissorX)
{
int xDiff = mState.scissorX - destRect.x0;
destScissoredRect.x0 = mState.scissorX;
sourceScissoredRect.x0 += xDiff;
}
if(destRect.x1 > mState.scissorX + mState.scissorWidth)
{
int xDiff = destRect.x1 - (mState.scissorX + mState.scissorWidth);
destScissoredRect.x1 = mState.scissorX + mState.scissorWidth;
sourceScissoredRect.x1 -= xDiff;
}
if(destRect.y0 < mState.scissorY)
{
int yDiff = mState.scissorY - destRect.y0;
destScissoredRect.y0 = mState.scissorY;
sourceScissoredRect.y0 += yDiff;
}
if(destRect.y1 > mState.scissorY + mState.scissorHeight)
{
int yDiff = destRect.y1 - (mState.scissorY + mState.scissorHeight);
destScissoredRect.y1 = mState.scissorY + mState.scissorHeight;
sourceScissoredRect.y1 -= yDiff;
}
sw::Rect scissorRect(mState.scissorX, mState.scissorY, mState.scissorX + mState.scissorWidth, mState.scissorY + mState.scissorHeight);
Device::ClipDstRect(sourceScissoredRect, destScissoredRect, scissorRect, flipX, flipY);
}
sw::Rect sourceTrimmedRect = sourceScissoredRect;
sw::Rect destTrimmedRect = destScissoredRect;
sw::SliceRectF sourceTrimmedRect = sourceScissoredRect;
sw::SliceRect destTrimmedRect = destScissoredRect;
// The source & destination rectangles also may need to be trimmed if they fall out of the bounds of
// the actual draw and read surfaces.
if(sourceTrimmedRect.x0 < 0)
{
int xDiff = 0 - sourceTrimmedRect.x0;
sourceTrimmedRect.x0 = 0;
destTrimmedRect.x0 += xDiff;
}
// The source & destination rectangles also may need to be trimmed if
// they fall out of the bounds of the actual draw and read surfaces.
sw::Rect sourceTrimRect(0, 0, readBufferWidth, readBufferHeight);
Device::ClipSrcRect(sourceTrimmedRect, destTrimmedRect, sourceTrimRect, flipX, flipY);
if(sourceTrimmedRect.x1 > readBufferWidth)
{
int xDiff = sourceTrimmedRect.x1 - readBufferWidth;
sourceTrimmedRect.x1 = readBufferWidth;
destTrimmedRect.x1 -= xDiff;
}
if(sourceTrimmedRect.y0 < 0)
{
int yDiff = 0 - sourceTrimmedRect.y0;
sourceTrimmedRect.y0 = 0;
destTrimmedRect.y0 += yDiff;
}
if(sourceTrimmedRect.y1 > readBufferHeight)
{
int yDiff = sourceTrimmedRect.y1 - readBufferHeight;
sourceTrimmedRect.y1 = readBufferHeight;
destTrimmedRect.y1 -= yDiff;
}
if(destTrimmedRect.x0 < 0)
{
int xDiff = 0 - destTrimmedRect.x0;
destTrimmedRect.x0 = 0;
sourceTrimmedRect.x0 += xDiff;
}
if(destTrimmedRect.x1 > drawBufferWidth)
{
int xDiff = destTrimmedRect.x1 - drawBufferWidth;
destTrimmedRect.x1 = drawBufferWidth;
sourceTrimmedRect.x1 -= xDiff;
}
if(destTrimmedRect.y0 < 0)
{
int yDiff = 0 - destTrimmedRect.y0;
destTrimmedRect.y0 = 0;
sourceTrimmedRect.y0 += yDiff;
}
if(destTrimmedRect.y1 > drawBufferHeight)
{
int yDiff = destTrimmedRect.y1 - drawBufferHeight;
destTrimmedRect.y1 = drawBufferHeight;
sourceTrimmedRect.y1 -= yDiff;
}
sw::Rect destTrimRect(0, 0, drawBufferWidth, drawBufferHeight);
Device::ClipDstRect(sourceTrimmedRect, destTrimmedRect, destTrimRect, flipX, flipY);
bool partialBufferCopy = false;
......@@ -4242,21 +4168,21 @@ void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1
if(blitRenderTarget || blitDepth || blitStencil)
{
if(flipX)
{
swap(destTrimmedRect.x0, destTrimmedRect.x1);
}
if(flipY)
{
swap(destTrimmedRect.y0, destTrimmedRect.y1);
}
if(blitRenderTarget)
{
egl::Image *readRenderTarget = readFramebuffer->getReadRenderTarget();
egl::Image *drawRenderTarget = drawFramebuffer->getRenderTarget(0);
if(flipX)
{
swap(destRect.x0, destRect.x1);
}
if(flipy)
{
swap(destRect.y0, destRect.y1);
}
bool success = device->stretchRect(readRenderTarget, &sourceRect, drawRenderTarget, &destRect, (filter ? Device::USE_FILTER : 0) | Device::COLOR_BUFFER);
bool success = device->stretchRect(readRenderTarget, &sourceTrimmedRect, drawRenderTarget, &destTrimmedRect, (filter ? Device::USE_FILTER : 0) | Device::COLOR_BUFFER);
readRenderTarget->release();
drawRenderTarget->release();
......@@ -4273,7 +4199,7 @@ void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1
egl::Image *readRenderTarget = readFramebuffer->getDepthBuffer();
egl::Image *drawRenderTarget = drawFramebuffer->getDepthBuffer();
bool success = device->stretchRect(readRenderTarget, &sourceRect, drawRenderTarget, &destRect, (filter ? Device::USE_FILTER : 0) | Device::DEPTH_BUFFER);
bool success = device->stretchRect(readRenderTarget, &sourceTrimmedRect, drawRenderTarget, &destTrimmedRect, (filter ? Device::USE_FILTER : 0) | Device::DEPTH_BUFFER);
readRenderTarget->release();
drawRenderTarget->release();
......@@ -4290,7 +4216,7 @@ void Context::blitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1
egl::Image *readRenderTarget = readFramebuffer->getStencilBuffer();
egl::Image *drawRenderTarget = drawFramebuffer->getStencilBuffer();
bool success = device->stretchRect(readRenderTarget, &sourceRect, drawRenderTarget, &destRect, (filter ? Device::USE_FILTER : 0) | Device::STENCIL_BUFFER);
bool success = device->stretchRect(readRenderTarget, &sourceTrimmedRect, drawRenderTarget, &destTrimmedRect, (filter ? Device::USE_FILTER : 0) | Device::STENCIL_BUFFER);
readRenderTarget->release();
drawRenderTarget->release();
......
......@@ -507,7 +507,7 @@ namespace es2
}
}
bool Device::stretchRect(sw::Surface *source, const sw::SliceRect *sourceRect, sw::Surface *dest, const sw::SliceRect *destRect, unsigned char flags)
bool Device::stretchRect(sw::Surface *source, const sw::SliceRectF *sourceRect, sw::Surface *dest, const sw::SliceRect *destRect, unsigned char flags)
{
if(!source || !dest)
{
......@@ -543,10 +543,10 @@ namespace es2
if(sourceRect)
{
sRect.x0 = (float)(sourceRect->x0);
sRect.x1 = (float)(sourceRect->x1);
sRect.y0 = (float)(sourceRect->y0);
sRect.y1 = (float)(sourceRect->y1);
sRect.x0 = sourceRect->x0;
sRect.x1 = sourceRect->x1;
sRect.y0 = sourceRect->y0;
sRect.y1 = sourceRect->y1;
sRect.slice = sourceRect->slice;
if(sRect.x0 > sRect.x1)
......@@ -589,118 +589,16 @@ 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;
}
sw::Rect srcClipRect(0, 0, sWidth, sHeight);
ClipSrcRect(sRect, dRect, srcClipRect, flipX, flipY);
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)
sw::Rect dstClipRect(0, 0, dWidth, dHeight);
ClipDstRect(sRect, dRect, dstClipRect, flipX, flipY);
if((sRect.width() == 0) || (sRect.height() == 0) ||
(dRect.width() == 0) || (dRect.height() == 0))
{
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;
return true; // no work to do
}
if(!validRectangle(&sRect, source) || !validRectangle(&dRect, dest))
......@@ -723,6 +621,7 @@ namespace es2
int sourcePitchB = isStencil ? source->getStencilPitchB() : source->getInternalPitchB();
int destPitchB = isStencil ? dest->getStencilPitchB() : dest->getInternalPitchB();
bool isOutOfBounds = (sRect.x0 < 0.0f) || (sRect.y0 < 0.0f) || (sRect.x1 > (float)sWidth) || (sRect.y1 > (float)sHeight);
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());
......@@ -739,7 +638,7 @@ namespace es2
alpha0xFF = true;
}
if(fullCopy && !scaling && equalFormats && !alpha0xFF && equalSlice && smallMargin && !flipX && !flipY)
if(fullCopy && !scaling && !isOutOfBounds && equalFormats && !alpha0xFF && equalSlice && smallMargin && !flipX && !flipY)
{
byte *sourceBuffer = isStencil ? (byte*)source->lockStencil(0, 0, 0, PUBLIC) : (byte*)source->lockInternal(0, 0, 0, LOCK_READONLY, PUBLIC);
byte *destBuffer = isStencil ? (byte*)dest->lockStencil(0, 0, 0, PUBLIC) : (byte*)dest->lockInternal(0, 0, 0, LOCK_DISCARD, PUBLIC);
......@@ -749,7 +648,7 @@ namespace es2
isStencil ? source->unlockStencil() : source->unlockInternal();
isStencil ? dest->unlockStencil() : dest->unlockInternal();
}
else if(isDepth && !scaling && equalFormats && !hasQuadLayout)
else if(isDepth && !scaling && !isOutOfBounds && equalFormats && !hasQuadLayout)
{
byte *sourceBuffer = (byte*)source->lockInternal((int)sRect.x0, (int)sRect.y0, 0, LOCK_READONLY, PUBLIC);
byte *destBuffer = (byte*)dest->lockInternal(dRect.x0, dRect.y0, 0, fullCopy ? LOCK_DISCARD : LOCK_WRITEONLY, PUBLIC);
......@@ -759,7 +658,7 @@ namespace es2
source->unlockInternal();
dest->unlockInternal();
}
else if((flags & Device::COLOR_BUFFER) && !scaling && equalFormats && !hasQuadLayout)
else if((flags & Device::COLOR_BUFFER) && !scaling && !isOutOfBounds && equalFormats && !hasQuadLayout)
{
byte *sourceBytes = (byte*)source->lockInternal((int)sRect.x0, (int)sRect.y0, sourceRect->slice, LOCK_READONLY, PUBLIC);
byte *destBytes = (byte*)dest->lockInternal(dRect.x0, dRect.y0, destRect->slice, fullCopy ? LOCK_DISCARD : LOCK_WRITEONLY, PUBLIC);
......@@ -796,8 +695,7 @@ namespace es2
swap(dRect.y0, dRect.y1);
}
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);
blit(source, sRect, dest, dRect, scaling && (flags & Device::USE_FILTER), isStencil);
}
else UNREACHABLE(false);
......@@ -1034,17 +932,127 @@ namespace es2
return false;
}
if(rect->x0 < 0 || rect->y0 < 0)
return true;
}
void Device::ClipDstRect(sw::RectF &srcRect, sw::Rect &dstRect, sw::Rect &clipRect, bool flipX, bool flipY)
{
if(dstRect.x0 < clipRect.x0)
{
return false;
float offset = (static_cast<float>(clipRect.x0 - dstRect.x0) / static_cast<float>(dstRect.width())) * srcRect.width();
if(flipX)
{
srcRect.x1 -= offset;
}
else
{
srcRect.x0 += offset;
}
dstRect.x0 = clipRect.x0;
}
if(rect->x1 >(float)surface->getWidth() || rect->y1 >(float)surface->getHeight())
if(dstRect.x1 > clipRect.x1)
{
return false;
float offset = (static_cast<float>(dstRect.x1 - clipRect.x1) / static_cast<float>(dstRect.width())) * srcRect.width();
if(flipX)
{
srcRect.x0 += offset;
}
else
{
srcRect.x1 -= offset;
}
dstRect.x1 = clipRect.x1;
}
if(dstRect.y0 < clipRect.y0)
{
float offset = (static_cast<float>(clipRect.y0 - dstRect.y0) / static_cast<float>(dstRect.height())) * srcRect.height();
if(flipY)
{
srcRect.y1 -= offset;
}
else
{
srcRect.y0 += offset;
}
dstRect.y0 = clipRect.y0;
}
if(dstRect.y1 > clipRect.y1)
{
float offset = (static_cast<float>(dstRect.y1 - clipRect.y1) / static_cast<float>(dstRect.height())) * srcRect.height();
if(flipY)
{
srcRect.y0 += offset;
}
else
{
srcRect.y1 -= offset;
}
dstRect.y1 = clipRect.y1;
}
}
return true;
void Device::ClipSrcRect(sw::RectF &srcRect, sw::Rect &dstRect, sw::Rect &clipRect, bool flipX, bool flipY)
{
if(srcRect.x0 < static_cast<float>(clipRect.x0))
{
float ratio = static_cast<float>(dstRect.width()) / srcRect.width();
float offsetf = roundf((static_cast<float>(clipRect.x0) - srcRect.x0) * ratio);
int offset = static_cast<int>(offsetf);
if(flipX)
{
dstRect.x1 -= offset;
}
else
{
dstRect.x0 += offset;
}
srcRect.x0 += offsetf / ratio;
}
if(srcRect.x1 > static_cast<float>(clipRect.x1))
{
float ratio = static_cast<float>(dstRect.width()) / srcRect.width();
float offsetf = roundf((srcRect.x1 - static_cast<float>(clipRect.x1)) * ratio);
int offset = static_cast<int>(offsetf);
if(flipX)
{
dstRect.x0 += offset;
}
else
{
dstRect.x1 -= offset;
}
srcRect.x1 -= offsetf / ratio;
}
if(srcRect.y0 < static_cast<float>(clipRect.y0))
{
float ratio = static_cast<float>(dstRect.height()) / srcRect.height();
float offsetf = roundf((static_cast<float>(clipRect.y0) - srcRect.y0) * ratio);
int offset = static_cast<int>(offsetf);
if(flipY)
{
dstRect.y1 -= offset;
}
else
{
dstRect.y0 += offset;
}
srcRect.y0 += offsetf / ratio;
}
if(srcRect.y1 > static_cast<float>(clipRect.y1))
{
float ratio = static_cast<float>(dstRect.height()) / srcRect.height();
float offsetf = roundf((srcRect.y1 - static_cast<float>(clipRect.y1)) * ratio);
int offset = static_cast<int>(offsetf);
if(flipY)
{
dstRect.y0 += offset;
}
else
{
dstRect.y1 -= offset;
}
srcRect.y1 -= offsetf / ratio;
}
}
void Device::finish()
......
......@@ -73,10 +73,13 @@ namespace es2
void setVertexShaderConstantF(unsigned int startRegister, const float *constantData, unsigned int count);
void setViewport(const Viewport &viewport);
bool stretchRect(sw::Surface *sourceSurface, const sw::SliceRect *sourceRect, sw::Surface *destSurface, const sw::SliceRect *destRect, unsigned char flags);
bool stretchRect(sw::Surface *sourceSurface, const sw::SliceRectF *sourceRect, sw::Surface *destSurface, const sw::SliceRect *destRect, unsigned char flags);
bool stretchCube(sw::Surface *sourceSurface, sw::Surface *destSurface);
void finish();
static void ClipDstRect(sw::RectF &srcRect, sw::Rect &dstRect, sw::Rect &clipRect, bool flipX = false, bool flipY = false);
static void ClipSrcRect(sw::RectF &srcRect, sw::Rect &dstRect, sw::Rect &clipRect, bool flipX = false, bool flipY = false);
private:
sw::Context *const context;
......
......@@ -460,7 +460,12 @@ bool Texture::copy(egl::Image *source, const sw::SliceRect &sourceRect, GLint xo
Device *device = getDevice();
sw::SliceRect destRect(xoffset, yoffset, xoffset + (sourceRect.x1 - sourceRect.x0), yoffset + (sourceRect.y1 - sourceRect.y0), zoffset);
bool success = device->stretchRect(source, &sourceRect, dest, &destRect, Device::ALL_BUFFERS);
sw::SliceRectF sourceRectF(static_cast<float>(sourceRect.x0),
static_cast<float>(sourceRect.y0),
static_cast<float>(sourceRect.x1),
static_cast<float>(sourceRect.y1),
sourceRect.slice);
bool success = device->stretchRect(source, &sourceRectF, dest, &destRect, Device::ALL_BUFFERS);
if(!success)
{
......@@ -1997,7 +2002,7 @@ void Texture2DArray::generateMipmaps()
GLsizei srch = image[i - 1]->getHeight();
for(int z = 0; z < depth; ++z)
{
sw::SliceRect srcRect(0, 0, srcw, srch, z);
sw::SliceRectF srcRect(0.0f, 0.0f, static_cast<float>(srcw), static_cast<float>(srch), z);
sw::SliceRect dstRect(0, 0, w, h, z);
getDevice()->stretchRect(image[i - 1], &srcRect, image[i], &dstRect, Device::ALL_BUFFERS | Device::USE_FILTER);
}
......
......@@ -1275,6 +1275,12 @@ namespace sw
Int X = Int(x);
Int Y = Int(y);
if(state.clampToEdge)
{
X = Clamp(X, 0, sWidth - 1);
Y = Clamp(Y, 0, sHeight - 1);
}
Pointer<Byte> s = source + ComputeOffset(X, Y, sPitchB, srcBytes, srcQuadLayout);
if(!read(color, s, state))
......@@ -1296,6 +1302,12 @@ namespace sw
Int X = Int(x);
Int Y = Int(y);
if(state.clampToEdge)
{
X = Clamp(X, 0, sWidth - 1);
Y = Clamp(Y, 0, sHeight - 1);
}
Pointer<Byte> s = source + ComputeOffset(X, Y, sPitchB, srcBytes, srcQuadLayout);
if(!read(color, s, state))
......@@ -1305,8 +1317,17 @@ namespace sw
}
else // Bilinear filtering
{
Float x0 = x - 0.5f;
Float y0 = y - 0.5f;
Float X = x;
Float Y = y;
if(state.clampToEdge)
{
X = Float(Clamp(Int(x), 0, sWidth - 1));
Y = Float(Clamp(Int(y), 0, sHeight - 1));
}
Float x0 = X - 0.5f;
Float y0 = Y - 0.5f;
Int X0 = Max(Int(x0), 0);
Int Y0 = Max(Int(y0), 0);
......@@ -1379,6 +1400,10 @@ namespace sw
}
State state(options);
state.clampToEdge = (sourceRect.x0 < 0.0f) ||
(sourceRect.y0 < 0.0f) ||
(sourceRect.x1 > (float)source->getWidth()) ||
(sourceRect.y1 > (float)source->getHeight());
bool useSourceInternal = !source->isExternalDirty();
bool useDestInternal = !dest->isExternalDirty();
......
......@@ -29,9 +29,9 @@ namespace sw
{
Options() = default;
Options(bool filter, bool useStencil, bool convertSRGB)
: writeMask(0xF), clearOperation(false), filter(filter), useStencil(useStencil), convertSRGB(convertSRGB) {}
: writeMask(0xF), clearOperation(false), filter(filter), useStencil(useStencil), convertSRGB(convertSRGB), clampToEdge(false) {}
Options(unsigned int writeMask)
: writeMask(writeMask), clearOperation(true), filter(false), useStencil(false), convertSRGB(true) {}
: writeMask(writeMask), clearOperation(true), filter(false), useStencil(false), convertSRGB(true), clampToEdge(false) {}
union
{
......@@ -50,6 +50,7 @@ namespace sw
bool filter : 1;
bool useStencil : 1;
bool convertSRGB : 1;
bool clampToEdge : 1;
};
struct State : Options
......
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