Factor code between PixelUnpackState and PixelPackState

BUG=angleproject:1512

Change-Id: I4c60472d216bfc5198e635d70fd197a5738dde98
Reviewed-on: https://chromium-review.googlesource.com/390133
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Geoff Lang <geofflang@chromium.org>
diff --git a/src/libANGLE/angletypes.h b/src/libANGLE/angletypes.h
index e6923c8..0c5c7ea 100644
--- a/src/libANGLE/angletypes.h
+++ b/src/libANGLE/angletypes.h
@@ -197,59 +197,39 @@
 bool operator==(const SamplerState &a, const SamplerState &b);
 bool operator!=(const SamplerState &a, const SamplerState &b);
 
-struct PixelUnpackState
+struct PixelStoreStateBase
 {
     BindingPointer<Buffer> pixelBuffer;
-    GLint alignment;
-    GLint rowLength;
-    GLint skipRows;
-    GLint skipPixels;
-    GLint imageHeight;
-    GLint skipImages;
-
-    PixelUnpackState()
-        : alignment(4),
-          rowLength(0),
-          skipRows(0),
-          skipPixels(0),
-          imageHeight(0),
-          skipImages(0)
-    {}
-
-    PixelUnpackState(GLint alignmentIn, GLint rowLengthIn)
-        : alignment(alignmentIn),
-          rowLength(rowLengthIn),
-          skipRows(0),
-          skipPixels(0),
-          imageHeight(0),
-          skipImages(0)
-    {}
+    GLint alignment   = 4;
+    GLint rowLength   = 0;
+    GLint skipRows    = 0;
+    GLint skipPixels  = 0;
+    GLint imageHeight = 0;
+    GLint skipImages  = 0;
 };
 
-struct PixelPackState
+struct PixelUnpackState : PixelStoreStateBase
 {
-    BindingPointer<Buffer> pixelBuffer;
-    GLint alignment;
-    bool reverseRowOrder;
-    GLint rowLength;
-    GLint skipRows;
-    GLint skipPixels;
+    PixelUnpackState() {}
 
-    PixelPackState()
-        : alignment(4),
-          reverseRowOrder(false),
-          rowLength(0),
-          skipRows(0),
-          skipPixels(0)
-    {}
+    PixelUnpackState(GLint alignmentIn, GLint rowLengthIn)
+    {
+        alignment = alignmentIn;
+        rowLength = rowLengthIn;
+    }
+};
 
-    explicit PixelPackState(GLint alignmentIn, bool reverseRowOrderIn)
-        : alignment(alignmentIn),
-          reverseRowOrder(reverseRowOrderIn),
-          rowLength(0),
-          skipRows(0),
-          skipPixels(0)
-    {}
+struct PixelPackState : PixelStoreStateBase
+{
+    PixelPackState() {}
+
+    PixelPackState(GLint alignmentIn, bool reverseRowOrderIn)
+        : reverseRowOrder(reverseRowOrderIn)
+    {
+        alignment = alignmentIn;
+    }
+
+    bool reverseRowOrder = false;
 };
 
 // Used in Program and VertexArray.
diff --git a/src/libANGLE/formatutils.cpp b/src/libANGLE/formatutils.cpp
index 4222d72..b9c5b4e 100644
--- a/src/libANGLE/formatutils.cpp
+++ b/src/libANGLE/formatutils.cpp
@@ -858,7 +858,7 @@
     return components * typeInfo.bytes;
 }
 
-gl::ErrorOrResult<GLuint> InternalFormat::computeRowPitch(GLenum formatType,
+ErrorOrResult<GLuint> InternalFormat::computeRowPitch(GLenum formatType,
                                                           GLsizei width,
                                                           GLint alignment,
                                                           GLint rowLength) const
@@ -867,7 +867,7 @@
     if (compressed)
     {
         ASSERT(rowLength == 0);
-        return computeCompressedImageSize(formatType, gl::Extents(width, 1, 1));
+        return computeCompressedImageSize(formatType, Extents(width, 1, 1));
     }
 
     CheckedNumeric<GLuint> checkedWidth(rowLength > 0 ? rowLength : width);
@@ -880,7 +880,7 @@
     return aligned.ValueOrDie();
 }
 
