SDL: GPU: Add SDL_CalculateGPUTextureFormatSize (#11146)

From 6ea4a66451c8e79b9aa3a80097d35af9b3ed409c Mon Sep 17 00:00:00 2001
From: Evan Hemsley <[EMAIL REDACTED]>
Date: Thu, 10 Oct 2024 16:34:38 -0700
Subject: [PATCH] GPU: Add SDL_CalculateGPUTextureFormatSize (#11146)

---------

Co-authored-by: Sam Lantinga <slouken@libsdl.org>
---
 include/SDL3/SDL_gpu.h            | 17 +++++++++++++++++
 src/dynapi/SDL_dynapi.sym         |  1 +
 src/dynapi/SDL_dynapi_overrides.h |  1 +
 src/dynapi/SDL_dynapi_procs.h     |  1 +
 src/gpu/SDL_gpu.c                 | 13 +++++++++++++
 src/gpu/SDL_sysgpu.h              | 12 ------------
 src/gpu/metal/SDL_gpu_metal.m     |  2 +-
 7 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index 66455d67b23b6..89d5000a299f4 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -3714,6 +3714,23 @@ extern SDL_DECLSPEC bool SDLCALL SDL_GPUTextureSupportsSampleCount(
     SDL_GPUTextureFormat format,
     SDL_GPUSampleCount sample_count);
 
+/**
+ * Calculate the size in bytes of a texture format with dimensions.
+ *
+ * \param format a texture format.
+ * \param width width in pixels.
+ * \param height height in pixels.
+ * \param depth_or_layer_count depth for 3D textures or layer count otherwise.
+ * \returns the size of a texture with this format and dimensions.
+ *
+ * \since This function is available since SDL 3.1.5.
+ */
+extern SDL_DECLSPEC Uint32 SDLCALL SDL_CalculateGPUTextureFormatSize(
+    SDL_GPUTextureFormat format,
+    Uint32 width,
+    Uint32 height,
+    Uint32 depth_or_layer_count);
+
 #ifdef SDL_PLATFORM_GDK
 
 /**
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index c60e930c03666..06cc432a93f95 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1178,6 +1178,7 @@ SDL3_0.0.0 {
     SDL_wcstol;
     SDL_StepBackUTF8;
     SDL_DelayPrecise;
+    SDL_CalculateGPUTextureFormatSize;
     # extra symbols go here (don't modify this line)
   local: *;
 };
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index c03a633f12739..6de3884830abf 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1203,3 +1203,4 @@
 #define SDL_wcstol SDL_wcstol_REAL
 #define SDL_StepBackUTF8 SDL_StepBackUTF8_REAL
 #define SDL_DelayPrecise SDL_DelayPrecise_REAL
+#define SDL_CalculateGPUTextureFormatSize SDL_CalculateGPUTextureFormatSize_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 59c32eaaf55b5..e6dcb8b8028ba 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1209,3 +1209,4 @@ SDL_DYNAPI_PROC(wchar_t*,SDL_wcsstr,(const wchar_t *a, const wchar_t *b),(a,b),r
 SDL_DYNAPI_PROC(long,SDL_wcstol,(const wchar_t *a, wchar_t **b, int c),(a,b,c),return)
 SDL_DYNAPI_PROC(Uint32,SDL_StepBackUTF8,(const char *a, const char **b),(a,b),return)
 SDL_DYNAPI_PROC(void,SDL_DelayPrecise,(Uint64 a),(a),)
+SDL_DYNAPI_PROC(Uint32,SDL_CalculateGPUTextureFormatSize,(SDL_GPUTextureFormat a, Uint32 b, Uint32 c, Uint32 d),(a,b,c,d),return)
diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index 3607a7954063f..c550c7a5703d9 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -2796,3 +2796,16 @@ void SDL_ReleaseGPUFence(
         device->driverData,
         fence);
 }
+
+Uint32 SDL_CalculateGPUTextureFormatSize(
+    SDL_GPUTextureFormat format,
+    Uint32 width,
+    Uint32 height,
+    Uint32 depth_or_layer_count)
+{
+    Uint32 blockWidth = Texture_GetBlockWidth(format);
+    Uint32 blockHeight = Texture_GetBlockHeight(format);
+    Uint32 blocksPerRow = (width + blockWidth - 1) / blockWidth;
+    Uint32 blocksPerColumn = (height + blockHeight - 1) / blockHeight;
+    return depth_or_layer_count * blocksPerRow * blocksPerColumn * SDL_GPUTextureFormatTexelBlockSize(format);
+}
diff --git a/src/gpu/SDL_sysgpu.h b/src/gpu/SDL_sysgpu.h
index f06ae62d677d0..51fd06fcc70ea 100644
--- a/src/gpu/SDL_sysgpu.h
+++ b/src/gpu/SDL_sysgpu.h
@@ -362,18 +362,6 @@ static inline Uint32 BytesPerRow(
     return blocksPerRow * SDL_GPUTextureFormatTexelBlockSize(format);
 }
 
-static inline Sint32 BytesPerImage(
-    Uint32 width,
-    Uint32 height,
-    SDL_GPUTextureFormat format)
-{
-    Uint32 blockWidth = Texture_GetBlockWidth(format);
-    Uint32 blockHeight = Texture_GetBlockHeight(format);
-    Uint32 blocksPerRow = (width + blockWidth - 1) / blockWidth;
-    Uint32 blocksPerColumn = (height + blockHeight - 1) / blockHeight;
-    return blocksPerRow * blocksPerColumn * SDL_GPUTextureFormatTexelBlockSize(format);
-}
-
 // GraphicsDevice Limits
 
 #define MAX_TEXTURE_SAMPLERS_PER_STAGE 16
diff --git a/src/gpu/metal/SDL_gpu_metal.m b/src/gpu/metal/SDL_gpu_metal.m
index faed52bb6156b..d60d69d4ba5b1 100644
--- a/src/gpu/metal/SDL_gpu_metal.m
+++ b/src/gpu/metal/SDL_gpu_metal.m
@@ -1752,7 +1752,7 @@ static void METAL_UploadToTexture(
                  copyFromBuffer:bufferContainer->activeBuffer->handle
                    sourceOffset:source->offset
               sourceBytesPerRow:BytesPerRow(destination->w, textureContainer->header.info.format)
-            sourceBytesPerImage:BytesPerImage(destination->w, destination->h, textureContainer->header.info.format)
+            sourceBytesPerImage:SDL_CalculateGPUTextureFormatSize(textureContainer->header.info.format, destination->w, destination->h, destination->d)
                      sourceSize:MTLSizeMake(destination->w, destination->h, destination->d)
                       toTexture:metalTexture->handle
                destinationSlice:destination->layer