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: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/renderer/gl/FramebufferGL.cpp b/src/libANGLE/renderer/gl/FramebufferGL.cpp
index 8d84c60..24b5c31 100644
--- a/src/libANGLE/renderer/gl/FramebufferGL.cpp
+++ b/src/libANGLE/renderer/gl/FramebufferGL.cpp
@@ -25,10 +25,55 @@
#include "platform/Platform.h"
using namespace gl;
+using angle::CheckedNumeric;
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,
const FunctionsGL *functions,
StateManagerGL *stateManager,
@@ -234,14 +279,35 @@
const PixelPackState &packState = context->getGLState().getPackState();
mStateManager->setPixelPackState(packState);
- mStateManager->bindFramebuffer(GL_READ_FRAMEBUFFER, mFramebufferID);
-
nativegl::ReadPixelsFormat readPixelsFormat =
nativegl::GetReadPixelsFormat(mFunctions, mWorkarounds, format, type);
- mFunctions->readPixels(area.x, area.y, area.width, area.height, readPixelsFormat.format,
- readPixelsFormat.type, pixels);
+ GLenum readFormat = readPixelsFormat.format;
+ 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,
@@ -396,4 +462,75 @@
}
}
}
+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