SDL: gpu: Add debug validation for viewport/scissor being larger than target size

From 0ccb76ae876e3e893d179828f16f0933326bf084 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Thu, 17 Sep 2026 13:29:54 -0400
Subject: [PATCH] gpu: Add debug validation for viewport/scissor being larger
 than target size

---
 src/gpu/SDL_gpu.c    | 22 ++++++++++++++++++++++
 src/gpu/SDL_sysgpu.h |  2 ++
 2 files changed, 24 insertions(+)

diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index 527c5c7c738a4..12939c2d9e0dd 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -749,6 +749,10 @@ SDL_GPUDevice *SDL_CreateGPUDeviceWithProperties(SDL_PropertiesID props)
             if (!SDL_GetBooleanProperty(props, SDL_PROP_GPU_DEVICE_CREATE_FEATURE_ANISOTROPY_BOOLEAN, true)) {
                 result->validate_feature_anisotropy_disabled = true;
             }
+            if (result->debug_mode) {
+                result->max_viewport_width = SDL_MAX_UINT32;
+                result->max_viewport_height = SDL_MAX_UINT32;
+            }
         }
     }
     return result;
@@ -1822,9 +1826,15 @@ SDL_GPURenderPass *SDL_BeginGPURenderPass(
         CHECK_COMMAND_BUFFER_RETURN_NULL
         CHECK_ANY_PASS_IN_PROGRESS("Cannot begin render pass during another pass!", NULL);
 
+        COMMAND_BUFFER_DEVICE->max_viewport_width = SDL_MAX_UINT32;
+        COMMAND_BUFFER_DEVICE->max_viewport_height = SDL_MAX_UINT32;
+
         for (Uint32 i = 0; i < num_color_targets; i += 1) {
             TextureCommonHeader *textureHeader = (TextureCommonHeader *)color_target_infos[i].texture;
 
+            COMMAND_BUFFER_DEVICE->max_viewport_width = SDL_min(COMMAND_BUFFER_DEVICE->max_viewport_width, textureHeader->info.width);
+            COMMAND_BUFFER_DEVICE->max_viewport_height = SDL_min(COMMAND_BUFFER_DEVICE->max_viewport_height, textureHeader->info.height);
+
             if (color_target_infos[i].cycle && color_target_infos[i].load_op == SDL_GPU_LOADOP_LOAD) {
                 SDL_assert_release(!"Cannot cycle color target when load op is LOAD!");
                 return NULL;
@@ -1959,6 +1969,12 @@ void SDL_SetGPUViewport(
 
     if (RENDERPASS_DEVICE->debug_mode) {
         CHECK_RENDERPASS
+
+        if (((viewport->x + viewport->w) > RENDERPASS_DEVICE->max_viewport_width) ||
+            ((viewport->y + viewport->h) > RENDERPASS_DEVICE->max_viewport_height)) {
+            SDL_assert_release(!"Viewport size exceeds current render target dimensions");
+            return;
+        }
     }
 
     RENDERPASS_DEVICE->SetViewport(
@@ -1981,6 +1997,12 @@ void SDL_SetGPUScissor(
 
     if (RENDERPASS_DEVICE->debug_mode) {
         CHECK_RENDERPASS
+
+        if (((Uint32) (scissor->x + scissor->w) > RENDERPASS_DEVICE->max_viewport_width) ||
+            ((Uint32) (scissor->y + scissor->h) > RENDERPASS_DEVICE->max_viewport_height)) {
+            SDL_assert_release(!"Scissor rectangle size exceeds current render target dimensions");
+            return;
+        }
     }
 
     RENDERPASS_DEVICE->SetScissor(
diff --git a/src/gpu/SDL_sysgpu.h b/src/gpu/SDL_sysgpu.h
index 604be78b5b9b2..c04ce508edaec 100644
--- a/src/gpu/SDL_sysgpu.h
+++ b/src/gpu/SDL_sysgpu.h
@@ -1124,6 +1124,8 @@ struct SDL_GPUDevice
     bool default_enable_depth_clip;
     bool validate_feature_depth_clamp_disabled;
     bool validate_feature_anisotropy_disabled;
+    Uint32 max_viewport_width;
+    Uint32 max_viewport_height;
 };
 
 #define ASSIGN_DRIVER_FUNC(func, name) \