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) ...@@ -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, gl::ErrorOrResult<GLuint> InternalFormat::computeRowPitch(GLenum formatType,
GLsizei width, GLsizei width,
GLint alignment, GLint alignment,
...@@ -843,11 +850,8 @@ gl::ErrorOrResult<GLuint> InternalFormat::computeRowPitch(GLenum formatType, ...@@ -843,11 +850,8 @@ gl::ErrorOrResult<GLuint> InternalFormat::computeRowPitch(GLenum formatType,
return computeCompressedImageSize(formatType, gl::Extents(width, 1, 1)); 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> checkedWidth(rowLength > 0 ? rowLength : width);
CheckedNumeric<GLuint> checkedRowBytes = checkedWidth * checkedComponents * checkedTypeBytes; CheckedNumeric<GLuint> checkedRowBytes = checkedWidth * computePixelBytes(formatType);
ASSERT(alignment > 0 && isPow2(alignment)); ASSERT(alignment > 0 && isPow2(alignment));
CheckedNumeric<GLuint> checkedAlignment(alignment); CheckedNumeric<GLuint> checkedAlignment(alignment);
...@@ -926,25 +930,53 @@ gl::ErrorOrResult<GLuint> InternalFormat::computeUnpackSize( ...@@ -926,25 +930,53 @@ gl::ErrorOrResult<GLuint> InternalFormat::computeUnpackSize(
return computeCompressedImageSize(formatType, size); return computeCompressedImageSize(formatType, size);
} }
base::CheckedNumeric<GLuint> checkedGroups(unpack.rowLength > 0 ? unpack.rowLength CheckedNumeric<GLuint> rowPitch;
: size.width); CheckedNumeric<GLuint> depthPitch;
base::CheckedNumeric<GLuint> checkedRows(unpack.imageHeight > 0 ? unpack.imageHeight ANGLE_TRY_RESULT(computeRowPitch(formatType, size.width, unpack.alignment, unpack.rowLength),
: size.height); rowPitch);
ANGLE_TRY_RESULT(computeDepthPitch(formatType, size.width, size.height, unpack.alignment,
// Compute the groups of all the layers in (0,depth-1) unpack.rowLength, unpack.imageHeight),
auto layerGroups = checkedGroups * checkedRows * (size.depth - 1); depthPitch);
// Compute the groups in the last layer (for non-3D textures, the only one) CheckedNumeric<GLuint> depthMinusOne = size.depth - 1;
auto lastLayerGroups = checkedGroups * (size.height - 1) + size.width; CheckedNumeric<GLuint> heightMinusOne = size.height - 1;
CheckedNumeric<GLuint> pixelBytes = computePixelBytes(formatType);
// The total size is the sum times the bytes per pixel. CheckedNumeric<GLuint> totalSize = depthMinusOne * depthPitch;
auto totalSize = (layerGroups + lastLayerGroups) * pixelBytes; totalSize += heightMinusOne * rowPitch;
totalSize += size.width * pixelBytes;
ANGLE_TRY_CHECKED_MATH(totalSize); ANGLE_TRY_CHECKED_MATH(totalSize);
return totalSize.ValueOrDie(); 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) GLenum GetSizedInternalFormat(GLenum internalFormat, GLenum type)
{ {
const InternalFormat &formatInfo = GetInternalFormatInfo(internalFormat); const InternalFormat &formatInfo = GetInternalFormatInfo(internalFormat);
......
...@@ -47,6 +47,8 @@ struct InternalFormat ...@@ -47,6 +47,8 @@ struct InternalFormat
{ {
InternalFormat(); InternalFormat();
GLuint computePixelBytes(GLenum formatType) const;
gl::ErrorOrResult<GLuint> computeRowPitch(GLenum formatType, gl::ErrorOrResult<GLuint> computeRowPitch(GLenum formatType,
GLsizei width, GLsizei width,
GLint alignment, GLint alignment,
...@@ -69,6 +71,10 @@ struct InternalFormat ...@@ -69,6 +71,10 @@ struct InternalFormat
const gl::Extents &size, const gl::Extents &size,
const gl::PixelUnpackState &unpack) const; 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 isLUMA() const;
bool operator==(const InternalFormat &other) const; bool operator==(const InternalFormat &other) const;
......
...@@ -21,20 +21,25 @@ ...@@ -21,20 +21,25 @@
#include "libANGLE/renderer/gl/WorkaroundsGL.h" #include "libANGLE/renderer/gl/WorkaroundsGL.h"
#include "libANGLE/renderer/gl/formatutilsgl.h" #include "libANGLE/renderer/gl/formatutilsgl.h"
using angle::CheckedNumeric;
namespace rx namespace rx
{ {
static bool UseTexImage2D(GLenum textureType) namespace
{
bool UseTexImage2D(GLenum textureType)
{ {
return textureType == GL_TEXTURE_2D || textureType == GL_TEXTURE_CUBE_MAP; return textureType == GL_TEXTURE_2D || textureType == GL_TEXTURE_CUBE_MAP;
} }
static bool UseTexImage3D(GLenum textureType) bool UseTexImage3D(GLenum textureType)
{ {
return textureType == GL_TEXTURE_2D_ARRAY || textureType == GL_TEXTURE_3D; return textureType == GL_TEXTURE_2D_ARRAY || textureType == GL_TEXTURE_3D;
} }
static bool CompatibleTextureTarget(GLenum textureType, GLenum textureTarget) bool CompatibleTextureTarget(GLenum textureType, GLenum textureTarget)
{ {
if (textureType != GL_TEXTURE_CUBE_MAP) if (textureType != GL_TEXTURE_CUBE_MAP)
{ {
...@@ -46,12 +51,12 @@ static bool CompatibleTextureTarget(GLenum textureType, GLenum textureTarget) ...@@ -46,12 +51,12 @@ static bool CompatibleTextureTarget(GLenum textureType, GLenum textureTarget)
} }
} }
static bool IsLUMAFormat(GLenum format) bool IsLUMAFormat(GLenum format)
{ {
return format == GL_LUMINANCE || format == GL_ALPHA || format == GL_LUMINANCE_ALPHA; return format == GL_LUMINANCE || format == GL_ALPHA || format == GL_LUMINANCE_ALPHA;
} }
static LUMAWorkaroundGL GetLUMAWorkaroundInfo(const gl::InternalFormat &originalFormatInfo, LUMAWorkaroundGL GetLUMAWorkaroundInfo(const gl::InternalFormat &originalFormatInfo,
GLenum destinationFormat) GLenum destinationFormat)
{ {
if (IsLUMAFormat(originalFormatInfo.format)) if (IsLUMAFormat(originalFormatInfo.format))
...@@ -67,23 +72,69 @@ static LUMAWorkaroundGL GetLUMAWorkaroundInfo(const gl::InternalFormat &original ...@@ -67,23 +72,69 @@ static LUMAWorkaroundGL GetLUMAWorkaroundInfo(const gl::InternalFormat &original
} }
} }
static bool IsDepthStencilFormat(GLenum format) bool IsDepthStencilFormat(GLenum format)
{ {
return format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL; return format == GL_DEPTH_COMPONENT || format == GL_DEPTH_STENCIL;
} }
static bool GetDepthStencilWorkaround(const gl::InternalFormat &originalFormatInfo) bool GetDepthStencilWorkaround(const gl::InternalFormat &originalFormatInfo)
{ {
return IsDepthStencilFormat(originalFormatInfo.format); return IsDepthStencilFormat(originalFormatInfo.format);
} }
static LevelInfoGL GetLevelInfo(GLenum originalFormat, GLenum destinationFormat) LevelInfoGL GetLevelInfo(GLenum originalFormat, GLenum destinationFormat)
{ {
const gl::InternalFormat &originalFormatInfo = gl::GetInternalFormatInfo(originalFormat); const gl::InternalFormat &originalFormatInfo = gl::GetInternalFormatInfo(originalFormat);
return LevelInfoGL(originalFormat, GetDepthStencilWorkaround(originalFormatInfo), return LevelInfoGL(originalFormat, GetDepthStencilWorkaround(originalFormatInfo),
GetLUMAWorkaroundInfo(originalFormatInfo, destinationFormat)); GetLUMAWorkaroundInfo(originalFormatInfo, destinationFormat));
} }
gl::ErrorOrResult<bool> ShouldApplyLastRowPaddingWorkaround(const gl::Box &area,
const gl::PixelUnpackState &unpack,
GLenum format,
GLenum type,
bool useTexImage3D,
const uint8_t *pixels)
{
if (unpack.pixelBuffer.get() == nullptr)
{
return false;
}
// We are using an unpack buffer, compute what the driver thinks is going to be the last
// byte read. If it is past the end of the buffer, we will need to use the workaround
// otherwise the driver will generate INVALID_OPERATION and not do the texture specification
// and upload.
CheckedNumeric<size_t> checkedEndByte;
CheckedNumeric<size_t> pixelBytes;
size_t rowPitch;
gl::Extents size(area.width, area.height, area.depth);
const gl::InternalFormat &glFormat =
gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type));
ANGLE_TRY_RESULT(glFormat.computeUnpackEndByte(type, size, unpack, useTexImage3D),
checkedEndByte);
ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, area.width, unpack.alignment, unpack.rowLength),
rowPitch);
pixelBytes = glFormat.computePixelBytes(type);
checkedEndByte += reinterpret_cast<intptr_t>(pixels);
// At the point checkedEndByte is the actual last byte read.
// 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>(unpack.pixelBuffer->getSize());
}
} // anonymous namespace
LUMAWorkaroundGL::LUMAWorkaroundGL() : LUMAWorkaroundGL(false, GL_NONE) LUMAWorkaroundGL::LUMAWorkaroundGL() : LUMAWorkaroundGL(false, GL_NONE)
{ {
} }
...@@ -144,12 +195,29 @@ gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat ...@@ -144,12 +195,29 @@ gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat
// driver bug. // driver bug.
reserveTexImageToBeFilled(target, level, internalFormat, size, format, type); reserveTexImageToBeFilled(target, level, internalFormat, size, format, type);
gl::Box area(0, 0, 0, size.width, size.height, size.depth); gl::Box area(0, 0, 0, size.width, size.height, size.depth);
ANGLE_TRY(setSubImageRowByRowWorkaround(target, level, area, format, type, unpack, pixels)); return setSubImageRowByRowWorkaround(target, level, area, format, type, unpack, pixels);
} }
else
if (mWorkarounds.unpackLastRowSeparatelyForPaddingInclusion)
{ {
setImageHelper(target, level, internalFormat, size, format, type, pixels); bool apply;
gl::Box area(0, 0, 0, size.width, size.height, size.depth);
ANGLE_TRY_RESULT(ShouldApplyLastRowPaddingWorkaround(area, unpack, format, type,
UseTexImage3D(mState.mTarget), pixels),
apply);
// The driver will think the pixel buffer doesn't have enough data, work around this bug
// by uploading the last row (and last level if 3D) separately.
if (apply)
{
reserveTexImageToBeFilled(target, level, internalFormat, size, format, type);
return setSubImageAlignmentWorkaround(target, level, area, format, type, unpack,
pixels);
} }
}
setImageHelper(target, level, internalFormat, size, format, type, pixels);
return gl::NoError(); return gl::NoError();
} }
...@@ -212,32 +280,46 @@ gl::Error TextureGL::setSubImage(GLenum target, size_t level, const gl::Box &are ...@@ -212,32 +280,46 @@ gl::Error TextureGL::setSubImage(GLenum target, size_t level, const gl::Box &are
nativegl::TexSubImageFormat texSubImageFormat = nativegl::TexSubImageFormat texSubImageFormat =
nativegl::GetTexSubImageFormat(mFunctions, mWorkarounds, format, type); nativegl::GetTexSubImageFormat(mFunctions, mWorkarounds, format, type);
ASSERT(mLevelInfo[level].lumaWorkaround.enabled ==
GetLevelInfo(format, texSubImageFormat.format).lumaWorkaround.enabled);
mStateManager->bindTexture(mState.mTarget, mTextureID); mStateManager->bindTexture(mState.mTarget, mTextureID);
if (mWorkarounds.unpackOverlappingRowsSeparatelyUnpackBuffer && unpack.pixelBuffer.get() && if (mWorkarounds.unpackOverlappingRowsSeparatelyUnpackBuffer && unpack.pixelBuffer.get() &&
unpack.rowLength != 0 && unpack.rowLength < area.width) unpack.rowLength != 0 && unpack.rowLength < area.width)
{ {
ANGLE_TRY(setSubImageRowByRowWorkaround(target, level, area, format, type, unpack, pixels)); return setSubImageRowByRowWorkaround(target, level, area, format, type, unpack, pixels);
} }
else if (UseTexImage2D(mState.mTarget))
if (mWorkarounds.unpackLastRowSeparatelyForPaddingInclusion)
{
bool apply;
ANGLE_TRY_RESULT(ShouldApplyLastRowPaddingWorkaround(area, unpack, format, type,
UseTexImage3D(mState.mTarget), pixels),
apply);
// The driver will think the pixel buffer doesn't have enough data, work around this bug
// by uploading the last row (and last level if 3D) separately.
if (apply)
{
return setSubImageAlignmentWorkaround(target, level, area, format, type, unpack,
pixels);
}
}
if (UseTexImage2D(mState.mTarget))
{ {
ASSERT(area.z == 0 && area.depth == 1); ASSERT(area.z == 0 && area.depth == 1);
mFunctions->texSubImage2D(target, static_cast<GLint>(level), area.x, area.y, area.width, mFunctions->texSubImage2D(target, static_cast<GLint>(level), area.x, area.y, area.width,
area.height, texSubImageFormat.format, texSubImageFormat.type, area.height, texSubImageFormat.format, texSubImageFormat.type,
pixels); pixels);
} }
else if (UseTexImage3D(mState.mTarget)) else
{ {
ASSERT(UseTexImage3D(mState.mTarget));
mFunctions->texSubImage3D(target, static_cast<GLint>(level), area.x, area.y, area.z, mFunctions->texSubImage3D(target, static_cast<GLint>(level), area.x, area.y, area.z,
area.width, area.height, area.depth, texSubImageFormat.format, area.width, area.height, area.depth, texSubImageFormat.format,
texSubImageFormat.type, pixels); texSubImageFormat.type, pixels);
} }
else
{
UNREACHABLE();
}
ASSERT(mLevelInfo[level].lumaWorkaround.enabled ==
GetLevelInfo(format, texSubImageFormat.format).lumaWorkaround.enabled);
return gl::Error(GL_NO_ERROR); return gl::Error(GL_NO_ERROR);
} }
...@@ -250,25 +332,25 @@ gl::Error TextureGL::setSubImageRowByRowWorkaround(GLenum target, ...@@ -250,25 +332,25 @@ gl::Error TextureGL::setSubImageRowByRowWorkaround(GLenum target,
const gl::PixelUnpackState &unpack, const gl::PixelUnpackState &unpack,
const uint8_t *pixels) const uint8_t *pixels)
{ {
gl::PixelUnpackState unpackToUse; gl::PixelUnpackState directUnpack;
unpackToUse.pixelBuffer = unpack.pixelBuffer; directUnpack.pixelBuffer = unpack.pixelBuffer;
mStateManager->setPixelUnpackState(unpackToUse); directUnpack.alignment = 1;
unpackToUse.pixelBuffer.set(nullptr); mStateManager->setPixelUnpackState(directUnpack);
const gl::Format &glFormat = mState.getImageDesc(mState.mTarget, level).format; directUnpack.pixelBuffer.set(nullptr);
const gl::InternalFormat &glFormat =
gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type));
GLuint rowBytes = 0; GLuint rowBytes = 0;
ANGLE_TRY_RESULT( ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, area.width, unpack.alignment, unpack.rowLength),
glFormat.info->computeRowPitch(GL_NONE, area.width, unpack.alignment, unpack.rowLength),
rowBytes); rowBytes);
GLuint imageBytes = 0; GLuint imageBytes = 0;
ANGLE_TRY_RESULT( ANGLE_TRY_RESULT(glFormat.computeDepthPitch(type, area.width, area.height, unpack.alignment,
glFormat.info->computeDepthPitch(GL_NONE, area.width, area.height, unpack.alignment,
unpack.rowLength, unpack.imageHeight), unpack.rowLength, unpack.imageHeight),
imageBytes); imageBytes);
bool useTexImage3D = UseTexImage3D(mState.mTarget); bool useTexImage3D = UseTexImage3D(mState.mTarget);
GLuint skipBytes = 0; GLuint skipBytes = 0;
ANGLE_TRY_RESULT( ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, imageBytes, unpack.skipImages,
glFormat.info->computeSkipBytes(rowBytes, imageBytes, unpack.skipImages, unpack.skipRows, unpack.skipRows, unpack.skipPixels, useTexImage3D),
unpack.skipPixels, useTexImage3D),
skipBytes); skipBytes);
const uint8_t *pixelsWithSkip = pixels + skipBytes; const uint8_t *pixelsWithSkip = pixels + skipBytes;
...@@ -287,8 +369,9 @@ gl::Error TextureGL::setSubImageRowByRowWorkaround(GLenum target, ...@@ -287,8 +369,9 @@ gl::Error TextureGL::setSubImageRowByRowWorkaround(GLenum target,
} }
} }
} }
else if (UseTexImage2D(mState.mTarget)) else
{ {
ASSERT(UseTexImage2D(mState.mTarget));
for (GLint row = 0; row < area.height; ++row) for (GLint row = 0; row < area.height; ++row)
{ {
GLint byteOffset = row * rowBytes; GLint byteOffset = row * rowBytes;
...@@ -297,10 +380,91 @@ gl::Error TextureGL::setSubImageRowByRowWorkaround(GLenum target, ...@@ -297,10 +380,91 @@ gl::Error TextureGL::setSubImageRowByRowWorkaround(GLenum target,
area.width, 1, format, type, rowPixels); area.width, 1, format, type, rowPixels);
} }
} }
return gl::NoError();
}
gl::Error TextureGL::setSubImageAlignmentWorkaround(GLenum target,
size_t level,
const gl::Box &area,
GLenum format,
GLenum type,
const gl::PixelUnpackState &unpack,
const uint8_t *pixels)
{
const gl::InternalFormat &glFormat =
gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type));
GLuint rowBytes = 0;
ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, area.width, unpack.alignment, unpack.rowLength),
rowBytes);
GLuint imageBytes = 0;
ANGLE_TRY_RESULT(glFormat.computeDepthPitch(type, area.width, area.height, unpack.alignment,
unpack.rowLength, unpack.imageHeight),
imageBytes);
bool useTexImage3D = UseTexImage3D(mState.mTarget);
GLuint skipBytes = 0;
ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, imageBytes, unpack.skipImages,
unpack.skipRows, unpack.skipPixels, useTexImage3D),
skipBytes);
gl::PixelUnpackState directUnpack;
directUnpack.pixelBuffer = unpack.pixelBuffer;
directUnpack.alignment = 1;
if (useTexImage3D)
{
// Upload all but the last slice
if (area.depth > 1)
{
mFunctions->texSubImage3D(target, static_cast<GLint>(level), area.x, area.y, area.z,
area.width, area.height, area.depth - 1, format, type,
pixels);
}
// Upload the last slice but its last row
if (area.height > 1)
{
// Do not include skipBytes in the last image pixel start offset as it will be done by
// the driver
GLint lastImageOffset = (area.depth - 1) * imageBytes;
const GLubyte *lastImagePixels = pixels + lastImageOffset;
mFunctions->texSubImage3D(target, static_cast<GLint>(level), area.x, area.y,
area.z + area.depth - 1, area.width, area.height - 1, 1,
format, type, lastImagePixels);
}
// Upload the last row of the last slice "manually"
mStateManager->setPixelUnpackState(directUnpack);
GLint lastRowOffset =
skipBytes + (area.depth - 1) * imageBytes + (area.height - 1) * rowBytes;
const GLubyte *lastRowPixels = pixels + lastRowOffset;
mFunctions->texSubImage3D(target, static_cast<GLint>(level), area.x,
area.y + area.height - 1, area.z + area.depth - 1, area.width, 1,
1, format, type, lastRowPixels);
}
else else
{ {
UNREACHABLE(); ASSERT(UseTexImage2D(mState.mTarget));
// Upload all but the last row
if (area.height > 1)
{
mFunctions->texSubImage2D(target, static_cast<GLint>(level), area.x, area.y, area.width,
area.height - 1, format, type, pixels);
}
// Upload the last row "manually"
mStateManager->setPixelUnpackState(directUnpack);
GLint lastRowOffset = skipBytes + (area.height - 1) * rowBytes;
const GLubyte *lastRowPixels = pixels + lastRowOffset;
mFunctions->texSubImage2D(target, static_cast<GLint>(level), area.x,
area.y + area.height - 1, area.width, 1, format, type,
lastRowPixels);
} }
directUnpack.pixelBuffer.set(nullptr);
return gl::NoError(); return gl::NoError();
} }
......
...@@ -120,6 +120,14 @@ class TextureGL : public TextureImpl ...@@ -120,6 +120,14 @@ 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,
size_t level,
const gl::Box &area,
GLenum format,
GLenum type,
const gl::PixelUnpackState &unpack,
const uint8_t *pixels);
const FunctionsGL *mFunctions; const FunctionsGL *mFunctions;
const WorkaroundsGL &mWorkarounds; const WorkaroundsGL &mWorkarounds;
StateManagerGL *mStateManager; StateManagerGL *mStateManager;
......
...@@ -80,6 +80,20 @@ struct WorkaroundsGL ...@@ -80,6 +80,20 @@ struct WorkaroundsGL
// On Intel Mac, calculation of loop conditions in for and while loop has bug. // 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. // Add "&& true" to the end of the condition expression to work around the bug.
bool addAndTrueToLoopCondition; 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 ...@@ -902,6 +902,12 @@ void GenerateWorkarounds(const FunctionsGL *functions, WorkaroundsGL *workaround
workarounds->unpackOverlappingRowsSeparatelyUnpackBuffer = vendor == VENDOR_ID_NVIDIA; workarounds->unpackOverlappingRowsSeparatelyUnpackBuffer = vendor == VENDOR_ID_NVIDIA;
workarounds->initializeCurrentVertexAttributes = 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, ...@@ -486,44 +486,19 @@ bool ValidateES3TexImageParametersBase(Context *context,
const gl::Extents size(width, height, depth); const gl::Extents size(width, height, depth);
const auto &unpack = context->getGLState().getUnpackState(); 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; bool targetIs3D = target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY;
auto skipBytesOrErr = formatInfo.computeSkipBytes( auto endByteOrErr = formatInfo.computeUnpackEndByte(type, size, unpack, targetIs3D);
rowPitchOrErr.getResult(), depthPitchOrErr.getResult(), unpack.skipImages, if (endByteOrErr.isError())
unpack.skipRows, unpack.skipPixels, targetIs3D);
if (skipBytesOrErr.isError())
{ {
context->handleError(skipBytesOrErr.getError()); context->handleError(endByteOrErr.getError());
return false; 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() || if (!checkedEndByte.IsValid() ||
(checkedCopyBytes.ValueOrDie() > static_cast<size_t>(pixelUnpackBuffer->getSize()))) (checkedEndByte.ValueOrDie() > static_cast<size_t>(pixelUnpackBuffer->getSize())))
{ {
// Overflow past the end of the buffer // Overflow past the end of the buffer
context->handleError(Error(GL_INVALID_OPERATION)); 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