-gl::ErrorOrResult<GLuint> InternalFormat::computeDepthPitch(GLenum formatType,
+ErrorOrResult<GLuint> InternalFormat::computeDepthPitch(GLenum formatType,
                                                             GLsizei width,
                                                             GLsizei height,
                                                             GLint alignment,
@@ -898,8 +898,8 @@
     return depthPitch.ValueOrDie();
 }
 
-gl::ErrorOrResult<GLuint> InternalFormat::computeCompressedImageSize(GLenum formatType,
-                                                                     const gl::Extents &size) const
+ErrorOrResult<GLuint> InternalFormat::computeCompressedImageSize(GLenum formatType,
+                                                                     const Extents &size) const
 {
     CheckedNumeric<GLuint> checkedWidth(size.width);
     CheckedNumeric<GLuint> checkedHeight(size.height);
@@ -915,21 +915,19 @@
     return bytes.ValueOrDie();
 }
 
-gl::ErrorOrResult<GLuint> InternalFormat::computeSkipBytes(GLuint rowPitch,
+ErrorOrResult<GLuint> InternalFormat::computeSkipBytes(GLuint rowPitch,
                                                            GLuint depthPitch,
-                                                           GLint skipImages,
-                                                           GLint skipRows,
-                                                           GLint skipPixels,
-                                                           bool applySkipImages) const
+                                                           const PixelStoreStateBase &state,
+                                                           bool is3D) const
 {
     CheckedNumeric<GLuint> checkedRowPitch(rowPitch);
     CheckedNumeric<GLuint> checkedDepthPitch(depthPitch);
-    CheckedNumeric<GLuint> checkedSkipImages(static_cast<GLuint>(skipImages));
-    CheckedNumeric<GLuint> checkedSkipRows(static_cast<GLuint>(skipRows));
-    CheckedNumeric<GLuint> checkedSkipPixels(static_cast<GLuint>(skipPixels));
+    CheckedNumeric<GLuint> checkedSkipImages(static_cast<GLuint>(state.skipImages));
+    CheckedNumeric<GLuint> checkedSkipRows(static_cast<GLuint>(state.skipRows));
+    CheckedNumeric<GLuint> checkedSkipPixels(static_cast<GLuint>(state.skipPixels));
     CheckedNumeric<GLuint> checkedPixelBytes(pixelBytes);
     auto checkedSkipImagesBytes = checkedSkipImages * checkedDepthPitch;
-    if (!applySkipImages)
+    if (!is3D)
     {
         checkedSkipImagesBytes = 0;
     }
@@ -939,108 +937,46 @@
     return skipBytes.ValueOrDie();
 }
 
-gl::ErrorOrResult<GLuint> InternalFormat::computePackSize(GLenum formatType,
-                                                          const gl::Extents &size,
-                                                          const gl::PixelPackState &pack) const
+ErrorOrResult<GLuint> InternalFormat::computePackUnpackEndByte(
+    GLenum formatType,
+    const Extents &size,
+    const PixelStoreStateBase &state,
+    bool is3D) const
 {
-    ASSERT(!compressed);
-
-    if (size.height == 0)
+    GLuint depthPitch = 0;
+    if (is3D)
     {
-        return 0;
+        ANGLE_TRY_RESULT(computeDepthPitch(formatType, size.width, size.height, state.alignment,
+                                           state.rowLength, state.imageHeight),
+                         depthPitch);
     }
 
-    CheckedNumeric<GLuint> rowPitch;
-    ANGLE_TRY_RESULT(computeRowPitch(formatType, size.width, pack.alignment, pack.rowLength),
+    GLuint rowPitch = 0;
+    ANGLE_TRY_RESULT(computeRowPitch(formatType, size.width, state.alignment, state.rowLength),
                      rowPitch);
 
-    CheckedNumeric<GLuint> heightMinusOne = size.height - 1;
-    CheckedNumeric<GLuint> bytes          = computePixelBytes(formatType);
-
-    CheckedNumeric<GLuint> totalSize = heightMinusOne * rowPitch;
-    totalSize += size.width * bytes;
-
-    ANGLE_TRY_CHECKED_MATH(totalSize);
-
-    return totalSize.ValueOrDie();
-}
-
-gl::ErrorOrResult<GLuint> InternalFormat::computeUnpackSize(
-    GLenum formatType,
-    const gl::Extents &size,
-    const gl::PixelUnpackState &unpack) const
-{
-    // Compressed images do not use unpack parameters.
+    CheckedNumeric<GLuint> checkedCopyBytes = 0;
     if (compressed)
     {
-        return computeCompressedImageSize(formatType, size);
+        ANGLE_TRY_RESULT(computeCompressedImageSize(formatType, size), checkedCopyBytes);
     }
-
-    if (size.height == 0 || size.depth == 0)
+    else if (size.height != 0 && (!is3D || size.depth != 0))
     {
-        return 0;
+        CheckedNumeric<GLuint> bytes = computePixelBytes(formatType);
+        checkedCopyBytes += size.width * bytes;
+
+        CheckedNumeric<GLuint> heightMinusOne = size.height - 1;
+        checkedCopyBytes += heightMinusOne * rowPitch;
+
+        if (is3D)
+        {
+            CheckedNumeric<GLuint> depthMinusOne = size.depth - 1;
+            checkedCopyBytes += depthMinusOne * depthPitch;
+        }
     }
 
-    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);
-
-    CheckedNumeric<GLuint> depthMinusOne  = size.depth - 1;
-    CheckedNumeric<GLuint> heightMinusOne = size.height - 1;
-    CheckedNumeric<GLuint> bytes          = computePixelBytes(formatType);
-
-    CheckedNumeric<GLuint> totalSize = depthMinusOne * depthPitch;
-    totalSize += heightMinusOne * rowPitch;
-    totalSize += size.width * bytes;
-
-    ANGLE_TRY_CHECKED_MATH(totalSize);
-
-    return totalSize.ValueOrDie();
-}
-
-gl::ErrorOrResult<GLuint> InternalFormat::computePackEndByte(GLenum formatType,
-                                                             const gl::Extents &size,
-                                                             const gl::PixelPackState &pack) const
-{
-    GLuint rowPitch;
-    CheckedNumeric<GLuint> checkedSkipBytes;
-    CheckedNumeric<GLuint> checkedCopyBytes;
-
-    ANGLE_TRY_RESULT(computeRowPitch(formatType, size.width, pack.alignment, pack.rowLength),
-                     rowPitch);
-    ANGLE_TRY_RESULT(computeSkipBytes(rowPitch, 0, 0, pack.skipRows, pack.skipPixels, false),
-                     checkedSkipBytes);
-    ANGLE_TRY_RESULT(computePackSize(formatType, size, pack), checkedCopyBytes);
-
-    CheckedNumeric<GLuint> endByte = checkedCopyBytes + checkedSkipBytes;
-
-    ANGLE_TRY_CHECKED_MATH(endByte);
-    return endByte.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> checkedSkipBytes = 0;
+    ANGLE_TRY_RESULT(computeSkipBytes(rowPitch, depthPitch, state, is3D), checkedSkipBytes);
 
     CheckedNumeric<GLuint> endByte = checkedCopyBytes + checkedSkipBytes;
 
diff --git a/src/libANGLE/formatutils.h b/src/libANGLE/formatutils.h
index 0bd3ae6..6339038 100644
--- a/src/libANGLE/formatutils.h
+++ b/src/libANGLE/formatutils.h
@@ -49,39 +49,30 @@
 
     GLuint computePixelBytes(GLenum formatType) const;
 
-    gl::ErrorOrResult<GLuint> computeRowPitch(GLenum formatType,
-                                              GLsizei width,
-                                              GLint alignment,
-                                              GLint rowLength) const;
-    gl::ErrorOrResult<GLuint> computeDepthPitch(GLenum formatType,
-                                                GLsizei width,
-                                                GLsizei height,
-                                                GLint alignment,
-                                                GLint rowLength,
-                                                GLint imageHeight) const;
-    gl::ErrorOrResult<GLuint> computeCompressedImageSize(GLenum formatType,
-                                                         const gl::Extents &size) const;
-    gl::ErrorOrResult<GLuint> computeSkipBytes(GLuint rowPitch,
-                                               GLuint depthPitch,
-                                               GLint skipImages,
-                                               GLint skipRows,
-                                               GLint skipPixels,
-                                               bool applySkipImages) const;
+    ErrorOrResult<GLuint> computeRowPitch(GLenum formatType,
+                                          GLsizei width,
+                                          GLint alignment,
+                                          GLint rowLength) const;
+    ErrorOrResult<GLuint> computeDepthPitch(GLenum formatType,
+                                            GLsizei width,
+                                            GLsizei height,
+                                            GLint alignment,
+                                            GLint rowLength,
+                                            GLint imageHeight) const;
 
-    gl::ErrorOrResult<GLuint> computePackSize(GLenum formatType,
-                                              const gl::Extents &size,
-                                              const gl::PixelPackState &pack) const;
-    gl::ErrorOrResult<GLuint> computeUnpackSize(GLenum formatType,
-                                                const gl::Extents &size,
-                                                const gl::PixelUnpackState &unpack) const;
+    ErrorOrResult<GLuint> computeCompressedImageSize(GLenum formatType,
+                                                     const Extents &size) const;
 
-    gl::ErrorOrResult<GLuint> computePackEndByte(GLenum formatType,
-                                                 const gl::Extents &size,
-                                                 const gl::PixelPackState &pack) const;
-    gl::ErrorOrResult<GLuint> computeUnpackEndByte(GLenum formatType,
-                                                   const gl::Extents &size,
-                                                   const gl::PixelUnpackState &unpack,
-                                                   bool applySkipImages) const;
+    ErrorOrResult<GLuint> computeSkipBytes(GLuint rowPitch,
+                                           GLuint depthPitch,
+                                           const PixelStoreStateBase &state,
+                                           bool is3D) const;
+
+    ErrorOrResult<GLuint> computePackUnpackEndByte(GLenum formatType,
+                                                       const Extents &size,
+                                                       const PixelStoreStateBase &state,
+                                                       bool is3D) const;
+
     bool isLUMA() const;
     GLenum getReadPixelsFormat() const;
     GLenum getReadPixelsType() const;
@@ -282,7 +273,7 @@
     VERTEX_FORMAT_UINT210_INT,
 };
 
-typedef std::vector<gl::VertexFormatType> InputLayout;
+typedef std::vector<VertexFormatType> InputLayout;
 
 struct VertexFormat : angle::NonCopyable
 {
diff --git a/src/libANGLE/renderer/d3d/FramebufferD3D.cpp b/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
index a272264..f6eef6f 100644
--- a/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
+++ b/src/libANGLE/renderer/d3d/FramebufferD3D.cpp
@@ -242,13 +242,13 @@
 
     GLenum sizedInternalFormat = gl::GetSizedInternalFormat(format, type);
     const gl::InternalFormat &sizedFormatInfo = gl::GetInternalFormatInfo(sizedInternalFormat);
-    GLuint outputPitch                        = 0;
+
+    GLuint outputPitch = 0;
     ANGLE_TRY_RESULT(
         sizedFormatInfo.computeRowPitch(type, area.width, packState.alignment, packState.rowLength),
         outputPitch);
     GLuint outputSkipBytes = 0;
-    ANGLE_TRY_RESULT(sizedFormatInfo.computeSkipBytes(outputPitch, 0, 0, packState.skipRows,
-                                                      packState.skipPixels, false),
+    ANGLE_TRY_RESULT(sizedFormatInfo.computeSkipBytes(outputPitch, 0, packState, false),
                      outputSkipBytes);
 
     return readPixelsImpl(area, format, type, outputPitch, packState,
diff --git a/src/libANGLE/renderer/d3d/d3d11/Image11.cpp b/src/libANGLE/renderer/d3d/d3d11/Image11.cpp
index d27a8d5..bde01b8 100644
--- a/src/libANGLE/renderer/d3d/d3d11/Image11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/Image11.cpp
@@ -262,8 +262,7 @@
                      inputDepthPitch);
     GLuint inputSkipBytes = 0;
     ANGLE_TRY_RESULT(
-        formatInfo.computeSkipBytes(inputRowPitch, inputDepthPitch, unpack.skipImages,
-                                    unpack.skipRows, unpack.skipPixels, applySkipImages),
+        formatInfo.computeSkipBytes(inputRowPitch, inputDepthPitch, unpack, applySkipImages),
         inputSkipBytes);
 
     const d3d11::DXGIFormatSize &dxgiFormatInfo = d3d11::GetDXGIFormatSizeInfo(mDXGIFormat);
diff --git a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
index 8fe8fc3..6764dfe 100644
--- a/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
+++ b/src/libANGLE/renderer/d3d/d3d11/TextureStorage11.cpp
@@ -630,8 +630,7 @@
                      srcDepthPitch);
     GLuint srcSkipBytes = 0;
     ANGLE_TRY_RESULT(
-        internalFormatInfo.computeSkipBytes(srcRowPitch, srcDepthPitch, unpack.skipImages,
-                                            unpack.skipRows, unpack.skipPixels, index.is3D()),
+        internalFormatInfo.computeSkipBytes(srcRowPitch, srcDepthPitch, unpack, index.is3D()),
         srcSkipBytes);
 
     const d3d11::Format &d3d11Format =
diff --git a/src/libANGLE/renderer/gl/FramebufferGL.cpp b/src/libANGLE/renderer/gl/FramebufferGL.cpp
index 24b5c31..4f8a4f0 100644
--- a/src/libANGLE/renderer/gl/FramebufferGL.cpp
+++ b/src/libANGLE/renderer/gl/FramebufferGL.cpp
@@ -22,6 +22,7 @@
 #include "libANGLE/renderer/gl/TextureGL.h"
 #include "libANGLE/renderer/gl/WorkaroundsGL.h"
 #include "libANGLE/renderer/gl/formatutilsgl.h"
+#include "libANGLE/renderer/gl/renderergl_utils.h"
 #include "platform/Platform.h"
 
 using namespace gl;
@@ -30,50 +31,6 @@
 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,
@@ -294,10 +251,12 @@
 
     if (mWorkarounds.packLastRowSeparatelyForPaddingInclusion)
     {
+        gl::Extents size(area.width, area.height, 1);
+
         bool apply;
-        ANGLE_TRY_RESULT(
-            ShouldApplyLastRowPaddingWorkaround(area, packState, readFormat, readType, pixels),
-            apply);
+        ANGLE_TRY_RESULT(ShouldApplyLastRowPaddingWorkaround(size, packState, readFormat, readType,
+                                                             false, pixels),
+                         apply);
 
         if (apply)
         {
@@ -476,9 +435,7 @@
     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);
+    ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, 0, pack, false), skipBytes);
 
     gl::PixelPackState directPack;
     directPack.pixelBuffer = pack.pixelBuffer;
@@ -509,9 +466,7 @@
     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);
