Commit 9a8d366a by Corentin Wallez Committed by Commit Bot

FramebufferGL: add readPixels workarounds

Implements workarounds for: - The pack state making rows overlap in memory, which causes crashes on some drivers. - The driver adding an extra last row padding when checking if the pixel pack buffer is large enough for the readPixels. BUG=angleproject:1512 Change-Id: I120ff58649bb523e8b01da6ef03d8fcadaf076b2 Reviewed-on: https://chromium-review.googlesource.com/388029 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarGeoff Lang <geofflang@chromium.org>
parent 0f40d235
...@@ -2569,6 +2569,11 @@ void Context::readPixels(GLint x, ...@@ -2569,6 +2569,11 @@ void Context::readPixels(GLint x,
GLenum type, GLenum type,
GLvoid *pixels) GLvoid *pixels)
{ {
if (width == 0 || height == 0)
{
return;
}
syncStateForReadPixels(); syncStateForReadPixels();
Framebuffer *framebufferObject = mGLState.getReadFramebuffer(); Framebuffer *framebufferObject = mGLState.getReadFramebuffer();
...@@ -2607,6 +2612,11 @@ void Context::copyTexSubImage2D(GLenum target, ...@@ -2607,6 +2612,11 @@ void Context::copyTexSubImage2D(GLenum target,
GLsizei width, GLsizei width,
GLsizei height) GLsizei height)
{ {
if (width == 0 || height == 0)
{
return;
}
// Only sync the read FBO // Only sync the read FBO
mGLState.syncDirtyObject(GL_READ_FRAMEBUFFER); mGLState.syncDirtyObject(GL_READ_FRAMEBUFFER);
...@@ -2629,6 +2639,11 @@ void Context::copyTexSubImage3D(GLenum target, ...@@ -2629,6 +2639,11 @@ void Context::copyTexSubImage3D(GLenum target,
GLsizei width, GLsizei width,
GLsizei height) GLsizei height)
{ {
if (width == 0 || height == 0)
{
return;
}
// Only sync the read FBO // Only sync the read FBO
mGLState.syncDirtyObject(GL_READ_FRAMEBUFFER); mGLState.syncDirtyObject(GL_READ_FRAMEBUFFER);
......
...@@ -25,10 +25,55 @@ ...@@ -25,10 +25,55 @@
#include "platform/Platform.h" #include "platform/Platform.h"
using namespace gl; using namespace gl;
using angle::CheckedNumeric;
namespace rx namespace rx
{ {
namespace
{
gl::ErrorOrResult<bool> ShouldApplyLastRowPaddingWorkaround(const gl::Rectangle &area,
const gl::PixelPackState &pack,
GLenum format,
GLenum type,
const void *pixels)
{
if (pack.pixelBuffer.get() == nullptr)
{
return false;
}
// We are using an pack buffer, compute what the driver thinks is going to be the last
// byte written. If it is past the end of the buffer, we will need to use the workaround
// otherwise the driver will generate INVALID_OPERATION.
CheckedNumeric<size_t> checkedEndByte;
CheckedNumeric<size_t> pixelBytes;
size_t rowPitch;
gl::Extents size(area.width, area.height, 1);
const gl::InternalFormat &glFormat =
gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type));
ANGLE_TRY_RESULT(glFormat.computePackEndByte(type, size, pack), checkedEndByte);
ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, area.width, pack.alignment, pack.rowLength),
rowPitch);
pixelBytes = glFormat.computePixelBytes(type);
checkedEndByte += reinterpret_cast<intptr_t>(pixels);
// At this point checkedEndByte is the actual last byte written.
// The driver adds an extra row padding (if any), mimic it.
ANGLE_TRY_CHECKED_MATH(pixelBytes);
if (pixelBytes.ValueOrDie() * size.width < rowPitch)
{
checkedEndByte += rowPitch - pixelBytes * size.width;
}
ANGLE_TRY_CHECKED_MATH(checkedEndByte);
return checkedEndByte.ValueOrDie() > static_cast<size_t>(pack.pixelBuffer->getSize());
}
} // anonymous namespace
FramebufferGL::FramebufferGL(const FramebufferState &state, FramebufferGL::FramebufferGL(const FramebufferState &state,
const FunctionsGL *functions, const FunctionsGL *functions,
StateManagerGL *stateManager, StateManagerGL *stateManager,
...@@ -234,14 +279,35 @@ Error FramebufferGL::readPixels(ContextImpl *context, ...@@ -234,14 +279,35 @@ Error FramebufferGL::readPixels(ContextImpl *context,
const PixelPackState &packState = context->getGLState().getPackState(); const PixelPackState &packState = context->getGLState().getPackState();
mStateManager->setPixelPackState(packState); mStateManager->setPixelPackState(packState);
mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferID);
nativegl::ReadPixelsFormat readPixelsFormat = nativegl::ReadPixelsFormat readPixelsFormat =
nativegl::GetReadPixelsFormat(mFunctions, mWorkarounds, format, type); nativegl::GetReadPixelsFormat(mFunctions, mWorkarounds, format, type);
mFunctions->readPixels(area.x, area.y, area.width, area.height, readPixelsFormat.format, GLenum readFormat = readPixelsFormat.format;
readPixelsFormat.type, pixels); GLenum readType = readPixelsFormat.type;
return Error(GL_NO_ERROR); mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferID);
if (mWorkarounds.packOverlappingRowsSeparatelyPackBuffer && packState.pixelBuffer.get() &&
packState.rowLength != 0 && packState.rowLength < area.width)
{
return readPixelsRowByRowWorkaround(area, readFormat, readType, packState, pixels);
}
if (mWorkarounds.packLastRowSeparatelyForPaddingInclusion)
{
bool apply;
ANGLE_TRY_RESULT(
ShouldApplyLastRowPaddingWorkaround(area, packState, readFormat, readType, pixels),
apply);
if (apply)
{
return readPixelsPaddingWorkaround(area, readFormat, readType, packState, pixels);
}
}
mFunctions->readPixels(area.x, area.y, area.width, area.height, readFormat, readType, pixels);
return gl::NoError();
} }
Error FramebufferGL::blit(ContextImpl *context, Error FramebufferGL::blit(ContextImpl *context,
...@@ -396,4 +462,75 @@ void FramebufferGL::syncClearBufferState(GLenum buffer, GLint drawBuffer) ...@@ -396,4 +462,75 @@ void FramebufferGL::syncClearBufferState(GLenum buffer, GLint drawBuffer)
} }
} }
} }
gl::Error FramebufferGL::readPixelsRowByRowWorkaround(const gl::Rectangle &area,
GLenum format,
GLenum type,
const gl::PixelPackState &pack,
GLvoid *pixels) const
{
intptr_t offset = reinterpret_cast<intptr_t>(pixels);
const gl::InternalFormat &glFormat =
gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type));
GLuint rowBytes = 0;
ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, area.width, pack.alignment, pack.rowLength),
rowBytes);
GLuint skipBytes = 0;
ANGLE_TRY_RESULT(
glFormat.computeSkipBytes(rowBytes, 0, 0, pack.skipRows, pack.skipPixels, false),
skipBytes);
gl::PixelPackState directPack;
directPack.pixelBuffer = pack.pixelBuffer;
directPack.alignment = 1;
mStateManager->setPixelPackState(directPack);
directPack.pixelBuffer.set(nullptr);
offset += skipBytes;
for (GLint row = 0; row < area.height; ++row)
{
mFunctions->readPixels(area.x, row + area.y, area.width, 1, format, type,
reinterpret_cast<GLvoid *>(offset));
offset += row * rowBytes;
}
return gl::NoError();
}
gl::Error FramebufferGL::readPixelsPaddingWorkaround(const gl::Rectangle &area,
GLenum format,
GLenum type,
const gl::PixelPackState &pack,
GLvoid *pixels) const
{
const gl::InternalFormat &glFormat =
gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type));
GLuint rowBytes = 0;
ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, area.width, pack.alignment, pack.rowLength),
rowBytes);
GLuint skipBytes = 0;
ANGLE_TRY_RESULT(
glFormat.computeSkipBytes(rowBytes, 0, 0, pack.skipRows, pack.skipPixels, false),
skipBytes);
// Get all by the last row
if (area.height > 1)
{
mFunctions->readPixels(area.x, area.y, area.width, area.height - 1, format, type, pixels);
}
// Get the last row manually
gl::PixelPackState directPack;
directPack.pixelBuffer = pack.pixelBuffer;
directPack.alignment = 1;
mStateManager->setPixelPackState(directPack);
directPack.pixelBuffer.set(nullptr);
intptr_t lastRowOffset =
reinterpret_cast<intptr_t>(pixels) + skipBytes + (area.height - 1) * rowBytes;
mFunctions->readPixels(area.x, area.y + area.height - 1, area.width, 1, format, type,
reinterpret_cast<GLvoid *>(lastRowOffset));
return gl::NoError();
}
} // namespace rx } // namespace rx
...@@ -85,6 +85,18 @@ class FramebufferGL : public FramebufferImpl ...@@ -85,6 +85,18 @@ class FramebufferGL : public FramebufferImpl
void syncClearState(GLbitfield mask); void syncClearState(GLbitfield mask);
void syncClearBufferState(GLenum buffer, GLint drawBuffer); void syncClearBufferState(GLenum buffer, GLint drawBuffer);
gl::Error readPixelsRowByRowWorkaround(const gl::Rectangle &area,
GLenum format,
GLenum type,
const gl::PixelPackState &pack,
GLvoid *pixels) const;
gl::Error readPixelsPaddingWorkaround(const gl::Rectangle &area,
GLenum format,
GLenum type,
const gl::PixelPackState &pack,
GLvoid *pixels) const;
const FunctionsGL *mFunctions; const FunctionsGL *mFunctions;
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
const WorkaroundsGL &mWorkarounds; const WorkaroundsGL &mWorkarounds;
......
...@@ -120,7 +120,7 @@ gl::ErrorOrResult<bool> ShouldApplyLastRowPaddingWorkaround(const gl::Box &area, ...@@ -120,7 +120,7 @@ gl::ErrorOrResult<bool> ShouldApplyLastRowPaddingWorkaround(const gl::Box &area,
checkedEndByte += reinterpret_cast<intptr_t>(pixels); checkedEndByte += reinterpret_cast<intptr_t>(pixels);
// At the point checkedEndByte is the actual last byte read. // At this point checkedEndByte is the actual last byte read.
// The driver adds an extra row padding (if any), mimic it. // The driver adds an extra row padding (if any), mimic it.
ANGLE_TRY_CHECKED_MATH(pixelBytes); ANGLE_TRY_CHECKED_MATH(pixelBytes);
if (pixelBytes.ValueOrDie() * size.width < rowPitch) if (pixelBytes.ValueOrDie() * size.width < rowPitch)
...@@ -194,6 +194,12 @@ gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat ...@@ -194,6 +194,12 @@ gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat
// The rows overlap in unpack memory. Upload the texture row by row to work around // The rows overlap in unpack memory. Upload the texture row by row to work around
// driver bug. // driver bug.
reserveTexImageToBeFilled(target, level, internalFormat, size, format, type); reserveTexImageToBeFilled(target, level, internalFormat, size, format, type);
if (size.width == 0 || size.height == 0 || size.depth == 0)
{
return gl::NoError();
}
gl::Box area(0, 0, 0, size.width, size.height, size.depth); gl::Box area(0, 0, 0, size.width, size.height, size.depth);
return setSubImageRowByRowWorkaround(target, level, area, format, type, unpack, pixels); return setSubImageRowByRowWorkaround(target, level, area, format, type, unpack, pixels);
} }
...@@ -211,8 +217,13 @@ gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat ...@@ -211,8 +217,13 @@ gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat
if (apply) if (apply)
{ {
reserveTexImageToBeFilled(target, level, internalFormat, size, format, type); reserveTexImageToBeFilled(target, level, internalFormat, size, format, type);
return setSubImageAlignmentWorkaround(target, level, area, format, type, unpack,
pixels); if (size.width == 0 || size.height == 0 || size.depth == 0)
{
return gl::NoError();
}
return setSubImagePaddingWorkaround(target, level, area, format, type, unpack, pixels);
} }
} }
...@@ -301,8 +312,7 @@ gl::Error TextureGL::setSubImage(GLenum target, size_t level, const gl::Box &are ...@@ -301,8 +312,7 @@ gl::Error TextureGL::setSubImage(GLenum target, size_t level, const gl::Box &are
// by uploading the last row (and last level if 3D) separately. // by uploading the last row (and last level if 3D) separately.
if (apply) if (apply)
{ {
return setSubImageAlignmentWorkaround(target, level, area, format, type, unpack, return setSubImagePaddingWorkaround(target, level, area, format, type, unpack, pixels);
pixels);
} }
} }
...@@ -383,13 +393,13 @@ gl::Error TextureGL::setSubImageRowByRowWorkaround(GLenum target, ...@@ -383,13 +393,13 @@ gl::Error TextureGL::setSubImageRowByRowWorkaround(GLenum target,
return gl::NoError(); return gl::NoError();
} }
gl::Error TextureGL::setSubImageAlignmentWorkaround(GLenum target, gl::Error TextureGL::setSubImagePaddingWorkaround(GLenum target,
size_t level, size_t level,
const gl::Box &area, const gl::Box &area,
GLenum format, GLenum format,
GLenum type, GLenum type,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
const uint8_t *pixels) const uint8_t *pixels)
{ {
const gl::InternalFormat &glFormat = const gl::InternalFormat &glFormat =
gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type)); gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type));
......
...@@ -120,13 +120,13 @@ class TextureGL : public TextureImpl ...@@ -120,13 +120,13 @@ class TextureGL : public TextureImpl
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
const uint8_t *pixels); const uint8_t *pixels);
gl::Error setSubImageAlignmentWorkaround(GLenum target, gl::Error setSubImagePaddingWorkaround(GLenum target,
size_t level, size_t level,
const gl::Box &area, const gl::Box &area,
GLenum format, GLenum format,
GLenum type, GLenum type,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
const uint8_t *pixels); const uint8_t *pixels);
const FunctionsGL *mFunctions; const FunctionsGL *mFunctions;
const WorkaroundsGL &mWorkarounds; const WorkaroundsGL &mWorkarounds;
......
...@@ -69,6 +69,8 @@ struct WorkaroundsGL ...@@ -69,6 +69,8 @@ struct WorkaroundsGL
// In the case of unpacking from a pixel unpack buffer, unpack overlapping rows row by row. // In the case of unpacking from a pixel unpack buffer, unpack overlapping rows row by row.
bool unpackOverlappingRowsSeparatelyUnpackBuffer; bool unpackOverlappingRowsSeparatelyUnpackBuffer;
// In the case of packing to a pixel pack buffer, pack overlapping rows row by row.
bool packOverlappingRowsSeparatelyPackBuffer;
// During initialization, assign the current vertex attributes to the spec-mandated defaults. // During initialization, assign the current vertex attributes to the spec-mandated defaults.
bool initializeCurrentVertexAttributes; bool initializeCurrentVertexAttributes;
...@@ -94,6 +96,8 @@ struct WorkaroundsGL ...@@ -94,6 +96,8 @@ struct WorkaroundsGL
// The last pixel read will be A, but the driver will think it is B, causing it to generate an // The last pixel read will be A, but the driver will think it is B, causing it to generate an
// error when the pixel buffer is just big enough. // error when the pixel buffer is just big enough.
bool unpackLastRowSeparatelyForPaddingInclusion; bool unpackLastRowSeparatelyForPaddingInclusion;
// Equivalent workaround when uploading data from a pixel pack buffer.
bool packLastRowSeparatelyForPaddingInclusion;
}; };
} }
......
...@@ -900,13 +900,16 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround ...@@ -900,13 +900,16 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround
workarounds->alwaysCallUseProgramAfterLink = true; workarounds->alwaysCallUseProgramAfterLink = true;
workarounds->unpackOverlappingRowsSeparatelyUnpackBuffer = vendor == VENDOR_ID_NVIDIA; workarounds->unpackOverlappingRowsSeparatelyUnpackBuffer = vendor == VENDOR_ID_NVIDIA;
workarounds->packOverlappingRowsSeparatelyPackBuffer = vendor == VENDOR_ID_NVIDIA;
workarounds->initializeCurrentVertexAttributes = vendor == VENDOR_ID_NVIDIA; workarounds->initializeCurrentVertexAttributes = vendor == VENDOR_ID_NVIDIA;
#if defined(ANGLE_PLATFORM_APPLE) #if defined(ANGLE_PLATFORM_APPLE)
workarounds->unpackLastRowSeparatelyForPaddingInclusion = true; workarounds->unpackLastRowSeparatelyForPaddingInclusion = true;
workarounds->packLastRowSeparatelyForPaddingInclusion = true;
#else #else
workarounds->unpackLastRowSeparatelyForPaddingInclusion = vendor == VENDOR_ID_NVIDIA; workarounds->unpackLastRowSeparatelyForPaddingInclusion = vendor == VENDOR_ID_NVIDIA;
workarounds->packLastRowSeparatelyForPaddingInclusion = vendor == VENDOR_ID_NVIDIA;
#endif #endif
} }
......
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