Commit c5cacd60 by Corentin Wallez Committed by Commit Bot

Implement a separate last row texture unpack buffer upload workaround

When uploading textures from an unpack buffer, some drivers expect an extra row paading, causing them to think the pixel buffer is not big enough. We work around this by uploading the last row separately. BUG=angleproject:1512 Change-Id: I52fb8b35dc450b957f1fafb0b405c81bf0504157 Reviewed-on: https://chromium-review.googlesource.com/385193 Commit-Queue: Corentin Wallez <cwallez@chromium.org> Reviewed-by: 's avatarCorentin Wallez <cwallez@chromium.org>
parent 44d0a736
......@@ -831,6 +831,13 @@ const InternalFormat &GetInternalFormatInfo(GLenum internalFormat)
}
}
GLuint InternalFormat::computePixelBytes(GLenum formatType) const
{
const auto &typeInfo = GetTypeInfo(formatType);
GLuint components = typeInfo.specialInterpretation ? 1u : componentCount;
return components * typeInfo.bytes;
}
gl::ErrorOrResult<GLuint> InternalFormat::computeRowPitch(GLenum formatType,
GLsizei width,
GLint alignment,
......@@ -843,11 +850,8 @@ gl::ErrorOrResult<GLuint> InternalFormat::computeRowPitch(GLenum formatType,
return computeCompressedImageSize(formatType, gl::Extents(width, 1, 1));
}
const auto &typeInfo = GetTypeInfo(formatType);
CheckedNumeric<GLuint> checkedComponents(typeInfo.specialInterpretation ? 1u : componentCount);
CheckedNumeric<GLuint> checkedTypeBytes(typeInfo.bytes);
CheckedNumeric<GLuint> checkedWidth(rowLength > 0 ? rowLength : width);
CheckedNumeric<GLuint> checkedRowBytes = checkedWidth * checkedComponents * checkedTypeBytes;
CheckedNumeric<GLuint> checkedRowBytes = checkedWidth * computePixelBytes(formatType);
ASSERT(alignment > 0 && isPow2(alignment));
CheckedNumeric<GLuint> checkedAlignment(alignment);
......@@ -926,25 +930,53 @@ gl::ErrorOrResult<GLuint> InternalFormat::computeUnpackSize(
return computeCompressedImageSize(formatType, size);
}
base::CheckedNumeric<GLuint> checkedGroups(unpack.rowLength > 0 ? unpack.rowLength
: size.width);
base::CheckedNumeric<GLuint> checkedRows(unpack.imageHeight > 0 ? unpack.imageHeight
: size.height);
CheckedNumeric<GLuint> rowPitch;
CheckedNumeric<GLuint> depthPitch;
ANGLE_TRY_RESULT(computeRowPitch(formatType, size.width, unpack.alignment, unpack.rowLength),
rowPitch);
ANGLE_TRY_RESULT(computeDepthPitch(formatType, size.width, size.height, unpack.alignment,
unpack.rowLength, unpack.imageHeight),
depthPitch);
// Compute the groups of all the layers in (0,depth-1)
auto layerGroups = checkedGroups * checkedRows * (size.depth - 1);
CheckedNumeric<GLuint> depthMinusOne = size.depth - 1;
CheckedNumeric<GLuint> heightMinusOne = size.height - 1;
CheckedNumeric<GLuint> pixelBytes = computePixelBytes(formatType);
// Compute the groups in the last layer (for non-3D textures, the only one)
auto lastLayerGroups = checkedGroups * (size.height - 1) + size.width;
// The total size is the sum times the bytes per pixel.
auto totalSize = (layerGroups + lastLayerGroups) * pixelBytes;
CheckedNumeric<GLuint> totalSize = depthMinusOne * depthPitch;
totalSize += heightMinusOne * rowPitch;
totalSize += size.width * pixelBytes;
ANGLE_TRY_CHECKED_MATH(totalSize);
return totalSize.ValueOrDie();
}
gl::ErrorOrResult<GLuint> InternalFormat::computeUnpackEndByte(GLenum formatType,
const gl::Extents &size,
const gl::PixelUnpackState &unpack,
bool applySkipImages) const
{
GLuint rowPitch;
GLuint depthPitch;
CheckedNumeric<GLuint> checkedSkipBytes;
CheckedNumeric<GLuint> checkedCopyBytes;
ANGLE_TRY_RESULT(computeRowPitch(formatType, size.width, unpack.alignment, unpack.rowLength),
rowPitch);
ANGLE_TRY_RESULT(computeDepthPitch(formatType, size.width, size.height, unpack.alignment,
unpack.rowLength, unpack.imageHeight),
depthPitch);
ANGLE_TRY_RESULT(computeSkipBytes(rowPitch, depthPitch, unpack.skipImages, unpack.skipRows,
unpack.skipPixels, applySkipImages),
checkedSkipBytes);
ANGLE_TRY_RESULT(computeUnpackSize(formatType, size, unpack), checkedCopyBytes);
CheckedNumeric<GLuint> endByte = checkedCopyBytes + checkedSkipBytes;
ANGLE_TRY_CHECKED_MATH(endByte);
return endByte.ValueOrDie();
}
GLenum GetSizedInternalFormat(GLenum internalFormat, GLenum type)
{
const InternalFormat &formatInfo = GetInternalFormatInfo(internalFormat);
......
......@@ -47,6 +47,8 @@ struct InternalFormat
{
InternalFormat();
GLuint computePixelBytes(GLenum formatType) const;
gl::ErrorOrResult<GLuint> computeRowPitch(GLenum formatType,
GLsizei width,
GLint alignment,
......@@ -69,6 +71,10 @@ struct InternalFormat
const gl::Extents &size,
const gl::PixelUnpackState &unpack) const;
gl::ErrorOrResult<GLuint> computeUnpackEndByte(GLenum formatType,
const gl::Extents &size,
const gl::PixelUnpackState &unpack,
bool applySkipImages) const;
bool isLUMA() const;
bool operator==(const InternalFormat &other) const;
......
......@@ -120,6 +120,14 @@ class TextureGL : public TextureImpl
const gl::PixelUnpackState &unpack,
const uint8_t *pixels);
gl::Error setSubImageAlignmentWorkaround(GLenum target,
size_t level,
const gl::Box &area,
GLenum format,
GLenum type,
const gl::PixelUnpackState &unpack,
const uint8_t *pixels);
const FunctionsGL *mFunctions;
const WorkaroundsGL &mWorkarounds;
StateManagerGL *mStateManager;
......
......@@ -80,6 +80,20 @@ struct WorkaroundsGL
// On Intel Mac, calculation of loop conditions in for and while loop has bug.
// Add "&& true" to the end of the condition expression to work around the bug.
bool addAndTrueToLoopCondition;
// When uploading textures from an unpack buffer, some drivers count an extra row padding when
// checking if the pixel unpack buffer is big enough. Tracking bug: http://anglebug.com/1512
// For example considering the pixel buffer below where in memory, each row data (D) of the
// texture is followed by some unused data (the dots):
// +-------+--+
// |DDDDDDD|..|
// |DDDDDDD|..|
// |DDDDDDD|..|
// |DDDDDDD|..|
// +-------A--B
// 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.
bool unpackLastRowSeparatelyForPaddingInclusion;
};
}
......
......@@ -902,6 +902,12 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround
workarounds->unpackOverlappingRowsSeparatelyUnpackBuffer = vendor == VENDOR_ID_NVIDIA;
workarounds->initializeCurrentVertexAttributes = vendor == VENDOR_ID_NVIDIA;
#if defined(ANGLE_PLATFORM_APPLE)
workarounds->unpackLastRowSeparatelyForPaddingInclusion = true;
#else
workarounds->unpackLastRowSeparatelyForPaddingInclusion = vendor == VENDOR_ID_NVIDIA;
#endif
}
}
......
......@@ -486,44 +486,19 @@ bool ValidateES3TexImageParametersBase(Context *context,
const gl::Extents size(width, height, depth);
const auto &unpack = context->getGLState().getUnpackState();
auto copyBytesOrErr = formatInfo.computeUnpackSize(type, size, unpack);
if (copyBytesOrErr.isError())
{
context->handleError(copyBytesOrErr.getError());
return false;
}
CheckedNumeric<size_t> checkedCopyBytes(copyBytesOrErr.getResult());
CheckedNumeric<size_t> checkedOffset(reinterpret_cast<size_t>(pixels));
checkedCopyBytes += checkedOffset;
auto rowPitchOrErr =
formatInfo.computeRowPitch(type, width, unpack.alignment, unpack.rowLength);
if (rowPitchOrErr.isError())
{
context->handleError(rowPitchOrErr.getError());
return false;
}
auto depthPitchOrErr = formatInfo.computeDepthPitch(type, width, height, unpack.alignment,
unpack.rowLength, unpack.imageHeight);
if (depthPitchOrErr.isError())
{
context->handleError(depthPitchOrErr.getError());
return false;
}
bool targetIs3D = target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY;
auto skipBytesOrErr = formatInfo.computeSkipBytes(
rowPitchOrErr.getResult(), depthPitchOrErr.getResult(), unpack.skipImages,
unpack.skipRows, unpack.skipPixels, targetIs3D);
if (skipBytesOrErr.isError())
auto endByteOrErr = formatInfo.computeUnpackEndByte(type, size, unpack, targetIs3D);
if (endByteOrErr.isError())
{
context->handleError(skipBytesOrErr.getError());
context->handleError(endByteOrErr.getError());
return false;
}
checkedCopyBytes += skipBytesOrErr.getResult();
CheckedNumeric<size_t> checkedEndByte(endByteOrErr.getResult());
CheckedNumeric<size_t> checkedOffset(reinterpret_cast<size_t>(pixels));
checkedEndByte += checkedOffset;
if (!checkedCopyBytes.IsValid() ||
(checkedCopyBytes.ValueOrDie() > static_cast<size_t>(pixelUnpackBuffer->getSize())))
if (!checkedEndByte.IsValid() ||
(checkedEndByte.ValueOrDie() > static_cast<size_t>(pixelUnpackBuffer->getSize())))
{
// Overflow past the end of the buffer
context->handleError(Error(GL_INVALID_OPERATION));
......
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