+    ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, 0, pack, false), skipBytes);
 
     // Get all by the last row
     if (area.height > 1)
diff --git a/src/libANGLE/renderer/gl/TextureGL.cpp b/src/libANGLE/renderer/gl/TextureGL.cpp
index 02f17a2..cfc19cb 100644
--- a/src/libANGLE/renderer/gl/TextureGL.cpp
+++ b/src/libANGLE/renderer/gl/TextureGL.cpp
@@ -20,6 +20,7 @@
 #include "libANGLE/renderer/gl/StateManagerGL.h"
 #include "libANGLE/renderer/gl/WorkaroundsGL.h"
 #include "libANGLE/renderer/gl/formatutilsgl.h"
+#include "libANGLE/renderer/gl/renderergl_utils.h"
 
 using angle::CheckedNumeric;
 
@@ -89,50 +90,6 @@
                        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 this 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)
@@ -185,8 +142,14 @@
     mTextureID = 0;
 }
 
-gl::Error TextureGL::setImage(GLenum target, size_t level, GLenum internalFormat, const gl::Extents &size, GLenum format, GLenum type,
-                              const gl::PixelUnpackState &unpack, const uint8_t *pixels)
+gl::Error TextureGL::setImage(GLenum target,
+                              size_t level,
+                              GLenum internalFormat,
+                              const gl::Extents &size,
+                              GLenum format,
+                              GLenum type,
+                              const gl::PixelUnpackState &unpack,
+                              const uint8_t *pixels)
 {
     if (mWorkarounds.unpackOverlappingRowsSeparatelyUnpackBuffer && unpack.pixelBuffer.get() &&
         unpack.rowLength != 0 && unpack.rowLength < size.width)
@@ -207,8 +170,7 @@
     if (mWorkarounds.unpackLastRowSeparatelyForPaddingInclusion)
     {
         bool apply;
-        gl::Box area(0, 0, 0, size.width, size.height, size.depth);
-        ANGLE_TRY_RESULT(ShouldApplyLastRowPaddingWorkaround(area, unpack, format, type,
+        ANGLE_TRY_RESULT(ShouldApplyLastRowPaddingWorkaround(size, unpack, format, type,
                                                              UseTexImage3D(mState.mTarget), pixels),
                          apply);
 
@@ -223,6 +185,7 @@
                 return gl::NoError();
             }
 
+            gl::Box area(0, 0, 0, size.width, size.height, size.depth);
             return setSubImagePaddingWorkaround(target, level, area, format, type, unpack, pixels);
         }
     }
@@ -303,8 +266,10 @@
 
     if (mWorkarounds.unpackLastRowSeparatelyForPaddingInclusion)
     {
+        gl::Extents size(area.width, area.height, area.depth);
+
         bool apply;
-        ANGLE_TRY_RESULT(ShouldApplyLastRowPaddingWorkaround(area, unpack, format, type,
+        ANGLE_TRY_RESULT(ShouldApplyLastRowPaddingWorkaround(size, unpack, format, type,
                                                              UseTexImage3D(mState.mTarget), pixels),
                          apply);
 
@@ -359,8 +324,7 @@
                      imageBytes);
     bool useTexImage3D = UseTexImage3D(mState.mTarget);
     GLuint skipBytes   = 0;
-    ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, imageBytes, unpack.skipImages,
-                                               unpack.skipRows, unpack.skipPixels, useTexImage3D),
+    ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, imageBytes, unpack, useTexImage3D),
                      skipBytes);
 
     const uint8_t *pixelsWithSkip = pixels + skipBytes;
