# SDL: GPU: Allow multisample textures to be read (#15838)

**URL:** <https://discourse.libsdl.org/t/sdl-gpu-allow-multisample-textures-to-be-read-15838/69314>\
**Category:** SDL Commits\
**Created:** [September 14, 2026, 7:14pm UTC](https://discourse.libsdl.org/t/sdl-gpu-allow-multisample-textures-to-be-read-15838/69314 "2026-09-14T19:14:08Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![SDLPushPostBot](https://discourse.libsdl.org/user_avatar/discourse.libsdl.org/sdlpushpostbot/32/4370_2.png) [@SDLPushPostBot](https://discourse.libsdl.org/u/SDLPushPostBot)\
**Post date:** [September 14, 2026, 7:14pm UTC](https://discourse.libsdl.org/t/sdl-gpu-allow-multisample-textures-to-be-read-15838/69314/1 "2026-09-14T19:14:08Z")

</div>

> <https://github.com/libsdl-org/SDL/commit/1a2ba3b94d247d790240d786213d45a71f49e98e>

```diff
From 1a2ba3b94d247d790240d786213d45a71f49e98e Mon Sep 17 00:00:00 2001
From: Evan Hemsley <[EMAIL REDACTED]>
Date: Mon, 14 Sep 2026 12:12:41 -0700
Subject: [PATCH] GPU: Allow multisample textures to be read (#15838)

---
 include/SDL3/SDL_gpu.h | 18 ++++++++++++++++++
 src/gpu/SDL_gpu.c | 31 ++++++++++++++++++++++++++-----
 src/gpu/d3d12/SDL_gpu_d3d12.c | 3 +++
 3 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/include/SDL3/SDL_gpu.h b/include/SDL3/SDL_gpu.h
index bdc09c18a082d..24f1e4b821eae 100644
--- a/include/SDL3/SDL_gpu.h
+++ b/include/SDL3/SDL_gpu.h
@@ -3736,6 +3736,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_BindGPUIndexBuffer(
  *
  * The textures must have been created with SDL_GPU_TEXTUREUSAGE_SAMPLER.
  *
+ * The textures being bound must have a matching type declared in the shader
+ * (2D, 3D, etc.). Multisample textures are not allowed.
+ *
  * Be sure your shader is set up according to the requirements documented in
  * SDL_CreateGPUShader().
  *
@@ -3762,6 +3765,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_BindGPUVertexSamplers(
  * These textures must have been created with
  * SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ.
  *
+ * The textures being bound must have a matching type declared in the shader
+ * (2D, 3D, 2DMS, etc.)
+ *
  * Be sure your shader is set up according to the requirements documented in
  * SDL_CreateGPUShader().
  *
@@ -3809,6 +3815,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_BindGPUVertexStorageBuffers(
  *
  * The textures must have been created with SDL_GPU_TEXTUREUSAGE_SAMPLER.
  *
+ * The textures being bound must have a matching type declared in the shader
+ * (2D, 3D, etc.). Multisample textures are not allowed.
+ *
  * Be sure your shader is set up according to the requirements documented in
  * SDL_CreateGPUShader().
  *
@@ -3835,6 +3844,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_BindGPUFragmentSamplers(
  * These textures must have been created with
  * SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ.
  *
+ * The textures being bound must have a matching type declared in the shader
+ * (2D, 3D, 2DMS, etc.)
+ *
  * Be sure your shader is set up according to the requirements documented in
  * SDL_CreateGPUShader().
  *
@@ -4057,6 +4069,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_BindGPUComputePipeline(
  *
  * The textures must have been created with SDL_GPU_TEXTUREUSAGE_SAMPLER.
  *
+ * The textures being bound must have a matching type declared in the shader
+ * (2D, 3D, etc.). Multisample textures are not allowed.
+ *
  * Be sure your shader is set up according to the requirements documented in
  * SDL_CreateGPUComputePipeline().
  *
@@ -4083,6 +4098,9 @@ extern SDL_DECLSPEC void SDLCALL SDL_BindGPUComputeSamplers(
  * These textures must have been created with
  * SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ.
  *
+ * The textures being bound must have a matching type declared in the shader
+ * (2D, 3D, 2DMS, etc.)
+ *
  * Be sure your shader is set up according to the requirements documented in
  * SDL_CreateGPUComputePipeline().
  *
diff --git a/src/gpu/SDL_gpu.c b/src/gpu/SDL_gpu.c
index a63bbaf4a9846..527c5c7c738a4 100644
--- a/src/gpu/SDL_gpu.c
+++ b/src/gpu/SDL_gpu.c
@@ -1287,11 +1287,8 @@ SDL_GPUTexture *SDL_CreateGPUTexture(
             failed = true;
         }
         if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1 &&
- (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_SAMPLER |
- SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ |
- SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ |
- SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE))) {
- SDL_assert_release(!"For multisample textures: usage cannot contain SAMPLER or STORAGE flags");
+ (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE)) {
+ SDL_assert_release(!"For multisample textures: usage cannot contain COMPUTE_STORAGE_WRITE flag");
             failed = true;
         }
         if (IsDepthFormat(createinfo->format) && (createinfo->usage & ~(SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER))) {
@@ -2102,6 +2099,14 @@ void SDL_BindGPUVertexSamplers(
         if (!((CommandBufferCommonHeader *)RENDERPASS_COMMAND_BUFFER)->ignore_render_pass_texture_validation)
         {
             CHECK_SAMPLER_TEXTURES
+
+ for (Uint32 i = 0; i < num_bindings; i += 1) {
+ TextureCommonHeader *texture_header = (TextureCommonHeader *)texture_sampler_bindings[i].texture;
+ if (texture_header->info.sample_count > SDL_GPU_SAMPLECOUNT_1)
+ {
+ SDL_assert_release(!"Multisample textures cannot be bound as samplers!");
+ }
+ }
         }
 
         for (Uint32 i = 0; i < num_bindings; i += 1) {
@@ -2215,6 +2220,14 @@ void SDL_BindGPUFragmentSamplers(
             CHECK_SAMPLER_TEXTURES
         }
 
+ for (Uint32 i = 0; i < num_bindings; i += 1) {
+ TextureCommonHeader *texture_header = (TextureCommonHeader *)texture_sampler_bindings[i].texture;
+ if (texture_header->info.sample_count > SDL_GPU_SAMPLECOUNT_1)
+ {
+ SDL_assert_release(!"Multisample textures cannot be bound as samplers!");
+ }
+ }
+
         for (Uint32 i = 0; i < num_bindings; i += 1) {
             ((RenderPass *)render_pass)->fragment_sampler_bound[first_slot + i] = true;
         }
@@ -2576,6 +2589,14 @@ void SDL_BindGPUComputeSamplers(
     if (COMPUTEPASS_DEVICE->debug_mode) {
         CHECK_COMPUTEPASS
 
+ for (Uint32 i = 0; i < num_bindings; i += 1) {
+ TextureCommonHeader *texture_header = (TextureCommonHeader *)texture_sampler_bindings[i].texture;
+ if (texture_header->info.sample_count > SDL_GPU_SAMPLECOUNT_1)
+ {
+ SDL_assert_release(!"Multisample textures cannot be bound as samplers!");
+ }
+ }
+
         for (Uint32 i = 0; i < num_bindings; i += 1) {
             ((ComputePass *)compute_pass)->sampler_bound[first_slot + i] = true;
         }
diff --git a/src/gpu/d3d12/SDL_gpu_d3d12.c b/src/gpu/d3d12/SDL_gpu_d3d12.c
index 2881e9ab594e9..19ebf099928a2 100644
--- a/src/gpu/d3d12/SDL_gpu_d3d12.c
+++ b/src/gpu/d3d12/SDL_gpu_d3d12.c
@@ -3535,6 +3535,9 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture(
             srvDesc.Texture3D.MipLevels = createinfo->num_levels;
             srvDesc.Texture3D.MostDetailedMip = 0;
             srvDesc.Texture3D.ResourceMinLODClamp = 0; // default behavior
+ } else if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1) {
+ srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2DMS;
+ srvDesc.Texture2DMS.UnusedField_NothingToDefine = 0;
         } else {
             srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
             srvDesc.Texture2D.MipLevels = createinfo->num_levels;

```