@@ -412,8 +376,7 @@
                      imageBytes);
     bool useTexImage3D = UseTexImage3D(mState.mTarget);
     GLuint skipBytes   = 0;
-    ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, imageBytes, unpack.skipImages,
-                                               unpack.skipRows, unpack.skipPixels, useTexImage3D),
+    ANGLE_TRY_RESULT(glFormat.computeSkipBytes(rowBytes, imageBytes, unpack, useTexImage3D),
                      skipBytes);
 
     gl::PixelUnpackState directUnpack;
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.cpp b/src/libANGLE/renderer/gl/renderergl_utils.cpp
index 4fcc135..b9e6e53 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.cpp
+++ b/src/libANGLE/renderer/gl/renderergl_utils.cpp
@@ -11,6 +11,8 @@
 
 #include <limits>
 
+#include "common/mathutil.h"
+#include "libANGLE/Buffer.h"
 #include "libANGLE/Caps.h"
 #include "libANGLE/formatutils.h"
 #include "libANGLE/renderer/gl/FunctionsGL.h"
@@ -20,6 +22,8 @@
 #include <algorithm>
 #include <sstream>
 
+using angle::CheckedNumeric;
+
 namespace rx
 {
 VendorID GetVendorID(const FunctionsGL *functions)
@@ -968,4 +972,45 @@
         return nullptr;
     }
 }
+
+gl::ErrorOrResult<bool> ShouldApplyLastRowPaddingWorkaround(const gl::Extents &size,
+                                                            const gl::PixelStoreStateBase &state,
+                                                            GLenum format,
+                                                            GLenum type,
+                                                            bool is3D,
+                                                            const void *pixels)
+{
+    if (state.pixelBuffer.get() == nullptr)
+    {
+        return false;
+    }
+
+    // We are using an pack or unpack buffer, compute what the driver thinks is going to be the
+    // last byte read or written. 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 operation.
+    CheckedNumeric<size_t> checkedEndByte;
+    CheckedNumeric<size_t> pixelBytes;
+    size_t rowPitch;
+
+    const gl::InternalFormat &glFormat =
+        gl::GetInternalFormatInfo(gl::GetSizedInternalFormat(format, type));
+    ANGLE_TRY_RESULT(glFormat.computePackUnpackEndByte(type, size, state, is3D), checkedEndByte);
+    ANGLE_TRY_RESULT(glFormat.computeRowPitch(type, size.width, state.alignment, state.rowLength),
+                     rowPitch);
+    pixelBytes = glFormat.computePixelBytes(type);
+
+    checkedEndByte += reinterpret_cast<intptr_t>(pixels);
+
+    // At this 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>(state.pixelBuffer->getSize());
+}
 }
diff --git a/src/libANGLE/renderer/gl/renderergl_utils.h b/src/libANGLE/renderer/gl/renderergl_utils.h
index 3b0cab2..bd5a425 100644
--- a/src/libANGLE/renderer/gl/renderergl_utils.h
+++ b/src/libANGLE/renderer/gl/renderergl_utils.h
@@ -11,6 +11,7 @@
 #define LIBANGLE_RENDERER_GL_RENDERERGLUTILS_H_
 
 #include "libANGLE/angletypes.h"
+#include "libANGLE/Error.h"
 #include "libANGLE/renderer/gl/functionsgl_typedefs.h"
 
 #include <string>
@@ -47,6 +48,13 @@
                                     size_t offset,
                                     size_t length,
                                     GLbitfield access);
+
+gl::ErrorOrResult<bool> ShouldApplyLastRowPaddingWorkaround(const gl::Extents &size,
+                                                            const gl::PixelStoreStateBase &state,
+                                                            GLenum format,
+                                                            GLenum type,
+                                                            bool is3D,
+                                                            const void *pixels);
 }
 
 #endif // LIBANGLE_RENDERER_GL_RENDERERGLUTILS_H_
diff --git a/src/libANGLE/validationES.cpp b/src/libANGLE/validationES.cpp
index 2d3474a..7daa549 100644
--- a/src/libANGLE/validationES.cpp
+++ b/src/libANGLE/validationES.cpp
@@ -1179,7 +1179,7 @@
         const gl::Extents size(width, height, 1);
         const auto &pack = context->getGLState().getPackState();
 
-        auto endByteOrErr = formatInfo.computePackEndByte(type, size, pack);
+        auto endByteOrErr = formatInfo.computePackUnpackEndByte(type, size, pack, false);
         if (endByteOrErr.isError())
         {
             context->handleError(endByteOrErr.getError());
@@ -1230,7 +1230,7 @@
     const gl::Extents size(width, height, 1);
     const auto &pack = context->getGLState().getPackState();
 
-    auto endByteOrErr = formatInfo.computePackEndByte(type, size, pack);
+    auto endByteOrErr = formatInfo.computePackUnpackEndByte(type, size, pack, false);
     if (endByteOrErr.isError())
     {
         context->handleError(endByteOrErr.getError());
diff --git a/src/libANGLE/validationES3.cpp b/src/libANGLE/validationES3.cpp
index 782646f..b594965 100644
--- a/src/libANGLE/validationES3.cpp
+++ b/src/libANGLE/validationES3.cpp
@@ -487,7 +487,7 @@
         const auto &unpack = context->getGLState().getUnpackState();
 
         bool targetIs3D     = target == GL_TEXTURE_3D || target == GL_TEXTURE_2D_ARRAY;
-        auto endByteOrErr   = formatInfo.computeUnpackEndByte(type, size, unpack, targetIs3D);
+        auto endByteOrErr   = formatInfo.computePackUnpackEndByte(type, size, unpack, targetIs3D);
         if (endByteOrErr.isError())
         {
             context->handleError(endByteOrErr.getError());