Browse Source

GPU: Rework MSAA (#10859)

Caleb Cornett 7 months ago
parent
commit
9416917353

+ 27 - 12
include/SDL3/SDL_gpu.h

@@ -282,8 +282,10 @@ typedef enum SDL_GPULoadOp
  */
 typedef enum SDL_GPUStoreOp
 {
-    SDL_GPU_STOREOP_STORE,     /**< The contents generated during the render pass will be written to memory. */
-    SDL_GPU_STOREOP_DONT_CARE  /**< The contents generated during the render pass are not needed and may be discarded. The contents will be undefined. */
+    SDL_GPU_STOREOP_STORE,             /**< The contents generated during the render pass will be written to memory. */
+    SDL_GPU_STOREOP_DONT_CARE,         /**< The contents generated during the render pass are not needed and may be discarded. The contents will be undefined. */
+    SDL_GPU_STOREOP_RESOLVE,           /**< The multisample contents generated during the render pass will be resolved to a non-multisample texture. The contents in the multisample texture may then be discarded and will be undefined. */
+    SDL_GPU_STOREOP_RESOLVE_AND_STORE  /**< The multisample contents generated during the render pass will be resolved to a non-multisample texture. The contents in the multisample texture will be written to memory. */
 } SDL_GPUStoreOp;
 
 /**
@@ -1499,7 +1501,8 @@ typedef struct SDL_GPUComputePipelineCreateInfo
  * The load_op field determines what is done with the texture at the beginning
  * of the render pass.
  *
- * - LOAD: Loads the data currently in the texture.
+ * - LOAD: Loads the data currently in the texture. Not recommended for
+ *   multisample textures as it requires significant memory bandwidth.
  * - CLEAR: Clears the texture to a single color.
  * - DONT_CARE: The driver will do whatever it wants with the texture memory.
  *   This is a good option if you know that every single pixel will be touched
@@ -1508,9 +1511,16 @@ typedef struct SDL_GPUComputePipelineCreateInfo
  * The store_op field determines what is done with the color results of the
  * render pass.
  *
- * - STORE: Stores the results of the render pass in the texture.
+ * - STORE: Stores the results of the render pass in the texture. Not recommended
+ *   for multisample textures as it requires significant memory bandwidth.
  * - DONT_CARE: The driver will do whatever it wants with the texture memory.
  *   This is often a good option for depth/stencil textures.
+ * - RESOLVE: Resolves a multisample texture into resolve_texture, which must have
+ *   a sample count of 1. Then the driver may discard the multisample texture memory.
+ *   This is the most performant method of resolving a multisample target.
+ * - RESOLVE_AND_STORE: Resolves a multisample texture into the resolve_texture,
+ *   which must have a sample count of 1. Then the driver stores the multisample
+ *   texture's contents. Not recommended as it requires significant memory bandwidth.
  *
  * \since This struct is available since SDL 3.0.0
  *
@@ -1518,16 +1528,19 @@ typedef struct SDL_GPUComputePipelineCreateInfo
  */
 typedef struct SDL_GPUColorTargetInfo
 {
-    SDL_GPUTexture *texture;      /**< The texture that will be used as a color target by a render pass. */
-    Uint32 mip_level;             /**< The mip level to use as a color target. */
-    Uint32 layer_or_depth_plane;  /**< The layer index or depth plane to use as a color target. This value is treated as a layer index on 2D array and cube textures, and as a depth plane on 3D textures. */
-    SDL_FColor clear_color;       /**< The color to clear the color target to at the start of the render pass. Ignored if SDL_GPU_LOADOP_CLEAR is not used. */
-    SDL_GPULoadOp load_op;        /**< What is done with the contents of the color target at the beginning of the render pass. */
-    SDL_GPUStoreOp store_op;      /**< What is done with the results of the render pass. */
-    SDL_bool cycle;               /**< SDL_TRUE cycles the texture if the texture is bound and load_op is not LOAD */
+    SDL_GPUTexture *texture;         /**< The texture that will be used as a color target by a render pass. */
+    Uint32 mip_level;                /**< The mip level to use as a color target. */
+    Uint32 layer_or_depth_plane;     /**< The layer index or depth plane to use as a color target. This value is treated as a layer index on 2D array and cube textures, and as a depth plane on 3D textures. */
+    SDL_FColor clear_color;          /**< The color to clear the color target to at the start of the render pass. Ignored if SDL_GPU_LOADOP_CLEAR is not used. */
+    SDL_GPULoadOp load_op;           /**< What is done with the contents of the color target at the beginning of the render pass. */
+    SDL_GPUStoreOp store_op;         /**< What is done with the results of the render pass. */
+    SDL_GPUTexture *resolve_texture; /**< The texture that will receive the results of a multisample resolve operation. Ignored if a RESOLVE* store_op is not used. */
+    Uint32 resolve_mip_level;        /**< The mip level of the resolve texture to use for the resolve operation. Ignored if a RESOLVE* store_op is not used. */
+    Uint32 resolve_layer;            /**< The layer index of the resolve texture to use for the resolve operation. Ignored if a RESOLVE* store_op is not used. */
+    SDL_bool cycle;                  /**< SDL_TRUE cycles the texture if the texture is bound and load_op is not LOAD */
+    SDL_bool cycle_resolve_texture;  /**< SDL_TRUE cycles the resolve texture if the resolve texture is bound. Ignored if a RESOLVE* store_op is not used. */
     Uint8 padding1;
     Uint8 padding2;
-    Uint8 padding3;
 } SDL_GPUColorTargetInfo;
 
 /**
@@ -1568,6 +1581,8 @@ typedef struct SDL_GPUColorTargetInfo
  *   This is often a good option for depth/stencil textures that don't need to
  *   be reused again.
  *
+ * Note that depth/stencil targets do not support multisample resolves.
+ *
  * \since This struct is available since SDL 3.0.0
  *
  * \sa SDL_BeginGPURenderPass

+ 54 - 12
src/gpu/SDL_gpu.c

@@ -867,6 +867,13 @@ SDL_GPUTexture *SDL_CreateGPUTexture(
             SDL_assert_release(!"For any texture: usage cannot contain both GRAPHICS_STORAGE_READ and SAMPLER");
             failed = true;
         }
+        if (createinfo->sample_count > 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");
+            failed = true;
+        }
         if (IsDepthFormat(createinfo->format) && (createinfo->usage & ~(SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER))) {
             SDL_assert_release(!"For depth textures: usage cannot contain any flags except for DEPTH_STENCIL_TARGET and SAMPLER");
             failed = true;
@@ -945,16 +952,10 @@ SDL_GPUTexture *SDL_CreateGPUTexture(
                     SDL_assert_release(!"For array textures: usage must not contain DEPTH_STENCIL_TARGET");
                     failed = true;
                 }
-                if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1) {
-                    SDL_assert_release(!"For array textures: sample_count must be SDL_GPU_SAMPLECOUNT_1");
-                    failed = true;
-                }
-            } else {
-                // 2D Texture Validation
-                if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1 && createinfo->num_levels > 1) {
-                    SDL_assert_release(!"For 2D textures: if sample_count is >= SDL_GPU_SAMPLECOUNT_1, then num_levels must be 1");
-                    failed = true;
-                }
+            }
+            if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1 && createinfo->num_levels > 1) {
+                SDL_assert_release(!"For 2D multisample textures: num_levels must be 1");
+                failed = true;
             }
             if (!SDL_GPUTextureSupportsFormat(device, createinfo->format, SDL_GPU_TEXTURETYPE_2D, createinfo->usage)) {
                 SDL_assert_release(!"For 2D textures: the format is unsupported for the given usage");
@@ -1347,13 +1348,50 @@ SDL_GPURenderPass *SDL_BeginGPURenderPass(
         CHECK_ANY_PASS_IN_PROGRESS("Cannot begin render pass during another pass!", NULL)
 
         for (Uint32 i = 0; i < num_color_targets; i += 1) {
+            TextureCommonHeader *textureHeader = (TextureCommonHeader *)color_target_infos[i].texture;
+
             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!");
             }
+
+            if (color_target_infos[i].store_op == SDL_GPU_STOREOP_RESOLVE || color_target_infos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+                if (color_target_infos[i].resolve_texture == NULL) {
+                    SDL_assert_release(!"Store op is RESOLVE or RESOLVE_AND_STORE but resolve_texture is NULL!");
+                } else {
+                    TextureCommonHeader *resolveTextureHeader = (TextureCommonHeader *)color_target_infos[i].resolve_texture;
+                    if (textureHeader->info.sample_count == SDL_GPU_SAMPLECOUNT_1) {
+                        SDL_assert_release(!"Store op is RESOLVE or RESOLVE_AND_STORE but texture is not multisample!");
+                    }
+                    if (resolveTextureHeader->info.sample_count != SDL_GPU_SAMPLECOUNT_1) {
+                        SDL_assert_release(!"Resolve texture must have a sample count of 1!");
+                    }
+                    if (resolveTextureHeader->info.format != textureHeader->info.format) {
+                        SDL_assert_release(!"Resolve texture must have the same format as its corresponding color target!");
+                    }
+                    if (resolveTextureHeader->info.type == SDL_GPU_TEXTURETYPE_3D) {
+                        SDL_assert_release(!"Resolve texture must not be of TEXTURETYPE_3D!");
+                    }
+                }
+            }
         }
 
-        if (depth_stencil_target_info != NULL && depth_stencil_target_info->cycle && (depth_stencil_target_info->load_op == SDL_GPU_LOADOP_LOAD || depth_stencil_target_info->load_op == SDL_GPU_LOADOP_LOAD)) {
-            SDL_assert_release(!"Cannot cycle depth target when load op or stencil load op is LOAD!");
+        if (depth_stencil_target_info != NULL) {
+
+            TextureCommonHeader *textureHeader = (TextureCommonHeader *)depth_stencil_target_info->texture;
+            if (!(textureHeader->info.usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
+                SDL_assert_release(!"Depth target must have been created with the DEPTH_STENCIL_TARGET usage flag!");
+            }
+
+            if (depth_stencil_target_info->cycle && (depth_stencil_target_info->load_op == SDL_GPU_LOADOP_LOAD || depth_stencil_target_info->stencil_load_op == SDL_GPU_LOADOP_LOAD)) {
+                SDL_assert_release(!"Cannot cycle depth target when load op or stencil load op is LOAD!");
+            }
+
+            if (depth_stencil_target_info->store_op == SDL_GPU_STOREOP_RESOLVE ||
+                depth_stencil_target_info->stencil_store_op == SDL_GPU_STOREOP_RESOLVE ||
+                depth_stencil_target_info->store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE ||
+                depth_stencil_target_info->stencil_store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+                SDL_assert_release(!"RESOLVE store ops are not supported for depth-stencil targets!");
+            }
         }
     }
 
@@ -2397,6 +2435,10 @@ void SDL_BlitGPUTexture(
             SDL_assert_release(!"Blit destination texture must be non-NULL");
             return; // attempting to proceed will crash
         }
+        if (srcHeader->info.sample_count != SDL_GPU_SAMPLECOUNT_1) {
+            SDL_assert_release(!"Blit source texture must have a sample count of 1");
+            failed = true;
+        }
         if ((srcHeader->info.usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) == 0) {
             SDL_assert_release(!"Blit source texture must be created with the SAMPLER usage flag");
             failed = true;

+ 53 - 105
src/gpu/d3d11/SDL_gpu_d3d11.c

@@ -438,9 +438,6 @@ typedef struct D3D11TextureSubresource
 
     ID3D11UnorderedAccessView *uav;                 // NULL if not a storage texture
     ID3D11DepthStencilView *depthStencilTargetView; // NULL if not a depth stencil target
-
-    ID3D11Resource *msaaHandle;             // NULL if not using MSAA
-    ID3D11RenderTargetView *msaaTargetView; // NULL if not an MSAA color target
 } D3D11TextureSubresource;
 
 struct D3D11Texture
@@ -637,12 +634,8 @@ typedef struct D3D11CommandBuffer
     D3D11GraphicsPipeline *graphicsPipeline;
     Uint8 stencilRef;
     SDL_FColor blendConstants;
-
-    // Render Pass MSAA resolve
-    D3D11Texture *colorTargetResolveTexture[MAX_COLOR_TARGET_BINDINGS];
-    Uint32 colorTargetResolveSubresourceIndex[MAX_COLOR_TARGET_BINDINGS];
-    ID3D11Resource *colorTargetMsaaHandle[MAX_COLOR_TARGET_BINDINGS];
-    DXGI_FORMAT colorTargetMsaaFormat[MAX_COLOR_TARGET_BINDINGS];
+    D3D11TextureSubresource *colorTargetSubresources[MAX_COLOR_TARGET_BINDINGS];
+    D3D11TextureSubresource *colorResolveSubresources[MAX_COLOR_TARGET_BINDINGS];
 
     // Compute Pass
     D3D11ComputePipeline *computePipeline;
@@ -1090,14 +1083,6 @@ static void D3D11_INTERNAL_DestroyTexture(D3D11Texture *d3d11Texture)
     }
 
     for (Uint32 subresourceIndex = 0; subresourceIndex < d3d11Texture->subresourceCount; subresourceIndex += 1) {
-        if (d3d11Texture->subresources[subresourceIndex].msaaHandle != NULL) {
-            ID3D11Resource_Release(d3d11Texture->subresources[subresourceIndex].msaaHandle);
-        }
-
-        if (d3d11Texture->subresources[subresourceIndex].msaaTargetView != NULL) {
-            ID3D11RenderTargetView_Release(d3d11Texture->subresources[subresourceIndex].msaaTargetView);
-        }
-
         if (d3d11Texture->subresources[subresourceIndex].colorTargetViews != NULL) {
             for (Uint32 depthIndex = 0; depthIndex < d3d11Texture->subresources[subresourceIndex].depth; depthIndex += 1) {
                 ID3D11RenderTargetView_Release(d3d11Texture->subresources[subresourceIndex].colorTargetViews[depthIndex]);
@@ -1933,8 +1918,8 @@ static D3D11Texture *D3D11_INTERNAL_CreateTexture(
         desc2D.Format = format;
         desc2D.MipLevels = createInfo->num_levels;
         desc2D.MiscFlags = 0;
-        desc2D.SampleDesc.Count = 1;
-        desc2D.SampleDesc.Quality = 0;
+        desc2D.SampleDesc.Count = SDLToD3D11_SampleCount[createInfo->sample_count];
+        desc2D.SampleDesc.Quality = isMultisample ? D3D11_STANDARD_MULTISAMPLE_PATTERN : 0;
         desc2D.Usage = isStaging ? D3D11_USAGE_STAGING : D3D11_USAGE_DEFAULT;
 
         if (createInfo->type == SDL_GPU_TEXTURETYPE_CUBE || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
@@ -2067,54 +2052,10 @@ static D3D11Texture *D3D11_INTERNAL_CreateTexture(
             d3d11Texture->subresources[subresourceIndex].colorTargetViews = NULL;
             d3d11Texture->subresources[subresourceIndex].uav = NULL;
             d3d11Texture->subresources[subresourceIndex].depthStencilTargetView = NULL;
-            d3d11Texture->subresources[subresourceIndex].msaaHandle = NULL;
-            d3d11Texture->subresources[subresourceIndex].msaaTargetView = NULL;
-
-            if (isMultisample) {
-                D3D11_TEXTURE2D_DESC desc2D;
-
-                if (isColorTarget) {
-                    desc2D.BindFlags = D3D11_BIND_RENDER_TARGET;
-                } else if (isDepthStencil) {
-                    desc2D.BindFlags = D3D11_BIND_DEPTH_STENCIL;
-                }
-
-                desc2D.Width = createInfo->width;
-                desc2D.Height = createInfo->height;
-                desc2D.ArraySize = 1;
-                desc2D.CPUAccessFlags = 0;
-                desc2D.Format = format;
-                desc2D.MipLevels = 1;
-                desc2D.MiscFlags = 0;
-                desc2D.SampleDesc.Count = SDLToD3D11_SampleCount[createInfo->sample_count];
-                desc2D.SampleDesc.Quality = (UINT)D3D11_STANDARD_MULTISAMPLE_PATTERN;
-                desc2D.Usage = D3D11_USAGE_DEFAULT;
-
-                res = ID3D11Device_CreateTexture2D(
-                    renderer->device,
-                    &desc2D,
-                    NULL,
-                    (ID3D11Texture2D **)&d3d11Texture->subresources[subresourceIndex].msaaHandle);
-                ERROR_CHECK_RETURN("Could not create MSAA texture!", NULL);
-
-                if (!isDepthStencil) {
-                    D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
-
-                    rtvDesc.Format = format;
-                    rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
-
-                    res = ID3D11Device_CreateRenderTargetView(
-                        renderer->device,
-                        d3d11Texture->subresources[subresourceIndex].msaaHandle,
-                        &rtvDesc,
-                        &d3d11Texture->subresources[subresourceIndex].msaaTargetView);
-                    ERROR_CHECK_RETURN("Could not create MSAA RTV!", NULL);
-                }
-            }
 
             if (isDepthStencil) {
-                D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
 
+                D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc;
                 dsvDesc.Format = SDLToD3D11_TextureFormat[createInfo->format];
                 dsvDesc.Flags = 0;
 
@@ -2127,31 +2068,42 @@ static D3D11Texture *D3D11_INTERNAL_CreateTexture(
 
                 res = ID3D11Device_CreateDepthStencilView(
                     renderer->device,
-                    isMultisample ? d3d11Texture->subresources[subresourceIndex].msaaHandle : d3d11Texture->handle,
+                    d3d11Texture->handle,
                     &dsvDesc,
                     &d3d11Texture->subresources[subresourceIndex].depthStencilTargetView);
                 ERROR_CHECK_RETURN("Could not create DSV!", NULL);
+
             } else if (isColorTarget) {
+
                 d3d11Texture->subresources[subresourceIndex].colorTargetViews = SDL_calloc(depth, sizeof(ID3D11RenderTargetView *));
 
                 for (Uint32 depthIndex = 0; depthIndex < depth; depthIndex += 1) {
                     D3D11_RENDER_TARGET_VIEW_DESC rtvDesc;
-
                     rtvDesc.Format = SDLToD3D11_TextureFormat[createInfo->format];
 
-                    if (createInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
-                        rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
-                        rtvDesc.Texture2DArray.MipSlice = levelIndex;
-                        rtvDesc.Texture2DArray.FirstArraySlice = layerIndex;
-                        rtvDesc.Texture2DArray.ArraySize = 1;
-                    } else if (createInfo->type == SDL_GPU_TEXTURETYPE_3D) {
-                        rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
-                        rtvDesc.Texture3D.MipSlice = levelIndex;
-                        rtvDesc.Texture3D.FirstWSlice = depthIndex;
-                        rtvDesc.Texture3D.WSize = 1;
+                    if (isMultisample) {
+                        if (createInfo->type == SDL_GPU_TEXTURETYPE_2D) {
+                            rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMS;
+                        } else if (createInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
+                            rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY;
+                            rtvDesc.Texture2DMSArray.FirstArraySlice = layerIndex;
+                            rtvDesc.Texture2DMSArray.ArraySize = 1;
+                        }
                     } else {
-                        rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
-                        rtvDesc.Texture2D.MipSlice = levelIndex;
+                        if (createInfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE || createInfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
+                            rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+                            rtvDesc.Texture2DArray.MipSlice = levelIndex;
+                            rtvDesc.Texture2DArray.FirstArraySlice = layerIndex;
+                            rtvDesc.Texture2DArray.ArraySize = 1;
+                        } else if (createInfo->type == SDL_GPU_TEXTURETYPE_3D) {
+                            rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
+                            rtvDesc.Texture3D.MipSlice = levelIndex;
+                            rtvDesc.Texture3D.FirstWSlice = depthIndex;
+                            rtvDesc.Texture3D.WSize = 1;
+                        } else {
+                            rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
+                            rtvDesc.Texture2D.MipSlice = levelIndex;
+                        }
                     }
 
                     res = ID3D11Device_CreateRenderTargetView(
@@ -3238,10 +3190,8 @@ static SDL_GPUCommandBuffer *D3D11_AcquireCommandBuffer(
     commandBuffer->blendConstants.a = 1.0f;
     commandBuffer->computePipeline = NULL;
     for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
-        commandBuffer->colorTargetResolveTexture[i] = NULL;
-        commandBuffer->colorTargetResolveSubresourceIndex[i] = 0;
-        commandBuffer->colorTargetMsaaHandle[i] = NULL;
-        commandBuffer->colorTargetMsaaFormat[i] = DXGI_FORMAT_UNKNOWN;
+        commandBuffer->colorTargetSubresources[i] = NULL;
+        commandBuffer->colorResolveSubresources[i] = NULL;
     }
 
     for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
@@ -3507,10 +3457,8 @@ static void D3D11_BeginRenderPass(
 
     // Clear the bound targets for the current command buffer
     for (Uint32 i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
-        d3d11CommandBuffer->colorTargetResolveTexture[i] = NULL;
-        d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i] = 0;
-        d3d11CommandBuffer->colorTargetMsaaHandle[i] = NULL;
-        d3d11CommandBuffer->colorTargetMsaaFormat[i] = DXGI_FORMAT_UNKNOWN;
+        d3d11CommandBuffer->colorTargetSubresources[i] = NULL;
+        d3d11CommandBuffer->colorResolveSubresources[i] = NULL;
     }
 
     // Set up the new color target bindings
@@ -3523,16 +3471,20 @@ static void D3D11_BeginRenderPass(
             colorTargetInfos[i].mip_level,
             colorTargetInfos[i].cycle);
 
-        if (subresource->msaaHandle != NULL) {
-            d3d11CommandBuffer->colorTargetResolveTexture[i] = subresource->parent;
-            d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i] = subresource->index;
-            d3d11CommandBuffer->colorTargetMsaaHandle[i] = subresource->msaaHandle;
-            d3d11CommandBuffer->colorTargetMsaaFormat[i] = SDLToD3D11_TextureFormat[container->header.info.format];
+        Uint32 rtvIndex = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0;
+        rtvs[i] = subresource->colorTargetViews[rtvIndex];
+        d3d11CommandBuffer->colorTargetSubresources[i] = subresource;
 
-            rtvs[i] = subresource->msaaTargetView;
-        } else {
-            Uint32 rtvIndex = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0;
-            rtvs[i] = subresource->colorTargetViews[rtvIndex];
+        if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+            D3D11TextureContainer *resolveContainer = (D3D11TextureContainer *)colorTargetInfos[i].resolve_texture;
+            D3D11TextureSubresource *resolveSubresource = D3D11_INTERNAL_PrepareTextureSubresourceForWrite(
+                renderer,
+                resolveContainer,
+                colorTargetInfos[i].resolve_layer,
+                colorTargetInfos[i].resolve_mip_level,
+                colorTargetInfos[i].cycle_resolve_texture);
+
+            d3d11CommandBuffer->colorResolveSubresources[i] = resolveSubresource;
         }
 
         if (colorTargetInfos[i].load_op == SDL_GPU_LOADOP_CLEAR) {
@@ -4220,14 +4172,14 @@ static void D3D11_EndRenderPass(
 
     // Resolve MSAA color render targets
     for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
-        if (d3d11CommandBuffer->colorTargetMsaaHandle[i] != NULL) {
+        if (d3d11CommandBuffer->colorResolveSubresources[i] != NULL) {
             ID3D11DeviceContext_ResolveSubresource(
                 d3d11CommandBuffer->context,
-                d3d11CommandBuffer->colorTargetResolveTexture[i]->handle,
-                d3d11CommandBuffer->colorTargetResolveSubresourceIndex[i],
-                d3d11CommandBuffer->colorTargetMsaaHandle[i],
-                0,
-                d3d11CommandBuffer->colorTargetMsaaFormat[i]);
+                d3d11CommandBuffer->colorResolveSubresources[i]->parent->handle,
+                d3d11CommandBuffer->colorResolveSubresources[i]->index,
+                d3d11CommandBuffer->colorTargetSubresources[i]->parent->handle,
+                d3d11CommandBuffer->colorTargetSubresources[i]->index,
+                SDLToD3D11_TextureFormat[d3d11CommandBuffer->colorTargetSubresources[i]->parent->container->header.info.format]);
         }
     }
 
@@ -5083,8 +5035,6 @@ static bool D3D11_INTERNAL_InitializeSwapchainTexture(
         return false;
     }
 
-    // Create container
-
     // Fill out the texture struct
     pTexture->handle = NULL;     // This will be set in AcquireSwapchainTexture.
     pTexture->shaderView = NULL; // We don't allow swapchain texture to be sampled
@@ -5095,8 +5045,6 @@ static bool D3D11_INTERNAL_InitializeSwapchainTexture(
     pTexture->subresources[0].colorTargetViews[0] = rtv;
     pTexture->subresources[0].uav = NULL;
     pTexture->subresources[0].depthStencilTargetView = NULL;
-    pTexture->subresources[0].msaaHandle = NULL;
-    pTexture->subresources[0].msaaTargetView = NULL;
     pTexture->subresources[0].layer = 0;
     pTexture->subresources[0].level = 0;
     pTexture->subresources[0].depth = 1;

+ 92 - 48
src/gpu/d3d12/SDL_gpu_d3d12.c

@@ -673,8 +673,8 @@ struct D3D12CommandBuffer
     Uint32 presentDataCount;
     Uint32 presentDataCapacity;
 
-    Uint32 colorTargetTextureSubresourceCount;
-    D3D12TextureSubresource *colorTargetTextureSubresources[MAX_COLOR_TARGET_BINDINGS];
+    D3D12TextureSubresource *colorTargetSubresources[MAX_COLOR_TARGET_BINDINGS];
+    D3D12TextureSubresource *colorResolveSubresources[MAX_COLOR_TARGET_BINDINGS];
     D3D12TextureSubresource *depthStencilTextureSubresource;
     D3D12GraphicsPipeline *currentGraphicsPipeline;
     D3D12ComputePipeline *currentComputePipeline;
@@ -2619,7 +2619,7 @@ static SDL_GPUGraphicsPipeline *D3D12_CreateGraphicsPipeline(
 
     psoDesc.SampleMask = sampleMask;
     psoDesc.SampleDesc.Count = SDLToD3D12_SampleCount[createinfo->multisample_state.sample_count];
-    psoDesc.SampleDesc.Quality = 0;
+    psoDesc.SampleDesc.Quality = (createinfo->multisample_state.sample_count > SDL_GPU_SAMPLECOUNT_1) ? D3D12_STANDARD_MULTISAMPLE_PATTERN : 0;
 
     psoDesc.DSVFormat = SDLToD3D12_TextureFormat[createinfo->target_info.depth_stencil_format];
     psoDesc.NumRenderTargets = createinfo->target_info.num_color_targets;
@@ -2787,6 +2787,7 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture(
 
     Uint32 layerCount = createinfo->type == SDL_GPU_TEXTURETYPE_3D ? 1 : createinfo->layer_count_or_depth;
     Uint32 depth = createinfo->type == SDL_GPU_TEXTURETYPE_3D ? createinfo->layer_count_or_depth : 1;
+    bool isMultisample = createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1;
 
     if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
         resourceFlags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET;
@@ -2824,8 +2825,8 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture(
         desc.DepthOrArraySize = (UINT16)createinfo->layer_count_or_depth;
         desc.MipLevels = (UINT16)createinfo->num_levels;
         desc.Format = SDLToD3D12_TextureFormat[createinfo->format];
-        desc.SampleDesc.Count = 1;
-        desc.SampleDesc.Quality = 0;
+        desc.SampleDesc.Count = SDLToD3D12_SampleCount[createinfo->sample_count];
+        desc.SampleDesc.Quality = isMultisample ? D3D12_STANDARD_MULTISAMPLE_PATTERN : 0;
         desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; // Apparently this is the most efficient choice
         desc.Flags = resourceFlags;
     } else {
@@ -2956,21 +2957,31 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture(
 
                     rtvDesc.Format = SDLToD3D12_TextureFormat[createinfo->format];
 
-                    if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
-                        rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
-                        rtvDesc.Texture2DArray.MipSlice = levelIndex;
-                        rtvDesc.Texture2DArray.FirstArraySlice = layerIndex;
-                        rtvDesc.Texture2DArray.ArraySize = 1;
-                        rtvDesc.Texture2DArray.PlaneSlice = 0;
-                    } else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
-                        rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
-                        rtvDesc.Texture3D.MipSlice = levelIndex;
-                        rtvDesc.Texture3D.FirstWSlice = depthIndex;
-                        rtvDesc.Texture3D.WSize = 1;
+                    if (isMultisample) {
+                        if (createinfo->type == SDL_GPU_TEXTURETYPE_2D) {
+                            rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMS;
+                        } else if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
+                            rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DMSARRAY;
+                            rtvDesc.Texture2DMSArray.FirstArraySlice = layerIndex;
+                            rtvDesc.Texture2DMSArray.ArraySize = 1;
+                        }
                     } else {
-                        rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
-                        rtvDesc.Texture2D.MipSlice = levelIndex;
-                        rtvDesc.Texture2D.PlaneSlice = 0;
+                        if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
+                            rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2DARRAY;
+                            rtvDesc.Texture2DArray.MipSlice = levelIndex;
+                            rtvDesc.Texture2DArray.FirstArraySlice = layerIndex;
+                            rtvDesc.Texture2DArray.ArraySize = 1;
+                            rtvDesc.Texture2DArray.PlaneSlice = 0;
+                        } else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
+                            rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE3D;
+                            rtvDesc.Texture3D.MipSlice = levelIndex;
+                            rtvDesc.Texture3D.FirstWSlice = depthIndex;
+                            rtvDesc.Texture3D.WSize = 1;
+                        } else {
+                            rtvDesc.ViewDimension = D3D12_RTV_DIMENSION_TEXTURE2D;
+                            rtvDesc.Texture2D.MipSlice = levelIndex;
+                            rtvDesc.Texture2D.PlaneSlice = 0;
+                        }
                     }
 
                     ID3D12Device_CreateRenderTargetView(
@@ -2992,8 +3003,13 @@ static D3D12Texture *D3D12_INTERNAL_CreateTexture(
 
                 dsvDesc.Format = SDLToD3D12_TextureFormat[createinfo->format];
                 dsvDesc.Flags = (D3D12_DSV_FLAGS)0;
-                dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
-                dsvDesc.Texture2D.MipSlice = levelIndex;
+
+                if (isMultisample) {
+                    dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2DMS;
+                } else {
+                    dsvDesc.ViewDimension = D3D12_DSV_DIMENSION_TEXTURE2D;
+                    dsvDesc.Texture2D.MipSlice = levelIndex;
+                }
 
                 ID3D12Device_CreateDepthStencilView(
                     renderer->device,
@@ -3886,11 +3902,6 @@ static void D3D12_BeginRenderPass(
         if (h < framebufferHeight) {
             framebufferHeight = h;
         }
-
-        if (!(container->header.info.usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET)) {
-            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Color target texture was not designated as a color target!");
-            return;
-        }
     }
 
     if (depthStencilTargetInfo != NULL) {
@@ -3908,12 +3919,6 @@ static void D3D12_BeginRenderPass(
         if (h < framebufferHeight) {
             framebufferHeight = h;
         }
-
-        // Fixme:
-        if (!(container->header.info.usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
-            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Depth stencil target texture was not designated as a depth target!");
-            return;
-        }
     }
 
     D3D12_CPU_DESCRIPTOR_HANDLE rtvs[MAX_COLOR_TARGET_BINDINGS];
@@ -3929,8 +3934,7 @@ static void D3D12_BeginRenderPass(
             D3D12_RESOURCE_STATE_RENDER_TARGET);
 
         Uint32 rtvIndex = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0;
-        D3D12_CPU_DESCRIPTOR_HANDLE rtv =
-            subresource->rtvHandles[rtvIndex].cpuHandle;
+        D3D12_CPU_DESCRIPTOR_HANDLE rtv = subresource->rtvHandles[rtvIndex].cpuHandle;
 
         if (colorTargetInfos[i].load_op == SDL_GPU_LOADOP_CLEAR) {
             float clearColor[4];
@@ -3948,12 +3952,24 @@ static void D3D12_BeginRenderPass(
         }
 
         rtvs[i] = rtv;
-        d3d12CommandBuffer->colorTargetTextureSubresources[i] = subresource;
+        d3d12CommandBuffer->colorTargetSubresources[i] = subresource;
 
         D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, subresource->parent);
-    }
 
-    d3d12CommandBuffer->colorTargetTextureSubresourceCount = numColorTargets;
+        if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+            D3D12TextureContainer *resolveContainer = (D3D12TextureContainer *)colorTargetInfos[i].resolve_texture;
+            D3D12TextureSubresource *resolveSubresource = D3D12_INTERNAL_PrepareTextureSubresourceForWrite(
+                d3d12CommandBuffer,
+                resolveContainer,
+                colorTargetInfos[i].resolve_layer,
+                colorTargetInfos[i].resolve_mip_level,
+                colorTargetInfos[i].cycle_resolve_texture,
+                D3D12_RESOURCE_STATE_RESOLVE_DEST);
+
+            d3d12CommandBuffer->colorResolveSubresources[i] = resolveSubresource;
+            D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, resolveSubresource->parent);
+        }
+    }
 
     D3D12_CPU_DESCRIPTOR_HANDLE dsv;
     if (depthStencilTargetInfo != NULL) {
@@ -4780,15 +4796,42 @@ static void D3D12_EndRenderPass(
     D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
     Uint32 i;
 
-    for (i = 0; i < d3d12CommandBuffer->colorTargetTextureSubresourceCount; i += 1) {
-        D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
-            d3d12CommandBuffer,
-            D3D12_RESOURCE_STATE_RENDER_TARGET,
-            d3d12CommandBuffer->colorTargetTextureSubresources[i]);
-
-        d3d12CommandBuffer->colorTargetTextureSubresources[i] = NULL;
+    for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
+        if (d3d12CommandBuffer->colorTargetSubresources[i] != NULL) {
+            if (d3d12CommandBuffer->colorResolveSubresources[i] != NULL) {
+                // Resolving requires some extra barriers
+                D3D12_INTERNAL_TextureSubresourceBarrier(
+                    d3d12CommandBuffer,
+                    D3D12_RESOURCE_STATE_RENDER_TARGET,
+                    D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
+                    d3d12CommandBuffer->colorTargetSubresources[i]
+                );
+
+                ID3D12GraphicsCommandList_ResolveSubresource(
+                    d3d12CommandBuffer->graphicsCommandList,
+                    d3d12CommandBuffer->colorResolveSubresources[i]->parent->resource,
+                    d3d12CommandBuffer->colorResolveSubresources[i]->index,
+                    d3d12CommandBuffer->colorTargetSubresources[i]->parent->resource,
+                    d3d12CommandBuffer->colorTargetSubresources[i]->index,
+                    SDLToD3D12_TextureFormat[d3d12CommandBuffer->colorTargetSubresources[i]->parent->container->header.info.format]);
+
+                D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
+                    d3d12CommandBuffer,
+                    D3D12_RESOURCE_STATE_RESOLVE_SOURCE,
+                    d3d12CommandBuffer->colorTargetSubresources[i]);
+
+                D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
+                    d3d12CommandBuffer,
+                    D3D12_RESOURCE_STATE_RESOLVE_DEST,
+                    d3d12CommandBuffer->colorResolveSubresources[i]);
+            } else {
+                D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
+                    d3d12CommandBuffer,
+                    D3D12_RESOURCE_STATE_RENDER_TARGET,
+                    d3d12CommandBuffer->colorTargetSubresources[i]);
+            }
+        }
     }
-    d3d12CommandBuffer->colorTargetTextureSubresourceCount = 0;
 
     if (d3d12CommandBuffer->depthStencilTextureSubresource != NULL) {
         D3D12_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
@@ -4809,7 +4852,8 @@ static void D3D12_EndRenderPass(
         NULL);
 
     // Reset bind state
-    SDL_zeroa(d3d12CommandBuffer->colorTargetTextureSubresources);
+    SDL_zeroa(d3d12CommandBuffer->colorTargetSubresources);
+    SDL_zeroa(d3d12CommandBuffer->colorResolveSubresources);
     d3d12CommandBuffer->depthStencilTextureSubresource = NULL;
 
     SDL_zeroa(d3d12CommandBuffer->vertexBuffers);
@@ -6820,8 +6864,8 @@ static SDL_GPUCommandBuffer *D3D12_AcquireCommandBuffer(
     // Set the bind state
     commandBuffer->currentGraphicsPipeline = NULL;
 
-    SDL_zeroa(commandBuffer->colorTargetTextureSubresources);
-    commandBuffer->colorTargetTextureSubresourceCount = 0;
+    SDL_zeroa(commandBuffer->colorTargetSubresources);
+    SDL_zeroa(commandBuffer->colorResolveSubresources);
     commandBuffer->depthStencilTextureSubresource = NULL;
 
     SDL_zeroa(commandBuffer->vertexBuffers);

+ 29 - 65
src/gpu/metal/SDL_gpu_metal.m

@@ -294,6 +294,13 @@ static MTLLoadAction SDLToMetal_LoadOp[] = {
     MTLLoadActionDontCare, // DONT_CARE
 };
 
+static MTLStoreAction SDLToMetal_StoreOp[] = {
+    MTLStoreActionStore,
+    MTLStoreActionDontCare,
+    MTLStoreActionMultisampleResolve,
+    MTLStoreActionStoreAndMultisampleResolve
+};
+
 static MTLVertexStepFunction SDLToMetal_StepFunction[] = {
     MTLVertexStepFunctionPerVertex,
     MTLVertexStepFunctionPerInstance,
@@ -323,25 +330,6 @@ static SDL_GPUTextureFormat SwapchainCompositionToFormat[] = {
 
 static CFStringRef SwapchainCompositionToColorSpace[4]; // initialized on device creation
 
-static MTLStoreAction SDLToMetal_StoreOp(
-    SDL_GPUStoreOp storeOp,
-    Uint8 isMultisample)
-{
-    if (isMultisample) {
-        if (storeOp == SDL_GPU_STOREOP_STORE) {
-            return MTLStoreActionStoreAndMultisampleResolve;
-        } else {
-            return MTLStoreActionMultisampleResolve;
-        }
-    } else {
-        if (storeOp == SDL_GPU_STOREOP_STORE) {
-            return MTLStoreActionStore;
-        } else {
-            return MTLStoreActionDontCare;
-        }
-    }
-};
-
 static MTLColorWriteMask SDLToMetal_ColorWriteMask(
     SDL_GPUColorComponentFlags mask)
 {
@@ -366,7 +354,6 @@ static MTLColorWriteMask SDLToMetal_ColorWriteMask(
 typedef struct MetalTexture
 {
     id<MTLTexture> handle;
-    id<MTLTexture> msaaHandle;
     SDL_AtomicInt referenceCount;
 } MetalTexture;
 
@@ -828,7 +815,6 @@ static void METAL_INTERNAL_DestroyTextureContainer(
 {
     for (Uint32 i = 0; i < container->textureCount; i += 1) {
         container->textures[i]->handle = nil;
-        container->textures[i]->msaaHandle = nil;
         SDL_free(container->textures[i]);
     }
     if (container->debugName != NULL) {
@@ -1329,7 +1315,6 @@ static MetalTexture *METAL_INTERNAL_CreateTexture(
 {
     MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor new];
     id<MTLTexture> texture;
-    id<MTLTexture> msaaTexture = NULL;
     MetalTexture *metalTexture;
 
     textureDescriptor.textureType = SDLToMetal_TextureType[createinfo->type];
@@ -1347,7 +1332,7 @@ static MetalTexture *METAL_INTERNAL_CreateTexture(
     textureDescriptor.height = createinfo->height;
     textureDescriptor.depth = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? createinfo->layer_count_or_depth : 1;
     textureDescriptor.mipmapLevelCount = createinfo->num_levels;
-    textureDescriptor.sampleCount = 1;
+    textureDescriptor.sampleCount = SDLToMetal_SampleCount[createinfo->sample_count];
     textureDescriptor.arrayLength =
         (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY)
             ? createinfo->layer_count_or_depth
@@ -1374,22 +1359,8 @@ static MetalTexture *METAL_INTERNAL_CreateTexture(
         return NULL;
     }
 
-    // Create the MSAA texture, if needed
-    if (createinfo->sample_count > SDL_GPU_SAMPLECOUNT_1 && createinfo->type == SDL_GPU_TEXTURETYPE_2D) {
-        textureDescriptor.textureType = MTLTextureType2DMultisample;
-        textureDescriptor.sampleCount = SDLToMetal_SampleCount[createinfo->sample_count];
-        textureDescriptor.usage = MTLTextureUsageRenderTarget;
-
-        msaaTexture = [renderer->device newTextureWithDescriptor:textureDescriptor];
-        if (msaaTexture == NULL) {
-            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create MSAA MTLTexture!");
-            return NULL;
-        }
-    }
-
     metalTexture = (MetalTexture *)SDL_calloc(1, sizeof(MetalTexture));
     metalTexture->handle = texture;
-    metalTexture->msaaHandle = msaaTexture;
     SDL_AtomicSet(&metalTexture->referenceCount, 0);
     return metalTexture;
 }
@@ -2189,12 +2160,7 @@ static void METAL_BeginRenderPass(
                 container,
                 colorTargetInfos[i].cycle);
 
-            if (texture->msaaHandle) {
-                passDescriptor.colorAttachments[i].texture = texture->msaaHandle;
-                passDescriptor.colorAttachments[i].resolveTexture = texture->handle;
-            } else {
-                passDescriptor.colorAttachments[i].texture = texture->handle;
-            }
+            passDescriptor.colorAttachments[i].texture = texture->handle;
             passDescriptor.colorAttachments[i].level = colorTargetInfos[i].mip_level;
             if (container->header.info.type == SDL_GPU_TEXTURETYPE_3D) {
                 passDescriptor.colorAttachments[i].depthPlane = colorTargetInfos[i].layer_or_depth_plane;
@@ -2207,11 +2173,23 @@ static void METAL_BeginRenderPass(
                 colorTargetInfos[i].clear_color.b,
                 colorTargetInfos[i].clear_color.a);
             passDescriptor.colorAttachments[i].loadAction = SDLToMetal_LoadOp[colorTargetInfos[i].load_op];
-            passDescriptor.colorAttachments[i].storeAction = SDLToMetal_StoreOp(
-                colorTargetInfos[i].store_op,
-                texture->msaaHandle ? 1 : 0);
+            passDescriptor.colorAttachments[i].storeAction = SDLToMetal_StoreOp[colorTargetInfos[i].store_op];
 
             METAL_INTERNAL_TrackTexture(metalCommandBuffer, texture);
+
+            if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+                MetalTextureContainer *resolveContainer = (MetalTextureContainer *)colorTargetInfos[i].resolve_texture;
+                MetalTexture *resolveTexture = METAL_INTERNAL_PrepareTextureForWrite(
+                    renderer,
+                    resolveContainer,
+                    colorTargetInfos[i].cycle_resolve_texture);
+
+                passDescriptor.colorAttachments[i].resolveTexture = resolveTexture->handle;
+                passDescriptor.colorAttachments[i].resolveSlice = colorTargetInfos[i].resolve_layer;
+                passDescriptor.colorAttachments[i].resolveLevel = colorTargetInfos[i].resolve_mip_level;
+
+                METAL_INTERNAL_TrackTexture(metalCommandBuffer, resolveTexture);
+            }
         }
 
         if (depthStencilTargetInfo != NULL) {
@@ -2221,29 +2199,15 @@ static void METAL_BeginRenderPass(
                 container,
                 depthStencilTargetInfo->cycle);
 
-            if (texture->msaaHandle) {
-                passDescriptor.depthAttachment.texture = texture->msaaHandle;
-                passDescriptor.depthAttachment.resolveTexture = texture->handle;
-            } else {
-                passDescriptor.depthAttachment.texture = texture->handle;
-            }
+            passDescriptor.depthAttachment.texture = texture->handle;
             passDescriptor.depthAttachment.loadAction = SDLToMetal_LoadOp[depthStencilTargetInfo->load_op];
-            passDescriptor.depthAttachment.storeAction = SDLToMetal_StoreOp(
-                depthStencilTargetInfo->store_op,
-                texture->msaaHandle ? 1 : 0);
+            passDescriptor.depthAttachment.storeAction = SDLToMetal_StoreOp[depthStencilTargetInfo->store_op];
             passDescriptor.depthAttachment.clearDepth = depthStencilTargetInfo->clear_depth;
 
             if (IsStencilFormat(container->header.info.format)) {
-                if (texture->msaaHandle) {
-                    passDescriptor.stencilAttachment.texture = texture->msaaHandle;
-                    passDescriptor.stencilAttachment.resolveTexture = texture->handle;
-                } else {
-                    passDescriptor.stencilAttachment.texture = texture->handle;
-                }
-                passDescriptor.stencilAttachment.loadAction = SDLToMetal_LoadOp[depthStencilTargetInfo->load_op];
-                passDescriptor.stencilAttachment.storeAction = SDLToMetal_StoreOp(
-                    depthStencilTargetInfo->store_op,
-                    texture->msaaHandle ? 1 : 0);
+                passDescriptor.stencilAttachment.texture = texture->handle;
+                passDescriptor.stencilAttachment.loadAction = SDLToMetal_LoadOp[depthStencilTargetInfo->stencil_load_op];
+                passDescriptor.stencilAttachment.storeAction = SDLToMetal_StoreOp[depthStencilTargetInfo->stencil_store_op];
                 passDescriptor.stencilAttachment.clearStencil = depthStencilTargetInfo->clear_stencil;
             }
 

+ 451 - 840
src/gpu/vulkan/SDL_gpu_vulkan.c

@@ -135,7 +135,7 @@ static VkPresentModeKHR SDLToVK_PresentMode[] = {
     VK_PRESENT_MODE_MAILBOX_KHR
 };
 
-static VkFormat SDLToVK_SurfaceFormat[] = {
+static VkFormat SDLToVK_TextureFormat[] = {
     VK_FORMAT_UNDEFINED,                // INVALID
     VK_FORMAT_R8_UNORM,                 // A8_UNORM
     VK_FORMAT_R8_UNORM,                 // R8_UNORM
@@ -194,7 +194,7 @@ static VkFormat SDLToVK_SurfaceFormat[] = {
     VK_FORMAT_D24_UNORM_S8_UINT,        // D24_UNORM_S8_UINT
     VK_FORMAT_D32_SFLOAT_S8_UINT,       // D32_FLOAT_S8_UINT
 };
-SDL_COMPILE_TIME_ASSERT(SDLToVK_SurfaceFormat, SDL_arraysize(SDLToVK_SurfaceFormat) == SDL_GPU_TEXTUREFORMAT_MAX_ENUM_VALUE);
+SDL_COMPILE_TIME_ASSERT(SDLToVK_TextureFormat, SDL_arraysize(SDLToVK_TextureFormat) == SDL_GPU_TEXTUREFORMAT_MAX_ENUM_VALUE);
 
 static VkComponentMapping SwizzleForSDLFormat(SDL_GPUTextureFormat format)
 {
@@ -396,7 +396,9 @@ static VkAttachmentLoadOp SDLToVK_LoadOp[] = {
 
 static VkAttachmentStoreOp SDLToVK_StoreOp[] = {
     VK_ATTACHMENT_STORE_OP_STORE,
-    VK_ATTACHMENT_STORE_OP_DONT_CARE
+    VK_ATTACHMENT_STORE_OP_DONT_CARE,
+    VK_ATTACHMENT_STORE_OP_DONT_CARE,
+    VK_ATTACHMENT_STORE_OP_STORE
 };
 
 static VkSampleCountFlagBits SDLToVK_SampleCount[] = {
@@ -503,15 +505,6 @@ typedef struct VulkanMemoryAllocator
 
 // Memory structures
 
-/* We use pointer indirection so that defrag can occur without objects
- * needing to be aware of the backing buffers changing.
- */
-typedef struct VulkanBufferHandle
-{
-    VulkanBuffer *vulkanBuffer;
-    VulkanBufferContainer *container;
-} VulkanBufferHandle;
-
 typedef enum VulkanBufferType
 {
     VULKAN_BUFFER_TYPE_GPU,
@@ -521,38 +514,29 @@ typedef enum VulkanBufferType
 
 struct VulkanBuffer
 {
+    VulkanBufferContainer *container;
+    Uint32 containerIndex;
+
     VkBuffer buffer;
-    VkDeviceSize size;
     VulkanMemoryUsedRegion *usedRegion;
 
+    // Needed for uniforms and defrag
     VulkanBufferType type;
-    SDL_GPUBufferUsageFlags usageFlags;
-
-    SDL_AtomicInt referenceCount; // Tracks command buffer usage
-
-    VulkanBufferHandle *handle;
+    SDL_GPUBufferUsageFlags usage;
+    VkDeviceSize size;
 
+    SDL_AtomicInt referenceCount;
     bool transitioned;
-    Uint8 markedForDestroy; // so that defrag doesn't double-free
+    bool markedForDestroy; // so that defrag doesn't double-free
 };
 
-/* Buffer resources consist of multiple backing buffer handles so that data transfers
- * can occur without blocking or the client having to manage extra resources.
- *
- * Cast from SDL_GPUBuffer or SDL_GPUTransferBuffer.
- */
 struct VulkanBufferContainer
 {
-    VulkanBufferHandle *activeBufferHandle;
+    VulkanBuffer *activeBuffer;
 
-    /* These are all the buffer handles that have been used by this container.
-     * If the resource is bound and then updated with a cycle parameter, a new resource
-     * will be added to this list.
-     * These can be reused after they are submitted and command processing is complete.
-     */
+    VulkanBuffer **buffers;
     Uint32 bufferCapacity;
     Uint32 bufferCount;
-    VulkanBufferHandle **bufferHandles;
 
     char *debugName;
 };
@@ -584,12 +568,6 @@ typedef struct VulkanShader
     SDL_AtomicInt referenceCount;
 } VulkanShader;
 
-typedef struct VulkanTextureHandle
-{
-    VulkanTexture *vulkanTexture;
-    VulkanTextureContainer *container;
-} VulkanTextureHandle;
-
 /* Textures are made up of individual subresources.
  * This helps us barrier the resource efficiently.
  */
@@ -603,64 +581,44 @@ typedef struct VulkanTextureSubresource
     VkImageView computeWriteView;
     VkImageView depthStencilView;
 
-    VulkanTextureHandle *msaaTexHandle; // NULL if parent sample count is 1 or is depth target
-
     bool transitioned; // used for layout tracking
 } VulkanTextureSubresource;
 
 struct VulkanTexture
 {
+    VulkanTextureContainer *container;
+    Uint32 containerIndex;
+
     VulkanMemoryUsedRegion *usedRegion;
 
     VkImage image;
     VkImageView fullView; // used for samplers and storage reads
-    VkExtent2D dimensions;
-
-    SDL_GPUTextureType type;
-    Uint8 isMSAAColorTarget;
-
-    Uint32 depth;
-    Uint32 layerCount;
-    Uint32 numLevels;
-    VkSampleCountFlagBits sampleCount; // NOTE: This refers to the sample count of a render target pass using this texture, not the actual sample count of the texture
-    VkFormat format;
     VkComponentMapping swizzle;
-    SDL_GPUTextureUsageFlags usageFlags;
     VkImageAspectFlags aspectFlags;
+    Uint32 depth; // used for cleanup only
+
+    // FIXME: It'd be nice if we didn't have to have this on the texture...
+    SDL_GPUTextureUsageFlags usage; // used for defrag transitions only.
 
     Uint32 subresourceCount;
     VulkanTextureSubresource *subresources;
 
-    VulkanTextureHandle *handle;
-
-    Uint8 markedForDestroy; // so that defrag doesn't double-free
+    bool markedForDestroy; // so that defrag doesn't double-free
     SDL_AtomicInt referenceCount;
 };
 
-/* Texture resources consist of multiple backing texture handles so that data transfers
- * can occur without blocking or the client having to manage extra resources.
- *
- * Cast from SDL_GPUTexture.
- */
 struct VulkanTextureContainer
 {
-    TextureCommonHeader header; // FIXME: Use this instead of passing so many args to CreateTexture
+    TextureCommonHeader header;
 
-    VulkanTextureHandle *activeTextureHandle;
+    VulkanTexture *activeTexture;
 
-    /* These are all the texture handles that have been used by this container.
-     * If the resource is bound and then updated with CYCLE, a new resource
-     * will be added to this list.
-     * These can be reused after they are submitted and command processing is complete.
-     */
     Uint32 textureCapacity;
     Uint32 textureCount;
-    VulkanTextureHandle **textureHandles;
-
-    // Swapchain images cannot be cycled
-    Uint8 canBeCycled;
+    VulkanTexture **textures;
 
     char *debugName;
+    bool canBeCycled;
 };
 
 typedef enum VulkanBufferUsageMode
@@ -752,7 +710,7 @@ typedef struct VulkanPresentData
 
 typedef struct VulkanUniformBuffer
 {
-    VulkanBufferHandle *bufferHandle;
+    VulkanBuffer *buffer;
     Uint32 drawOffset;
     Uint32 writeOffset;
 } VulkanUniformBuffer;
@@ -874,7 +832,7 @@ typedef struct RenderPassHashTableKey
     RenderPassColorTargetDescription colorTargetDescriptions[MAX_COLOR_TARGET_BINDINGS];
     Uint32 numColorTargets;
     RenderPassDepthStencilTargetDescription depthStencilTargetDescription;
-    VkSampleCountFlagBits colorAttachmentSampleCount;
+    VkSampleCountFlagBits sampleCount;
 } RenderPassHashTableKey;
 
 typedef struct VulkanRenderPassHashTableValue
@@ -885,7 +843,6 @@ typedef struct VulkanRenderPassHashTableValue
 typedef struct FramebufferHashTableKey
 {
     VkImageView colorAttachmentViews[MAX_COLOR_TARGET_BINDINGS];
-    VkImageView colorMultiSampleAttachmentViews[MAX_COLOR_TARGET_BINDINGS];
     Uint32 numColorTargets;
     VkImageView depthStencilAttachmentView;
     Uint32 width;
@@ -940,6 +897,8 @@ typedef struct VulkanCommandBuffer
 
     VulkanTextureSubresource *colorAttachmentSubresources[MAX_COLOR_TARGET_BINDINGS];
     Uint32 colorAttachmentSubresourceCount;
+    VulkanTextureSubresource *resolveAttachmentSubresources[MAX_COLOR_TARGET_BINDINGS];
+    Uint32 resolveAttachmentSubresourceCount;
 
     VulkanTextureSubresource *depthStencilAttachmentSubresource; // may be NULL
 
@@ -1155,20 +1114,6 @@ static void VULKAN_ReleaseWindow(SDL_GPURenderer *driverData, SDL_Window *window
 static void VULKAN_Wait(SDL_GPURenderer *driverData);
 static void VULKAN_WaitForFences(SDL_GPURenderer *driverData, bool waitAll, SDL_GPUFence *const *fences, Uint32 numFences);
 static void VULKAN_Submit(SDL_GPUCommandBuffer *commandBuffer);
-static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
-    VulkanRenderer *renderer,
-    Uint32 width,
-    Uint32 height,
-    Uint32 depth,
-    SDL_GPUTextureType type,
-    Uint32 layerCount,
-    Uint32 numLevels,
-    VkSampleCountFlagBits sampleCount,
-    VkFormat format,
-    VkComponentMapping swizzle,
-    VkImageAspectFlags aspectMask,
-    SDL_GPUTextureUsageFlags textureUsageFlags,
-    bool isMSAAColorTarget);
 
 // Error Handling
 
@@ -1220,35 +1165,6 @@ static inline void LogVulkanResultAsError(
 
 // Utility
 
-static inline bool VULKAN_INTERNAL_IsVulkanDepthFormat(VkFormat format)
-{
-    // FIXME: Can we refactor and use the regular IsDepthFormat for this?
-    return (
-        format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D16_UNORM] ||
-        format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D24_UNORM] ||
-        format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D24_UNORM_S8_UINT] ||
-        format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D32_FLOAT] ||
-        format == SDLToVK_SurfaceFormat[SDL_GPU_TEXTUREFORMAT_D32_FLOAT_S8_UINT]);
-}
-
-static inline VkSampleCountFlagBits VULKAN_INTERNAL_GetMaxMultiSampleCount(
-    VulkanRenderer *renderer,
-    VkSampleCountFlagBits multiSampleCount)
-{
-    VkSampleCountFlags flags = renderer->physicalDeviceProperties.properties.limits.framebufferColorSampleCounts;
-    VkSampleCountFlagBits maxSupported = VK_SAMPLE_COUNT_1_BIT;
-
-    if (flags & VK_SAMPLE_COUNT_8_BIT) {
-        maxSupported = VK_SAMPLE_COUNT_8_BIT;
-    } else if (flags & VK_SAMPLE_COUNT_4_BIT) {
-        maxSupported = VK_SAMPLE_COUNT_4_BIT;
-    } else if (flags & VK_SAMPLE_COUNT_2_BIT) {
-        maxSupported = VK_SAMPLE_COUNT_2_BIT;
-    }
-
-    return SDL_min(multiSampleCount, maxSupported);
-}
-
 static inline VkPolygonMode SDLToVK_PolygonMode(
     VulkanRenderer *renderer,
     SDL_GPUFillMode mode)
@@ -2455,7 +2371,7 @@ static void VULKAN_INTERNAL_TrackUniformBuffer(
 
     VULKAN_INTERNAL_TrackBuffer(
         commandBuffer,
-        uniformBuffer->bufferHandle->vulkanBuffer);
+        uniformBuffer->buffer);
 }
 
 #undef TRACK_RESOURCE
@@ -2719,17 +2635,17 @@ static VulkanBufferUsageMode VULKAN_INTERNAL_DefaultBufferUsageMode(
 {
     // NOTE: order matters here!
 
-    if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_VERTEX) {
+    if (buffer->usage & SDL_GPU_BUFFERUSAGE_VERTEX) {
         return VULKAN_BUFFER_USAGE_MODE_VERTEX_READ;
-    } else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_INDEX) {
+    } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_INDEX) {
         return VULKAN_BUFFER_USAGE_MODE_INDEX_READ;
-    } else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_INDIRECT) {
+    } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_INDIRECT) {
         return VULKAN_BUFFER_USAGE_MODE_INDIRECT;
-    } else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ) {
+    } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_GRAPHICS_STORAGE_READ) {
         return VULKAN_BUFFER_USAGE_MODE_GRAPHICS_STORAGE_READ;
-    } else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ) {
+    } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_READ) {
         return VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ;
-    } else if (buffer->usageFlags & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
+    } else if (buffer->usage & SDL_GPU_BUFFERUSAGE_COMPUTE_STORAGE_WRITE) {
         return VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE;
     } else {
         SDL_LogError(SDL_LOG_CATEGORY_GPU, "Buffer has no default usage mode!");
@@ -2743,17 +2659,17 @@ static VulkanTextureUsageMode VULKAN_INTERNAL_DefaultTextureUsageMode(
     // NOTE: order matters here!
     // NOTE: graphics storage bits and sampler bit are mutually exclusive!
 
-    if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER) {
+    if (texture->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) {
         return VULKAN_TEXTURE_USAGE_MODE_SAMPLER;
-    } else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) {
+    } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) {
         return VULKAN_TEXTURE_USAGE_MODE_GRAPHICS_STORAGE_READ;
-    } else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
+    } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
         return VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT;
-    } else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
+    } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
         return VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT;
-    } else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ) {
+    } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ) {
         return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ;
-    } else if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
+    } else if (texture->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
         return VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ_WRITE;
     } else {
         SDL_LogError(SDL_LOG_CATEGORY_GPU, "Texture has no default usage mode!");
@@ -2935,20 +2851,13 @@ static void VULKAN_INTERNAL_DestroyTexture(
 {
     // Clean up subresources
     for (Uint32 subresourceIndex = 0; subresourceIndex < texture->subresourceCount; subresourceIndex += 1) {
-        if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
+        if (texture->subresources[subresourceIndex].renderTargetViews != NULL) {
             for (Uint32 depthIndex = 0; depthIndex < texture->depth; depthIndex += 1) {
                 VULKAN_INTERNAL_RemoveFramebuffersContainingView(
                     renderer,
                     texture->subresources[subresourceIndex].renderTargetViews[depthIndex]);
             }
 
-            if (texture->subresources[subresourceIndex].msaaTexHandle != NULL) {
-                VULKAN_INTERNAL_DestroyTexture(
-                    renderer,
-                    texture->subresources[subresourceIndex].msaaTexHandle->vulkanTexture);
-                SDL_free(texture->subresources[subresourceIndex].msaaTexHandle);
-            }
-
             for (Uint32 depthIndex = 0; depthIndex < texture->depth; depthIndex += 1) {
                 renderer->vkDestroyImageView(
                     renderer->logicalDevice,
@@ -2958,14 +2867,14 @@ static void VULKAN_INTERNAL_DestroyTexture(
             SDL_free(texture->subresources[subresourceIndex].renderTargetViews);
         }
 
-        if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
+        if (texture->subresources[subresourceIndex].computeWriteView != VK_NULL_HANDLE) {
             renderer->vkDestroyImageView(
                 renderer->logicalDevice,
                 texture->subresources[subresourceIndex].computeWriteView,
                 NULL);
         }
 
-        if (texture->usageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
+        if (texture->subresources[subresourceIndex].depthStencilView != VK_NULL_HANDLE) {
             renderer->vkDestroyImageView(
                 renderer->logicalDevice,
                 texture->subresources[subresourceIndex].depthStencilView,
@@ -3172,16 +3081,14 @@ static void VULKAN_INTERNAL_DestroySwapchain(
     for (i = 0; i < swapchainData->imageCount; i += 1) {
         VULKAN_INTERNAL_RemoveFramebuffersContainingView(
             renderer,
-            swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews[0]);
+            swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0]);
         renderer->vkDestroyImageView(
             renderer->logicalDevice,
-            swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews[0],
+            swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0],
             NULL);
-        SDL_free(swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews);
-        SDL_free(swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources);
-
-        SDL_free(swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture);
-        SDL_free(swapchainData->textureContainers[i].activeTextureHandle);
+        SDL_free(swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews);
+        SDL_free(swapchainData->textureContainers[i].activeTexture->subresources);
+        SDL_free(swapchainData->textureContainers[i].activeTexture);
     }
 
     SDL_free(swapchainData->textureContainers);
@@ -3259,7 +3166,7 @@ static Uint32 VULKAN_INTERNAL_RenderPassHashFunction(
     result = result * hashFactor + hashTableKey->depthStencilTargetDescription.stencilStoreOp;
     result = result * hashFactor + hashTableKey->depthStencilTargetDescription.format;
 
-    result = result * hashFactor + hashTableKey->colorAttachmentSampleCount;
+    result = result * hashFactor + hashTableKey->sampleCount;
 
     return result;
 }
@@ -3276,7 +3183,7 @@ static bool VULKAN_INTERNAL_RenderPassHashKeyMatch(
         return 0;
     }
 
-    if (a->colorAttachmentSampleCount != b->colorAttachmentSampleCount) {
+    if (a->sampleCount != b->sampleCount) {
         return 0;
     }
 
@@ -3344,7 +3251,6 @@ static Uint32 VULKAN_INTERNAL_FramebufferHashFunction(
 
     for (Uint32 i = 0; i < hashTableKey->numColorTargets; i += 1) {
         result = result * hashFactor + (Uint32)(uintptr_t)hashTableKey->colorAttachmentViews[i];
-        result = result * hashFactor + (Uint32)(uintptr_t)hashTableKey->colorMultiSampleAttachmentViews[i];
     }
 
     result = result * hashFactor + (Uint32)(uintptr_t)hashTableKey->depthStencilAttachmentView;
@@ -3370,10 +3276,6 @@ static bool VULKAN_INTERNAL_FramebufferHashKeyMatch(
         if (a->colorAttachmentViews[i] != b->colorAttachmentViews[i]) {
             return 0;
         }
-
-        if (a->colorMultiSampleAttachmentViews[i] != b->colorMultiSampleAttachmentViews[i]) {
-            return 0;
-        }
     }
 
     if (a->depthStencilAttachmentView != b->depthStencilAttachmentView) {
@@ -4047,9 +3949,9 @@ static VulkanBuffer *VULKAN_INTERNAL_CreateBuffer(
     buffer = SDL_malloc(sizeof(VulkanBuffer));
 
     buffer->size = size;
-    buffer->usageFlags = usageFlags;
+    buffer->usage = usageFlags;
     buffer->type = type;
-    buffer->markedForDestroy = 0;
+    buffer->markedForDestroy = false;
     buffer->transitioned = false;
 
     createinfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
@@ -4088,21 +3990,19 @@ static VulkanBuffer *VULKAN_INTERNAL_CreateBuffer(
     }
 
     buffer->usedRegion->vulkanBuffer = buffer; // lol
-    buffer->handle = NULL;
 
     SDL_AtomicSet(&buffer->referenceCount, 0);
 
     return buffer;
 }
 
-// Indirection so we can cleanly defrag buffers
-static VulkanBufferHandle *VULKAN_INTERNAL_CreateBufferHandle(
+static VulkanBufferContainer *VULKAN_INTERNAL_CreateBufferContainer(
     VulkanRenderer *renderer,
     VkDeviceSize size,
     SDL_GPUBufferUsageFlags usageFlags,
     VulkanBufferType type)
 {
-    VulkanBufferHandle *bufferHandle;
+    VulkanBufferContainer *bufferContainer;
     VulkanBuffer *buffer;
 
     buffer = VULKAN_INTERNAL_CreateBuffer(
@@ -4112,49 +4012,21 @@ static VulkanBufferHandle *VULKAN_INTERNAL_CreateBufferHandle(
         type);
 
     if (buffer == NULL) {
-        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create buffer!");
-        return NULL;
-    }
-
-    bufferHandle = SDL_malloc(sizeof(VulkanBufferHandle));
-    bufferHandle->vulkanBuffer = buffer;
-    bufferHandle->container = NULL;
-
-    buffer->handle = bufferHandle;
-
-    return bufferHandle;
-}
-
-static VulkanBufferContainer *VULKAN_INTERNAL_CreateBufferContainer(
-    VulkanRenderer *renderer,
-    VkDeviceSize size,
-    SDL_GPUBufferUsageFlags usageFlags,
-    VulkanBufferType type)
-{
-    VulkanBufferContainer *bufferContainer;
-    VulkanBufferHandle *bufferHandle;
-
-    bufferHandle = VULKAN_INTERNAL_CreateBufferHandle(
-        renderer,
-        size,
-        usageFlags,
-        type);
-
-    if (bufferHandle == NULL) {
         SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create buffer container!");
         return NULL;
     }
 
     bufferContainer = SDL_malloc(sizeof(VulkanBufferContainer));
 
-    bufferContainer->activeBufferHandle = bufferHandle;
-    bufferHandle->container = bufferContainer;
+    bufferContainer->activeBuffer = buffer;
+    buffer->container = bufferContainer;
+    buffer->containerIndex = 0;
 
     bufferContainer->bufferCapacity = 1;
     bufferContainer->bufferCount = 1;
-    bufferContainer->bufferHandles = SDL_malloc(
-        bufferContainer->bufferCapacity * sizeof(VulkanBufferHandle *));
-    bufferContainer->bufferHandles[0] = bufferContainer->activeBufferHandle;
+    bufferContainer->buffers = SDL_malloc(
+        bufferContainer->bufferCapacity * sizeof(VulkanBuffer *));
+    bufferContainer->buffers[0] = bufferContainer->activeBuffer;
     bufferContainer->debugName = NULL;
 
     return bufferContainer;
@@ -4180,7 +4052,7 @@ static VulkanTextureSubresource *VULKAN_INTERNAL_FetchTextureSubresource(
         layer,
         textureContainer->header.info.num_levels);
 
-    return &textureContainer->activeTextureHandle->vulkanTexture->subresources[index];
+    return &textureContainer->activeTexture->subresources[index];
 }
 
 static void VULKAN_INTERNAL_CreateRenderTargetView(
@@ -4188,6 +4060,7 @@ static void VULKAN_INTERNAL_CreateRenderTargetView(
     VulkanTexture *texture,
     Uint32 layerOrDepth,
     Uint32 level,
+    VkFormat format,
     VkComponentMapping swizzle,
     VkImageView *pView)
 {
@@ -4199,7 +4072,7 @@ static void VULKAN_INTERNAL_CreateRenderTargetView(
     imageViewCreateInfo.pNext = NULL;
     imageViewCreateInfo.flags = 0;
     imageViewCreateInfo.image = texture->image;
-    imageViewCreateInfo.format = texture->format;
+    imageViewCreateInfo.format = format;
     imageViewCreateInfo.components = swizzle;
     imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags;
     imageViewCreateInfo.subresourceRange.baseMipLevel = level;
@@ -4226,6 +4099,7 @@ static void VULKAN_INTERNAL_CreateRenderTargetView(
 
 static void VULKAN_INTERNAL_CreateSubresourceView(
     VulkanRenderer *renderer,
+    const SDL_GPUTextureCreateInfo *createinfo,
     VulkanTexture *texture,
     Uint32 layer,
     Uint32 level,
@@ -4240,14 +4114,14 @@ static void VULKAN_INTERNAL_CreateSubresourceView(
     imageViewCreateInfo.pNext = NULL;
     imageViewCreateInfo.flags = 0;
     imageViewCreateInfo.image = texture->image;
-    imageViewCreateInfo.format = texture->format;
+    imageViewCreateInfo.format = SDLToVK_TextureFormat[createinfo->format];
     imageViewCreateInfo.components = swizzle;
     imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags;
     imageViewCreateInfo.subresourceRange.baseMipLevel = level;
     imageViewCreateInfo.subresourceRange.levelCount = 1;
     imageViewCreateInfo.subresourceRange.baseArrayLayer = layer;
     imageViewCreateInfo.subresourceRange.layerCount = 1;
-    imageViewCreateInfo.viewType = texture->depth > 1 ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D;
+    imageViewCreateInfo.viewType = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? VK_IMAGE_VIEW_TYPE_3D : VK_IMAGE_VIEW_TYPE_2D;
 
     vulkanResult = renderer->vkCreateImageView(
         renderer->logicalDevice,
@@ -4678,47 +4552,35 @@ static bool VULKAN_INTERNAL_CreateSwapchain(
         swapchainData->textureContainers[i].header.info.sample_count = SDL_GPU_SAMPLECOUNT_1;
         swapchainData->textureContainers[i].header.info.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
 
-        swapchainData->textureContainers[i].activeTextureHandle = SDL_malloc(sizeof(VulkanTextureHandle));
-
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture = SDL_malloc(sizeof(VulkanTexture));
-
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->image = swapchainImages[i];
+        swapchainData->textureContainers[i].activeTexture = SDL_malloc(sizeof(VulkanTexture));
+        swapchainData->textureContainers[i].activeTexture->image = swapchainImages[i];
 
         // Swapchain memory is managed by the driver
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->usedRegion = NULL;
-
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->dimensions.width = drawableWidth;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->dimensions.height = drawableHeight;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->format = swapchainData->format;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->swizzle = swapchainData->swapchainSwizzle;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->type = SDL_GPU_TEXTURETYPE_2D;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->depth = 1;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->layerCount = 1;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->numLevels = 1;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->sampleCount = VK_SAMPLE_COUNT_1_BIT;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->usageFlags =
-            SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
-        SDL_AtomicSet(&swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->referenceCount, 0);
-
-        swapchainData->textureContainers[i].activeTextureHandle->container = NULL;
+        swapchainData->textureContainers[i].activeTexture->usedRegion = NULL;
+
+        swapchainData->textureContainers[i].activeTexture->swizzle = swapchainData->swapchainSwizzle;
+        swapchainData->textureContainers[i].activeTexture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
+        swapchainData->textureContainers[i].activeTexture->depth = 1;
+        swapchainData->textureContainers[i].activeTexture->usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
+        swapchainData->textureContainers[i].activeTexture->container = &swapchainData->textureContainers[i];
+        SDL_AtomicSet(&swapchainData->textureContainers[i].activeTexture->referenceCount, 0);
 
         // Create slice
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresourceCount = 1;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources = SDL_malloc(sizeof(VulkanTextureSubresource));
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].parent = swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].layer = 0;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].level = 0;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].transitioned = true;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].msaaTexHandle = NULL;
-        swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews = SDL_malloc(sizeof(VkImageView));
+        swapchainData->textureContainers[i].activeTexture->subresourceCount = 1;
+        swapchainData->textureContainers[i].activeTexture->subresources = SDL_malloc(sizeof(VulkanTextureSubresource));
+        swapchainData->textureContainers[i].activeTexture->subresources[0].parent = swapchainData->textureContainers[i].activeTexture;
+        swapchainData->textureContainers[i].activeTexture->subresources[0].layer = 0;
+        swapchainData->textureContainers[i].activeTexture->subresources[0].level = 0;
+        swapchainData->textureContainers[i].activeTexture->subresources[0].transitioned = true;
+        swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews = SDL_malloc(sizeof(VkImageView));
         VULKAN_INTERNAL_CreateRenderTargetView(
             renderer,
-            swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture,
+            swapchainData->textureContainers[i].activeTexture,
             0,
             0,
+            swapchainData->format,
             swapchainData->swapchainSwizzle,
-            &swapchainData->textureContainers[i].activeTextureHandle->vulkanTexture->subresources[0].renderTargetViews[0]);
+            &swapchainData->textureContainers[i].activeTexture->subresources[0].renderTargetViews[0]);
     }
 
     SDL_stack_free(swapchainImages);
@@ -4807,8 +4669,7 @@ static void VULKAN_DestroyDevice(
     for (Uint32 i = 0; i < renderer->uniformBufferPoolCount; i += 1) {
         VULKAN_INTERNAL_DestroyBuffer(
             renderer,
-            renderer->uniformBufferPool[i]->bufferHandle->vulkanBuffer);
-        SDL_free(renderer->uniformBufferPool[i]->bufferHandle);
+            renderer->uniformBufferPool[i]->buffer);
         SDL_free(renderer->uniformBufferPool[i]);
     }
     SDL_free(renderer->uniformBufferPool);
@@ -5093,7 +4954,7 @@ static void VULKAN_INTERNAL_BindGraphicsDescriptorSets(
             currentWriteDescriptorSet->pTexelBufferView = NULL;
             currentWriteDescriptorSet->pImageInfo = NULL;
 
-            bufferInfos[bufferInfoCount].buffer = commandBuffer->vertexUniformBuffers[i]->bufferHandle->vulkanBuffer->buffer;
+            bufferInfos[bufferInfoCount].buffer = commandBuffer->vertexUniformBuffers[i]->buffer->buffer;
             bufferInfos[bufferInfoCount].offset = 0;
             bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
 
@@ -5263,7 +5124,7 @@ static void VULKAN_INTERNAL_BindGraphicsDescriptorSets(
             currentWriteDescriptorSet->pTexelBufferView = NULL;
             currentWriteDescriptorSet->pImageInfo = NULL;
 
-            bufferInfos[bufferInfoCount].buffer = commandBuffer->fragmentUniformBuffers[i]->bufferHandle->vulkanBuffer->buffer;
+            bufferInfos[bufferInfoCount].buffer = commandBuffer->fragmentUniformBuffers[i]->buffer->buffer;
             bufferInfos[bufferInfoCount].offset = 0;
             bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
 
@@ -5356,7 +5217,7 @@ static void VULKAN_DrawPrimitivesIndirect(
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
     VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
-    VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBufferHandle->vulkanBuffer;
+    VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBuffer;
     Uint32 pitch = sizeof(SDL_GPUIndirectDrawCommand);
     Uint32 i;
 
@@ -5393,7 +5254,7 @@ static void VULKAN_DrawIndexedPrimitivesIndirect(
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
     VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
-    VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBufferHandle->vulkanBuffer;
+    VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBuffer;
     Uint32 pitch = sizeof(SDL_GPUIndexedIndirectDrawCommand);
     Uint32 i;
 
@@ -5466,7 +5327,7 @@ static void VULKAN_SetBufferName(
         for (Uint32 i = 0; i < container->bufferCount; i += 1) {
             VULKAN_INTERNAL_SetBufferName(
                 renderer,
-                container->bufferHandles[i]->vulkanBuffer,
+                container->buffers[i],
                 text);
         }
     }
@@ -5514,7 +5375,7 @@ static void VULKAN_SetTextureName(
         for (Uint32 i = 0; i < container->textureCount; i += 1) {
             VULKAN_INTERNAL_SetTextureName(
                 renderer,
-                container->textureHandles[i]->vulkanTexture,
+                container->textures[i],
                 text);
         }
     }
@@ -5569,99 +5430,54 @@ static void VULKAN_PopDebugGroup(
     }
 }
 
-static VulkanTextureHandle *VULKAN_INTERNAL_CreateTextureHandle(
-    VulkanRenderer *renderer,
-    Uint32 width,
-    Uint32 height,
-    Uint32 depth,
-    SDL_GPUTextureType type,
-    Uint32 layerCount,
-    Uint32 numLevels,
-    VkSampleCountFlagBits sampleCount,
-    VkFormat format,
-    VkComponentMapping swizzle,
-    VkImageAspectFlags aspectMask,
-    SDL_GPUTextureUsageFlags textureUsageFlags,
-    bool isMSAAColorTarget)
-{
-    VulkanTextureHandle *textureHandle;
-    VulkanTexture *texture;
-
-    texture = VULKAN_INTERNAL_CreateTexture(
-        renderer,
-        width,
-        height,
-        depth,
-        type,
-        layerCount,
-        numLevels,
-        sampleCount,
-        format,
-        swizzle,
-        aspectMask,
-        textureUsageFlags,
-        isMSAAColorTarget);
-
-    if (texture == NULL) {
-        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create texture!");
-        return NULL;
-    }
-
-    textureHandle = SDL_malloc(sizeof(VulkanTextureHandle));
-    textureHandle->vulkanTexture = texture;
-    textureHandle->container = NULL;
-
-    texture->handle = textureHandle;
-
-    return textureHandle;
-}
-
 static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
     VulkanRenderer *renderer,
-    Uint32 width,
-    Uint32 height,
-    Uint32 depth,
-    SDL_GPUTextureType type,
-    Uint32 layerCount,
-    Uint32 numLevels,
-    VkSampleCountFlagBits sampleCount,
-    VkFormat format,
-    VkComponentMapping swizzle,
-    VkImageAspectFlags aspectMask,
-    SDL_GPUTextureUsageFlags textureUsageFlags,
-    bool isMSAAColorTarget)
+    const SDL_GPUTextureCreateInfo *createinfo)
 {
     VkResult vulkanResult;
     VkImageCreateInfo imageCreateInfo;
     VkImageCreateFlags imageCreateFlags = 0;
     VkImageViewCreateInfo imageViewCreateInfo;
     Uint8 bindResult;
-    Uint8 isRenderTarget =
-        ((textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) != 0) ||
-        ((textureUsageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) != 0);
     VkImageUsageFlags vkUsageFlags = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    Uint32 layerCount = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? 1 : createinfo->layer_count_or_depth;
+    Uint32 depth = (createinfo->type == SDL_GPU_TEXTURETYPE_3D) ? createinfo->layer_count_or_depth : 1;
+
+    // FIXME: We probably shouldn't alloc this until the actual image/memory creation have succeeded!
     VulkanTexture *texture = SDL_malloc(sizeof(VulkanTexture));
+    texture->markedForDestroy = false;
+    texture->swizzle = SwizzleForSDLFormat(createinfo->format);
+    texture->depth = depth;
+    texture->usage = createinfo->usage;
+    texture->fullView = VK_NULL_HANDLE;
+    SDL_AtomicSet(&texture->referenceCount, 0);
 
-    texture->type = type;
-    texture->isMSAAColorTarget = isMSAAColorTarget;
-    texture->markedForDestroy = 0;
+    if (IsDepthFormat(createinfo->format)) {
+        texture->aspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
 
-    if (type == SDL_GPU_TEXTURETYPE_CUBE || type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
+        if (IsStencilFormat(createinfo->format)) {
+            texture->aspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT;
+        }
+    } else {
+        texture->aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
+    }
+
+    if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE || createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
         imageCreateFlags |= VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
-    } else if (type == SDL_GPU_TEXTURETYPE_3D) {
+    } else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
         imageCreateFlags |= VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
     }
 
-    if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER) {
+    if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_SAMPLER) {
         vkUsageFlags |= VK_IMAGE_USAGE_SAMPLED_BIT;
     }
-    if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
+    if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
         vkUsageFlags |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
     }
-    if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
+    if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
         vkUsageFlags |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT;
     }
-    if (textureUsageFlags & (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ |
+    if (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ |
                              SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ |
                              SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE)) {
         vkUsageFlags |= VK_IMAGE_USAGE_STORAGE_BIT;
@@ -5670,14 +5486,14 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
     imageCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
     imageCreateInfo.pNext = NULL;
     imageCreateInfo.flags = imageCreateFlags;
-    imageCreateInfo.imageType = type == SDL_GPU_TEXTURETYPE_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
-    imageCreateInfo.format = format;
-    imageCreateInfo.extent.width = width;
-    imageCreateInfo.extent.height = height;
+    imageCreateInfo.imageType = createinfo->type == SDL_GPU_TEXTURETYPE_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D;
+    imageCreateInfo.format = SDLToVK_TextureFormat[createinfo->format];
+    imageCreateInfo.extent.width = createinfo->width;
+    imageCreateInfo.extent.height = createinfo->height;
     imageCreateInfo.extent.depth = depth;
-    imageCreateInfo.mipLevels = numLevels;
+    imageCreateInfo.mipLevels = createinfo->num_levels;
     imageCreateInfo.arrayLayers = layerCount;
-    imageCreateInfo.samples = isMSAAColorTarget || VULKAN_INTERNAL_IsVulkanDepthFormat(format) ? sampleCount : VK_SAMPLE_COUNT_1_BIT;
+    imageCreateInfo.samples = SDLToVK_SampleCount[createinfo->sample_count];
     imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
     imageCreateInfo.usage = vkUsageFlags;
     imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
@@ -5709,32 +5525,27 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
 
     texture->usedRegion->vulkanTexture = texture; // lol
 
-    texture->fullView = VK_NULL_HANDLE;
-
-    if (
-        (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_SAMPLER) ||
-        (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ) ||
-        (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) {
+    if (createinfo->usage & (SDL_GPU_TEXTUREUSAGE_SAMPLER | SDL_GPU_TEXTUREUSAGE_GRAPHICS_STORAGE_READ | SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_READ)) {
 
         imageViewCreateInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
         imageViewCreateInfo.pNext = NULL;
         imageViewCreateInfo.flags = 0;
         imageViewCreateInfo.image = texture->image;
-        imageViewCreateInfo.format = format;
-        imageViewCreateInfo.components = swizzle;
-        imageViewCreateInfo.subresourceRange.aspectMask = aspectMask;
+        imageViewCreateInfo.format = SDLToVK_TextureFormat[createinfo->format];
+        imageViewCreateInfo.components = texture->swizzle;
+        imageViewCreateInfo.subresourceRange.aspectMask = texture->aspectFlags;
         imageViewCreateInfo.subresourceRange.baseMipLevel = 0;
-        imageViewCreateInfo.subresourceRange.levelCount = numLevels;
+        imageViewCreateInfo.subresourceRange.levelCount = createinfo->num_levels;
         imageViewCreateInfo.subresourceRange.baseArrayLayer = 0;
         imageViewCreateInfo.subresourceRange.layerCount = layerCount;
 
-        if (type == SDL_GPU_TEXTURETYPE_CUBE) {
+        if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE) {
             imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
-        } else if (type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY){
+        } else if (createinfo->type == SDL_GPU_TEXTURETYPE_CUBE_ARRAY) {
             imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE_ARRAY;
-        } else if (type == SDL_GPU_TEXTURETYPE_3D) {
+        } else if (createinfo->type == SDL_GPU_TEXTURETYPE_3D) {
             imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_3D;
-        } else if (type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
+        } else if (createinfo->type == SDL_GPU_TEXTURETYPE_2D_ARRAY) {
             imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY;
         } else {
             imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
@@ -5753,49 +5564,35 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
         }
     }
 
-    texture->dimensions.width = width;
-    texture->dimensions.height = height;
-    texture->depth = depth;
-    texture->format = format;
-    texture->swizzle = swizzle;
-    texture->numLevels = numLevels;
-    texture->layerCount = layerCount;
-    texture->sampleCount = sampleCount;
-    texture->usageFlags = textureUsageFlags;
-    texture->aspectFlags = aspectMask;
-    SDL_AtomicSet(&texture->referenceCount, 0);
-
     // Define slices
-    texture->subresourceCount =
-        texture->layerCount *
-        texture->numLevels;
-
+    texture->subresourceCount = layerCount * createinfo->num_levels;
     texture->subresources = SDL_malloc(
         texture->subresourceCount * sizeof(VulkanTextureSubresource));
 
-    for (Uint32 i = 0; i < texture->layerCount; i += 1) {
-        for (Uint32 j = 0; j < texture->numLevels; j += 1) {
+    for (Uint32 i = 0; i < layerCount; i += 1) {
+        for (Uint32 j = 0; j < createinfo->num_levels; j += 1) {
             Uint32 subresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
                 j,
                 i,
-                texture->numLevels);
+                createinfo->num_levels);
 
             texture->subresources[subresourceIndex].renderTargetViews = NULL;
             texture->subresources[subresourceIndex].computeWriteView = VK_NULL_HANDLE;
             texture->subresources[subresourceIndex].depthStencilView = VK_NULL_HANDLE;
 
-            if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
+            if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET) {
                 texture->subresources[subresourceIndex].renderTargetViews = SDL_malloc(
-                    texture->depth * sizeof(VkImageView));
+                    depth * sizeof(VkImageView));
 
-                if (texture->depth > 1) {
-                    for (Uint32 k = 0; k < texture->depth; k += 1) {
+                if (depth > 1) {
+                    for (Uint32 k = 0; k < depth; k += 1) {
                         VULKAN_INTERNAL_CreateRenderTargetView(
                             renderer,
                             texture,
                             k,
                             j,
-                            swizzle,
+                            SDLToVK_TextureFormat[createinfo->format],
+                            texture->swizzle,
                             &texture->subresources[subresourceIndex].renderTargetViews[k]);
                     }
                 } else {
@@ -5804,57 +5601,38 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
                         texture,
                         i,
                         j,
-                        swizzle,
+                        SDLToVK_TextureFormat[createinfo->format],
+                        texture->swizzle,
                         &texture->subresources[subresourceIndex].renderTargetViews[0]);
                 }
             }
 
-            if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
+            if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE) {
                 VULKAN_INTERNAL_CreateSubresourceView(
                     renderer,
+                    createinfo,
                     texture,
                     i,
                     j,
-                    swizzle,
+                    texture->swizzle,
                     &texture->subresources[subresourceIndex].computeWriteView);
             }
 
-            if (textureUsageFlags & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
+            if (createinfo->usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET) {
                 VULKAN_INTERNAL_CreateSubresourceView(
                     renderer,
+                    createinfo,
                     texture,
                     i,
                     j,
-                    swizzle,
+                    texture->swizzle,
                     &texture->subresources[subresourceIndex].depthStencilView);
             }
 
             texture->subresources[subresourceIndex].parent = texture;
             texture->subresources[subresourceIndex].layer = i;
             texture->subresources[subresourceIndex].level = j;
-            texture->subresources[subresourceIndex].msaaTexHandle = NULL;
             texture->subresources[subresourceIndex].transitioned = false;
-
-            if (
-                sampleCount > VK_SAMPLE_COUNT_1_BIT &&
-                isRenderTarget &&
-                !isMSAAColorTarget &&
-                !VULKAN_INTERNAL_IsVulkanDepthFormat(texture->format)) {
-                texture->subresources[subresourceIndex].msaaTexHandle = VULKAN_INTERNAL_CreateTextureHandle(
-                    renderer,
-                    texture->dimensions.width >> j,
-                    texture->dimensions.height >> j,
-                    1,
-                    0,
-                    1,
-                    1,
-                    sampleCount,
-                    texture->format,
-                    texture->swizzle,
-                    aspectMask,
-                    SDL_GPU_TEXTUREUSAGE_COLOR_TARGET,
-                    true);
-            }
         }
     }
 
@@ -5863,100 +5641,98 @@ static VulkanTexture *VULKAN_INTERNAL_CreateTexture(
 
 static void VULKAN_INTERNAL_CycleActiveBuffer(
     VulkanRenderer *renderer,
-    VulkanBufferContainer *bufferContainer)
+    VulkanBufferContainer *container)
 {
-    VulkanBufferHandle *bufferHandle;
-    Uint32 i;
+    VulkanBuffer *buffer;
 
     // If a previously-cycled buffer is available, we can use that.
-    for (i = 0; i < bufferContainer->bufferCount; i += 1) {
-        bufferHandle = bufferContainer->bufferHandles[i];
-        if (SDL_AtomicGet(&bufferHandle->vulkanBuffer->referenceCount) == 0) {
-            bufferContainer->activeBufferHandle = bufferHandle;
+    for (Uint32 i = 0; i < container->bufferCount; i += 1) {
+        buffer = container->buffers[i];
+        if (SDL_AtomicGet(&buffer->referenceCount) == 0) {
+            container->activeBuffer = buffer;
             return;
         }
     }
 
-    // No buffer handle is available, generate a new one.
-    bufferContainer->activeBufferHandle = VULKAN_INTERNAL_CreateBufferHandle(
+    // No buffer handle is available, create a new one.
+    buffer = VULKAN_INTERNAL_CreateBuffer(
         renderer,
-        bufferContainer->activeBufferHandle->vulkanBuffer->size,
-        bufferContainer->activeBufferHandle->vulkanBuffer->usageFlags,
-        bufferContainer->activeBufferHandle->vulkanBuffer->type);
+        container->activeBuffer->size,
+        container->activeBuffer->usage,
+        container->activeBuffer->type);
 
-    bufferContainer->activeBufferHandle->container = bufferContainer;
+    if (!buffer) {
+        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to cycle active buffer!");
+        return;
+    }
 
     EXPAND_ARRAY_IF_NEEDED(
-        bufferContainer->bufferHandles,
-        VulkanBufferHandle *,
-        bufferContainer->bufferCount + 1,
-        bufferContainer->bufferCapacity,
-        bufferContainer->bufferCapacity * 2);
+        container->buffers,
+        VulkanBuffer *,
+        container->bufferCount + 1,
+        container->bufferCapacity,
+        container->bufferCapacity * 2);
 
-    bufferContainer->bufferHandles[bufferContainer->bufferCount] = bufferContainer->activeBufferHandle;
-    bufferContainer->bufferCount += 1;
+    container->buffers[container->bufferCount] = buffer;
+    buffer->container = container;
+    buffer->containerIndex = container->bufferCount;
+    container->bufferCount += 1;
 
-    if (
-        renderer->debugMode &&
-        renderer->supportsDebugUtils &&
-        bufferContainer->debugName != NULL) {
+    container->activeBuffer = buffer;
+
+    if (renderer->debugMode && renderer->supportsDebugUtils && container->debugName != NULL) {
         VULKAN_INTERNAL_SetBufferName(
             renderer,
-            bufferContainer->activeBufferHandle->vulkanBuffer,
-            bufferContainer->debugName);
+            container->activeBuffer,
+            container->debugName);
     }
 }
 
 static void VULKAN_INTERNAL_CycleActiveTexture(
     VulkanRenderer *renderer,
-    VulkanTextureContainer *textureContainer)
+    VulkanTextureContainer *container)
 {
+    VulkanTexture *texture;
+
     // If a previously-cycled texture is available, we can use that.
-    for (Uint32 i = 0; i < textureContainer->textureCount; i += 1) {
-        VulkanTextureHandle *textureHandle = textureContainer->textureHandles[i];
+    for (Uint32 i = 0; i < container->textureCount; i += 1) {
+        texture = container->textures[i];
 
-        if (SDL_AtomicGet(&textureHandle->vulkanTexture->referenceCount) == 0) {
-            textureContainer->activeTextureHandle = textureHandle;
+        if (SDL_AtomicGet(&texture->referenceCount) == 0) {
+            container->activeTexture = texture;
             return;
         }
     }
 
-    // No texture handle is available, generate a new one.
-    textureContainer->activeTextureHandle = VULKAN_INTERNAL_CreateTextureHandle(
+    // No texture is available, generate a new one.
+    texture = VULKAN_INTERNAL_CreateTexture(
         renderer,
-        textureContainer->activeTextureHandle->vulkanTexture->dimensions.width,
-        textureContainer->activeTextureHandle->vulkanTexture->dimensions.height,
-        textureContainer->activeTextureHandle->vulkanTexture->depth,
-        textureContainer->activeTextureHandle->vulkanTexture->type,
-        textureContainer->activeTextureHandle->vulkanTexture->layerCount,
-        textureContainer->activeTextureHandle->vulkanTexture->numLevels,
-        textureContainer->activeTextureHandle->vulkanTexture->sampleCount,
-        textureContainer->activeTextureHandle->vulkanTexture->format,
-        textureContainer->activeTextureHandle->vulkanTexture->swizzle,
-        textureContainer->activeTextureHandle->vulkanTexture->aspectFlags,
-        textureContainer->activeTextureHandle->vulkanTexture->usageFlags,
-        false);
+        &container->header.info);
 
-    textureContainer->activeTextureHandle->container = textureContainer;
+    if (!texture) {
+        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to cycle active texture!");
+        return;
+    }
 
     EXPAND_ARRAY_IF_NEEDED(
-        textureContainer->textureHandles,
-        VulkanTextureHandle *,
-        textureContainer->textureCount + 1,
-        textureContainer->textureCapacity,
-        textureContainer->textureCapacity * 2);
+        container->textures,
+        VulkanTexture *,
+        container->textureCount + 1,
+        container->textureCapacity,
+        container->textureCapacity * 2);
 
-    textureContainer->textureHandles[textureContainer->textureCount] = textureContainer->activeTextureHandle;
-    textureContainer->textureCount += 1;
+    container->textures[container->textureCount] = texture;
+    texture->container = container;
+    texture->containerIndex = container->textureCount;
+    container->textureCount += 1;
 
-    if (
-        renderer->debugMode &&
-        renderer->supportsDebugUtils &&
-        textureContainer->debugName != NULL) {
+    container->activeTexture = texture;
+
+    if (renderer->debugMode && renderer->supportsDebugUtils && container->debugName != NULL) {
         VULKAN_INTERNAL_SetTextureName(
             renderer,
-            textureContainer->activeTextureHandle->vulkanTexture,
-            textureContainer->debugName);
+            container->activeTexture,
+            container->debugName);
     }
 }
 
@@ -5969,7 +5745,7 @@ static VulkanBuffer *VULKAN_INTERNAL_PrepareBufferForWrite(
 {
     if (
         cycle &&
-        SDL_AtomicGet(&bufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0) {
+        SDL_AtomicGet(&bufferContainer->activeBuffer->referenceCount) > 0) {
         VULKAN_INTERNAL_CycleActiveBuffer(
             renderer,
             bufferContainer);
@@ -5979,9 +5755,9 @@ static VulkanBuffer *VULKAN_INTERNAL_PrepareBufferForWrite(
         renderer,
         commandBuffer,
         destinationUsageMode,
-        bufferContainer->activeBufferHandle->vulkanBuffer);
+        bufferContainer->activeBuffer);
 
-    return bufferContainer->activeBufferHandle->vulkanBuffer;
+    return bufferContainer->activeBuffer;
 }
 
 static VulkanTextureSubresource *VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
@@ -6001,7 +5777,7 @@ static VulkanTextureSubresource *VULKAN_INTERNAL_PrepareTextureSubresourceForWri
     if (
         cycle &&
         textureContainer->canBeCycled &&
-        SDL_AtomicGet(&textureContainer->activeTextureHandle->vulkanTexture->referenceCount) > 0) {
+        SDL_AtomicGet(&textureContainer->activeTexture->referenceCount) > 0) {
         VULKAN_INTERNAL_CycleActiveTexture(
             renderer,
             textureContainer);
@@ -6030,9 +5806,9 @@ static VkRenderPass VULKAN_INTERNAL_CreateRenderPass(
     const SDL_GPUDepthStencilTargetInfo *depthStencilTargetInfo)
 {
     VkResult vulkanResult;
-    VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1];
+    VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1 /* depth */];
     VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS];
-    VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS + 1];
+    VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS];
     VkAttachmentReference depthStencilAttachmentReference;
     VkRenderPassCreateInfo renderPassCreateInfo;
     VkSubpassDescription subpass;
@@ -6043,82 +5819,42 @@ static VkRenderPass VULKAN_INTERNAL_CreateRenderPass(
     Uint32 colorAttachmentReferenceCount = 0;
     Uint32 resolveReferenceCount = 0;
 
-    VulkanTexture *texture = NULL;
-
     for (i = 0; i < numColorTargets; i += 1) {
-        texture = ((VulkanTextureContainer *)colorTargetInfos[i].texture)->activeTextureHandle->vulkanTexture;
+        VulkanTextureContainer *container = (VulkanTextureContainer *)colorTargetInfos[i].texture;
+        attachmentDescriptions[attachmentDescriptionCount].flags = 0;
+        attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[container->header.info.format];
+        attachmentDescriptions[attachmentDescriptionCount].samples = SDLToVK_SampleCount[container->header.info.sample_count];
+        attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[colorTargetInfos[i].load_op];
+        attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[colorTargetInfos[i].store_op];
+        attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+        attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+        attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
-        if (texture->sampleCount > VK_SAMPLE_COUNT_1_BIT) {
-            // Resolve attachment and multisample attachment
+        colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount;
+        colorAttachmentReferences[colorAttachmentReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
-            attachmentDescriptions[attachmentDescriptionCount].flags = 0;
-            attachmentDescriptions[attachmentDescriptionCount].format = texture->format;
-            attachmentDescriptions[attachmentDescriptionCount].samples =
-                VK_SAMPLE_COUNT_1_BIT;
-            attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[colorTargetInfos[i].load_op];
-            attachmentDescriptions[attachmentDescriptionCount].storeOp =
-                VK_ATTACHMENT_STORE_OP_STORE; // Always store the resolve texture
-            attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp =
-                VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp =
-                VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].initialLayout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-            attachmentDescriptions[attachmentDescriptionCount].finalLayout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-            resolveReferences[resolveReferenceCount].attachment =
-                attachmentDescriptionCount;
-            resolveReferences[resolveReferenceCount].layout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+        attachmentDescriptionCount += 1;
+        colorAttachmentReferenceCount += 1;
 
-            attachmentDescriptionCount += 1;
-            resolveReferenceCount += 1;
+        if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+            VulkanTextureContainer *resolveContainer = (VulkanTextureContainer *)colorTargetInfos[i].resolve_texture;
 
             attachmentDescriptions[attachmentDescriptionCount].flags = 0;
-            attachmentDescriptions[attachmentDescriptionCount].format = texture->format;
-            attachmentDescriptions[attachmentDescriptionCount].samples = texture->sampleCount;
-            attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[colorTargetInfos[i].load_op];
-            attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[colorTargetInfos[i].store_op];
-            attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp =
-                VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp =
-                VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].initialLayout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-            attachmentDescriptions[attachmentDescriptionCount].finalLayout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-            colorAttachmentReferences[colorAttachmentReferenceCount].attachment =
-                attachmentDescriptionCount;
-            colorAttachmentReferences[colorAttachmentReferenceCount].layout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+            attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[resolveContainer->header.info.format];
+            attachmentDescriptions[attachmentDescriptionCount].samples = SDLToVK_SampleCount[resolveContainer->header.info.sample_count];
+            attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; // The texture will be overwritten anyway
+            attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_STORE; // Always store the resolve texture
+            attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+            attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+            attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+            attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
-            attachmentDescriptionCount += 1;
-            colorAttachmentReferenceCount += 1;
-        } else {
-            attachmentDescriptions[attachmentDescriptionCount].flags = 0;
-            attachmentDescriptions[attachmentDescriptionCount].format = texture->format;
-            attachmentDescriptions[attachmentDescriptionCount].samples =
-                VK_SAMPLE_COUNT_1_BIT;
-            attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[colorTargetInfos[i].load_op];
-            attachmentDescriptions[attachmentDescriptionCount].storeOp =
-                VK_ATTACHMENT_STORE_OP_STORE; // Always store non-MSAA textures
-            attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp =
-                VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp =
-                VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].initialLayout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-            attachmentDescriptions[attachmentDescriptionCount].finalLayout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-            colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount;
-            colorAttachmentReferences[colorAttachmentReferenceCount].layout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+            resolveReferences[resolveReferenceCount].attachment = attachmentDescriptionCount;
+            resolveReferences[resolveReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
             attachmentDescriptionCount += 1;
-            colorAttachmentReferenceCount += 1;
+            resolveReferenceCount += 1;
         }
     }
 
@@ -6134,33 +5870,27 @@ static VkRenderPass VULKAN_INTERNAL_CreateRenderPass(
     if (depthStencilTargetInfo == NULL) {
         subpass.pDepthStencilAttachment = NULL;
     } else {
-        texture = ((VulkanTextureContainer *)depthStencilTargetInfo->texture)->activeTextureHandle->vulkanTexture;
+        VulkanTextureContainer *container = (VulkanTextureContainer *)depthStencilTargetInfo->texture;
 
         attachmentDescriptions[attachmentDescriptionCount].flags = 0;
-        attachmentDescriptions[attachmentDescriptionCount].format = texture->format;
-        attachmentDescriptions[attachmentDescriptionCount].samples = texture->sampleCount;
-
+        attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[container->header.info.format];
+        attachmentDescriptions[attachmentDescriptionCount].samples = SDLToVK_SampleCount[container->header.info.sample_count];
         attachmentDescriptions[attachmentDescriptionCount].loadOp = SDLToVK_LoadOp[depthStencilTargetInfo->load_op];
         attachmentDescriptions[attachmentDescriptionCount].storeOp = SDLToVK_StoreOp[depthStencilTargetInfo->store_op];
         attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = SDLToVK_LoadOp[depthStencilTargetInfo->stencil_load_op];
         attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = SDLToVK_StoreOp[depthStencilTargetInfo->stencil_store_op];
-        attachmentDescriptions[attachmentDescriptionCount].initialLayout =
-            VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
-        attachmentDescriptions[attachmentDescriptionCount].finalLayout =
-            VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+        attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+        attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 
-        depthStencilAttachmentReference.attachment =
-            attachmentDescriptionCount;
-        depthStencilAttachmentReference.layout =
-            VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+        depthStencilAttachmentReference.attachment = attachmentDescriptionCount;
+        depthStencilAttachmentReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 
-        subpass.pDepthStencilAttachment =
-            &depthStencilAttachmentReference;
+        subpass.pDepthStencilAttachment = &depthStencilAttachmentReference;
 
         attachmentDescriptionCount += 1;
     }
 
-    if (texture != NULL && texture->sampleCount > VK_SAMPLE_COUNT_1_BIT) {
+    if (resolveReferenceCount > 0) {
         subpass.pResolveAttachments = resolveReferences;
     } else {
         subpass.pResolveAttachments = NULL;
@@ -6192,12 +5922,11 @@ static VkRenderPass VULKAN_INTERNAL_CreateRenderPass(
 
 static VkRenderPass VULKAN_INTERNAL_CreateTransientRenderPass(
     VulkanRenderer *renderer,
-    SDL_GPUGraphicsPipelineTargetInfo attachmentInfo,
+    SDL_GPUGraphicsPipelineTargetInfo targetInfo,
     VkSampleCountFlagBits sampleCount)
 {
-    VkAttachmentDescription attachmentDescriptions[2 * MAX_COLOR_TARGET_BINDINGS + 1];
+    VkAttachmentDescription attachmentDescriptions[MAX_COLOR_TARGET_BINDINGS + 1 /* depth */];
     VkAttachmentReference colorAttachmentReferences[MAX_COLOR_TARGET_BINDINGS];
-    VkAttachmentReference resolveReferences[MAX_COLOR_TARGET_BINDINGS + 1];
     VkAttachmentReference depthStencilAttachmentReference;
     SDL_GPUColorTargetDescription attachmentDescription;
     VkSubpassDescription subpass;
@@ -6205,123 +5934,62 @@ static VkRenderPass VULKAN_INTERNAL_CreateTransientRenderPass(
     VkRenderPass renderPass;
     VkResult result;
 
-    Uint32 multisampling = 0;
     Uint32 attachmentDescriptionCount = 0;
     Uint32 colorAttachmentReferenceCount = 0;
-    Uint32 resolveReferenceCount = 0;
     Uint32 i;
 
-    for (i = 0; i < attachmentInfo.num_color_targets; i += 1) {
-        attachmentDescription = attachmentInfo.color_target_descriptions[i];
+    for (i = 0; i < targetInfo.num_color_targets; i += 1) {
+        attachmentDescription = targetInfo.color_target_descriptions[i];
 
-        if (sampleCount > VK_SAMPLE_COUNT_1_BIT) {
-            multisampling = 1;
-
-            // Resolve attachment and multisample attachment
-
-            attachmentDescriptions[attachmentDescriptionCount].flags = 0;
-            attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[attachmentDescription.format];
-            attachmentDescriptions[attachmentDescriptionCount].samples = VK_SAMPLE_COUNT_1_BIT;
-            attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-            attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-            resolveReferences[resolveReferenceCount].attachment = attachmentDescriptionCount;
-            resolveReferences[resolveReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-            attachmentDescriptionCount += 1;
-            resolveReferenceCount += 1;
-
-            attachmentDescriptions[attachmentDescriptionCount].flags = 0;
-            attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[attachmentDescription.format];
-            attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount;
-
-            attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-            attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-            colorAttachmentReferences[colorAttachmentReferenceCount].attachment =
-                attachmentDescriptionCount;
-            colorAttachmentReferences[colorAttachmentReferenceCount].layout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+        attachmentDescriptions[attachmentDescriptionCount].flags = 0;
+        attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[attachmentDescription.format];
+        attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount;
+        attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+        attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
+        attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
+        attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+        attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
-            attachmentDescriptionCount += 1;
-            colorAttachmentReferenceCount += 1;
-        } else {
-            attachmentDescriptions[attachmentDescriptionCount].flags = 0;
-            attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_SurfaceFormat[attachmentDescription.format];
-            attachmentDescriptions[attachmentDescriptionCount].samples =
-                VK_SAMPLE_COUNT_1_BIT;
-            attachmentDescriptions[attachmentDescriptionCount].loadOp =
-                VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].storeOp =
-                VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp =
-                VK_ATTACHMENT_LOAD_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp =
-                VK_ATTACHMENT_STORE_OP_DONT_CARE;
-            attachmentDescriptions[attachmentDescriptionCount].initialLayout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-            attachmentDescriptions[attachmentDescriptionCount].finalLayout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
-
-            colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount;
-            colorAttachmentReferences[colorAttachmentReferenceCount].layout =
-                VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+        colorAttachmentReferences[colorAttachmentReferenceCount].attachment = attachmentDescriptionCount;
+        colorAttachmentReferences[colorAttachmentReferenceCount].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
 
-            attachmentDescriptionCount += 1;
-            colorAttachmentReferenceCount += 1;
-        }
+        attachmentDescriptionCount += 1;
+        colorAttachmentReferenceCount += 1;
     }
 
     subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
     subpass.flags = 0;
     subpass.inputAttachmentCount = 0;
     subpass.pInputAttachments = NULL;
-    subpass.colorAttachmentCount = attachmentInfo.num_color_targets;
+    subpass.colorAttachmentCount = targetInfo.num_color_targets;
     subpass.pColorAttachments = colorAttachmentReferences;
     subpass.preserveAttachmentCount = 0;
     subpass.pPreserveAttachments = NULL;
 
-    if (attachmentInfo.has_depth_stencil_target) {
+    if (targetInfo.has_depth_stencil_target) {
         attachmentDescriptions[attachmentDescriptionCount].flags = 0;
-        attachmentDescriptions[attachmentDescriptionCount].format =
-            SDLToVK_SurfaceFormat[attachmentInfo.depth_stencil_format];
+        attachmentDescriptions[attachmentDescriptionCount].format = SDLToVK_TextureFormat[targetInfo.depth_stencil_format];
         attachmentDescriptions[attachmentDescriptionCount].samples = sampleCount;
-
         attachmentDescriptions[attachmentDescriptionCount].loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
         attachmentDescriptions[attachmentDescriptionCount].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
         attachmentDescriptions[attachmentDescriptionCount].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
         attachmentDescriptions[attachmentDescriptionCount].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
-        attachmentDescriptions[attachmentDescriptionCount].initialLayout =
-            VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
-        attachmentDescriptions[attachmentDescriptionCount].finalLayout =
-            VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+        attachmentDescriptions[attachmentDescriptionCount].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+        attachmentDescriptions[attachmentDescriptionCount].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 
-        depthStencilAttachmentReference.attachment =
-            attachmentDescriptionCount;
-        depthStencilAttachmentReference.layout =
-            VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
+        depthStencilAttachmentReference.attachment = attachmentDescriptionCount;
+        depthStencilAttachmentReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
 
-        subpass.pDepthStencilAttachment =
-            &depthStencilAttachmentReference;
+        subpass.pDepthStencilAttachment = &depthStencilAttachmentReference;
 
         attachmentDescriptionCount += 1;
     } else {
         subpass.pDepthStencilAttachment = NULL;
     }
 
-    if (multisampling) {
-        subpass.pResolveAttachments = resolveReferences;
-    } else {
-        subpass.pResolveAttachments = NULL;
-    }
+    // Resolve attachments aren't needed for transient passes
+    subpass.pResolveAttachments = NULL;
 
     renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
     renderPassCreateInfo.pNext = NULL;
@@ -6353,7 +6021,6 @@ static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline(
 {
     VkResult vulkanResult;
     Uint32 i;
-    VkSampleCountFlagBits actualSampleCount;
 
     VulkanGraphicsPipeline *graphicsPipeline = (VulkanGraphicsPipeline *)SDL_malloc(sizeof(VulkanGraphicsPipeline));
     VkGraphicsPipelineCreateInfo vkPipelineCreateInfo;
@@ -6394,18 +6061,12 @@ static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline(
 
     VulkanRenderer *renderer = (VulkanRenderer *)driverData;
 
-    // Find a compatible sample count to use
-
-    actualSampleCount = VULKAN_INTERNAL_GetMaxMultiSampleCount(
-        renderer,
-        SDLToVK_SampleCount[createinfo->multisample_state.sample_count]);
-
     // Create a "compatible" render pass
 
     VkRenderPass transientRenderPass = VULKAN_INTERNAL_CreateTransientRenderPass(
         renderer,
         createinfo->target_info,
-        actualSampleCount);
+        SDLToVK_SampleCount[createinfo->multisample_state.sample_count]);
 
     // Dynamic state
 
@@ -6539,11 +6200,10 @@ static SDL_GPUGraphicsPipeline *VULKAN_CreateGraphicsPipeline(
     multisampleStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
     multisampleStateCreateInfo.pNext = NULL;
     multisampleStateCreateInfo.flags = 0;
-    multisampleStateCreateInfo.rasterizationSamples = actualSampleCount;
+    multisampleStateCreateInfo.rasterizationSamples = SDLToVK_SampleCount[createinfo->multisample_state.sample_count];
     multisampleStateCreateInfo.sampleShadingEnable = VK_FALSE;
     multisampleStateCreateInfo.minSampleShading = 1.0f;
-    multisampleStateCreateInfo.pSampleMask =
-        &sampleMask;
+    multisampleStateCreateInfo.pSampleMask = &sampleMask;
     multisampleStateCreateInfo.alphaToCoverageEnable = VK_FALSE;
     multisampleStateCreateInfo.alphaToOneEnable = VK_FALSE;
 
@@ -6896,58 +6556,31 @@ static SDL_GPUTexture *VULKAN_CreateTexture(
     const SDL_GPUTextureCreateInfo *createinfo)
 {
     VulkanRenderer *renderer = (VulkanRenderer *)driverData;
-    VkImageAspectFlags imageAspectFlags;
-    Uint8 isDepthFormat = IsDepthFormat(createinfo->format);
-    VkFormat format;
-    VkComponentMapping swizzle;
+    VulkanTexture *texture;
     VulkanTextureContainer *container;
-    VulkanTextureHandle *textureHandle;
-
-    format = SDLToVK_SurfaceFormat[createinfo->format];
-    swizzle = SwizzleForSDLFormat(createinfo->format);
-
-    if (isDepthFormat) {
-        imageAspectFlags = VK_IMAGE_ASPECT_DEPTH_BIT;
 
-        if (IsStencilFormat(createinfo->format)) {
-            imageAspectFlags |= VK_IMAGE_ASPECT_STENCIL_BIT;
-        }
-    } else {
-        imageAspectFlags = VK_IMAGE_ASPECT_COLOR_BIT;
-    }
-
-    textureHandle = VULKAN_INTERNAL_CreateTextureHandle(
+    texture = VULKAN_INTERNAL_CreateTexture(
         renderer,
-        createinfo->width,
-        createinfo->height,
-        createinfo->type == SDL_GPU_TEXTURETYPE_3D ? createinfo->layer_count_or_depth : 1,
-        createinfo->type,
-        createinfo->type == SDL_GPU_TEXTURETYPE_3D ? 1 : createinfo->layer_count_or_depth,
-        createinfo->num_levels,
-        SDLToVK_SampleCount[createinfo->sample_count],
-        format,
-        swizzle,
-        imageAspectFlags,
-        createinfo->usage,
-        false);
+        createinfo);
 
-    if (textureHandle == NULL) {
-        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create texture container!");
+    if (texture == NULL) {
+        SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create texture!");
         return NULL;
     }
 
     container = SDL_malloc(sizeof(VulkanTextureContainer));
     container->header.info = *createinfo;
-    container->canBeCycled = 1;
-    container->activeTextureHandle = textureHandle;
+    container->canBeCycled = true;
+    container->activeTexture = texture;
     container->textureCapacity = 1;
     container->textureCount = 1;
-    container->textureHandles = SDL_malloc(
-        container->textureCapacity * sizeof(VulkanTextureHandle *));
-    container->textureHandles[0] = container->activeTextureHandle;
+    container->textures = SDL_malloc(
+        container->textureCapacity * sizeof(VulkanTexture *));
+    container->textures[0] = container->activeTexture;
     container->debugName = NULL;
 
-    textureHandle->container = container;
+    texture->container = container;
+    texture->containerIndex = 0;
 
     return (SDL_GPUTexture *)container;
 }
@@ -6970,7 +6603,7 @@ static VulkanUniformBuffer *VULKAN_INTERNAL_CreateUniformBuffer(
 {
     VulkanUniformBuffer *uniformBuffer = SDL_malloc(sizeof(VulkanUniformBuffer));
 
-    uniformBuffer->bufferHandle = VULKAN_INTERNAL_CreateBufferHandle(
+    uniformBuffer->buffer = VULKAN_INTERNAL_CreateBuffer(
         renderer,
         (VkDeviceSize)size,
         0,
@@ -6978,6 +6611,7 @@ static VulkanUniformBuffer *VULKAN_INTERNAL_CreateUniformBuffer(
 
     uniformBuffer->drawOffset = 0;
     uniformBuffer->writeOffset = 0;
+    uniformBuffer->buffer->container = (VulkanBufferContainer *)uniformBuffer; // little hack for defrag
 
     return uniformBuffer;
 }
@@ -7014,7 +6648,7 @@ static void VULKAN_INTERNAL_ReleaseTexture(
     renderer->texturesToDestroy[renderer->texturesToDestroyCount] = vulkanTexture;
     renderer->texturesToDestroyCount += 1;
 
-    vulkanTexture->markedForDestroy = 1;
+    vulkanTexture->markedForDestroy = true;
 
     SDL_UnlockMutex(renderer->disposeLock);
 }
@@ -7030,15 +6664,14 @@ static void VULKAN_ReleaseTexture(
     SDL_LockMutex(renderer->disposeLock);
 
     for (i = 0; i < vulkanTextureContainer->textureCount; i += 1) {
-        VULKAN_INTERNAL_ReleaseTexture(renderer, vulkanTextureContainer->textureHandles[i]->vulkanTexture);
-        SDL_free(vulkanTextureContainer->textureHandles[i]);
+        VULKAN_INTERNAL_ReleaseTexture(renderer, vulkanTextureContainer->textures[i]);
     }
 
     // Containers are just client handles, so we can destroy immediately
     if (vulkanTextureContainer->debugName != NULL) {
         SDL_free(vulkanTextureContainer->debugName);
     }
-    SDL_free(vulkanTextureContainer->textureHandles);
+    SDL_free(vulkanTextureContainer->textures);
     SDL_free(vulkanTextureContainer);
 
     SDL_UnlockMutex(renderer->disposeLock);
@@ -7100,15 +6733,14 @@ static void VULKAN_INTERNAL_ReleaseBufferContainer(
     SDL_LockMutex(renderer->disposeLock);
 
     for (i = 0; i < bufferContainer->bufferCount; i += 1) {
-        VULKAN_INTERNAL_ReleaseBuffer(renderer, bufferContainer->bufferHandles[i]->vulkanBuffer);
-        SDL_free(bufferContainer->bufferHandles[i]);
+        VULKAN_INTERNAL_ReleaseBuffer(renderer, bufferContainer->buffers[i]);
     }
 
     // Containers are just client handles, so we can free immediately
     if (bufferContainer->debugName != NULL) {
         SDL_free(bufferContainer->debugName);
     }
-    SDL_free(bufferContainer->bufferHandles);
+    SDL_free(bufferContainer->buffers);
     SDL_free(bufferContainer);
 
     SDL_UnlockMutex(renderer->disposeLock);
@@ -7219,14 +6851,14 @@ static VkRenderPass VULKAN_INTERNAL_FetchRenderPass(
     Uint32 i;
 
     for (i = 0; i < numColorTargets; i += 1) {
-        key.colorTargetDescriptions[i].format = ((VulkanTextureContainer *)colorTargetInfos[i].texture)->activeTextureHandle->vulkanTexture->format;
+        key.colorTargetDescriptions[i].format = SDLToVK_TextureFormat[((VulkanTextureContainer *)colorTargetInfos[i].texture)->header.info.format];
         key.colorTargetDescriptions[i].loadOp = colorTargetInfos[i].load_op;
         key.colorTargetDescriptions[i].storeOp = colorTargetInfos[i].store_op;
     }
 
-    key.colorAttachmentSampleCount = VK_SAMPLE_COUNT_1_BIT;
+    key.sampleCount = VK_SAMPLE_COUNT_1_BIT;
     if (numColorTargets > 0) {
-        key.colorAttachmentSampleCount = ((VulkanTextureContainer *)colorTargetInfos[0].texture)->activeTextureHandle->vulkanTexture->sampleCount;
+        key.sampleCount = SDLToVK_SampleCount[((VulkanTextureContainer *)colorTargetInfos[0].texture)->header.info.sample_count];
     }
 
     key.numColorTargets = numColorTargets;
@@ -7238,7 +6870,7 @@ static VkRenderPass VULKAN_INTERNAL_FetchRenderPass(
         key.depthStencilTargetDescription.stencilLoadOp = SDL_GPU_LOADOP_DONT_CARE;
         key.depthStencilTargetDescription.stencilStoreOp = SDL_GPU_STOREOP_DONT_CARE;
     } else {
-        key.depthStencilTargetDescription.format = ((VulkanTextureContainer *)depthStencilTargetInfo->texture)->activeTextureHandle->vulkanTexture->format;
+        key.depthStencilTargetDescription.format = SDLToVK_TextureFormat[((VulkanTextureContainer *)depthStencilTargetInfo->texture)->header.info.format];
         key.depthStencilTargetDescription.loadOp = depthStencilTargetInfo->load_op;
         key.depthStencilTargetDescription.storeOp = depthStencilTargetInfo->store_op;
         key.depthStencilTargetDescription.stencilLoadOp = depthStencilTargetInfo->stencil_load_op;
@@ -7300,14 +6932,13 @@ static VulkanFramebuffer *VULKAN_INTERNAL_FetchFramebuffer(
     VulkanFramebuffer *vulkanFramebuffer = NULL;
     VkFramebufferCreateInfo framebufferInfo;
     VkResult result;
-    VkImageView imageViewAttachments[2 * MAX_COLOR_TARGET_BINDINGS + 1];
+    VkImageView imageViewAttachments[2 * MAX_COLOR_TARGET_BINDINGS + 1 /* depth */];
     FramebufferHashTableKey key;
     Uint32 attachmentCount = 0;
     Uint32 i;
 
     for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
         key.colorAttachmentViews[i] = VK_NULL_HANDLE;
-        key.colorMultiSampleAttachmentViews[i] = VK_NULL_HANDLE;
     }
 
     key.numColorTargets = numColorTargets;
@@ -7322,10 +6953,6 @@ static VulkanFramebuffer *VULKAN_INTERNAL_FetchFramebuffer(
         Uint32 rtvIndex =
             container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0;
         key.colorAttachmentViews[i] = subresource->renderTargetViews[rtvIndex];
-
-        if (subresource->msaaTexHandle != NULL) {
-            key.colorMultiSampleAttachmentViews[i] = subresource->msaaTexHandle->vulkanTexture->subresources[0].renderTargetViews[0];
-        }
     }
 
     if (depthStencilTargetInfo == NULL) {
@@ -7370,14 +6997,18 @@ static VulkanFramebuffer *VULKAN_INTERNAL_FetchFramebuffer(
         Uint32 rtvIndex =
             container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? colorTargetInfos[i].layer_or_depth_plane : 0;
 
-        imageViewAttachments[attachmentCount] =
-            subresource->renderTargetViews[rtvIndex];
+        imageViewAttachments[attachmentCount] = subresource->renderTargetViews[rtvIndex];
 
         attachmentCount += 1;
 
-        if (subresource->msaaTexHandle != NULL) {
-            imageViewAttachments[attachmentCount] =
-                subresource->msaaTexHandle->vulkanTexture->subresources[0].renderTargetViews[0];
+        if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+            VulkanTextureContainer *resolveContainer = (VulkanTextureContainer *)colorTargetInfos[i].resolve_texture;
+            VulkanTextureSubresource *resolveSubresource = VULKAN_INTERNAL_FetchTextureSubresource(
+                resolveContainer,
+                colorTargetInfos[i].resolve_layer,
+                colorTargetInfos[i].resolve_mip_level);
+
+            imageViewAttachments[attachmentCount] = resolveSubresource->renderTargetViews[0];
 
             attachmentCount += 1;
         }
@@ -7444,7 +7075,6 @@ static void VULKAN_INTERNAL_SetCurrentViewport(
     vulkanCommandBuffer->currentViewport.maxDepth = viewport->max_depth;
 
     // Viewport flip for consistency with other backends
-    // FIXME: need moltenVK hack
     vulkanCommandBuffer->currentViewport.y = viewport->y + viewport->h;
     vulkanCommandBuffer->currentViewport.height = -viewport->h;
 
@@ -7557,7 +7187,7 @@ static void VULKAN_BindVertexSamplers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
-        vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture;
+        vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
         vulkanCommandBuffer->vertexSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
 
         VULKAN_INTERNAL_TrackSampler(
@@ -7566,7 +7196,7 @@ static void VULKAN_BindVertexSamplers(
 
         VULKAN_INTERNAL_TrackTexture(
             vulkanCommandBuffer,
-            textureContainer->activeTextureHandle->vulkanTexture);
+            textureContainer->activeTexture);
     }
 
     vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
@@ -7583,11 +7213,11 @@ static void VULKAN_BindVertexStorageTextures(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
 
-        vulkanCommandBuffer->vertexStorageTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture;
+        vulkanCommandBuffer->vertexStorageTextures[firstSlot + i] = textureContainer->activeTexture;
 
         VULKAN_INTERNAL_TrackTexture(
             vulkanCommandBuffer,
-            textureContainer->activeTextureHandle->vulkanTexture);
+            textureContainer->activeTexture);
     }
 
     vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
@@ -7606,11 +7236,11 @@ static void VULKAN_BindVertexStorageBuffers(
     for (i = 0; i < numBindings; i += 1) {
         bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
 
-        vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer;
+        vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
 
         VULKAN_INTERNAL_TrackBuffer(
             vulkanCommandBuffer,
-            bufferContainer->activeBufferHandle->vulkanBuffer);
+            bufferContainer->activeBuffer);
     }
 
     vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
@@ -7626,7 +7256,7 @@ static void VULKAN_BindFragmentSamplers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
-        vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture;
+        vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
         vulkanCommandBuffer->fragmentSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
 
         VULKAN_INTERNAL_TrackSampler(
@@ -7635,7 +7265,7 @@ static void VULKAN_BindFragmentSamplers(
 
         VULKAN_INTERNAL_TrackTexture(
             vulkanCommandBuffer,
-            textureContainer->activeTextureHandle->vulkanTexture);
+            textureContainer->activeTexture);
     }
 
     vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
@@ -7653,11 +7283,11 @@ static void VULKAN_BindFragmentStorageTextures(
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
 
         vulkanCommandBuffer->fragmentStorageTextures[firstSlot + i] =
-            textureContainer->activeTextureHandle->vulkanTexture;
+            textureContainer->activeTexture;
 
         VULKAN_INTERNAL_TrackTexture(
             vulkanCommandBuffer,
-            textureContainer->activeTextureHandle->vulkanTexture);
+            textureContainer->activeTexture);
     }
 
     vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
@@ -7676,11 +7306,11 @@ static void VULKAN_BindFragmentStorageBuffers(
     for (i = 0; i < numBindings; i += 1) {
         bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
 
-        vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer;
+        vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
 
         VULKAN_INTERNAL_TrackBuffer(
             vulkanCommandBuffer,
-            bufferContainer->activeBufferHandle->vulkanBuffer);
+            bufferContainer->activeBuffer);
     }
 
     vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
@@ -7766,7 +7396,7 @@ static void VULKAN_INTERNAL_PushUniformData(
     }
 
     // If there is no more room, acquire a new uniform buffer
-    if (uniformBuffer->writeOffset + blockSize + MAX_UBO_SECTION_SIZE >= uniformBuffer->bufferHandle->vulkanBuffer->size) {
+    if (uniformBuffer->writeOffset + blockSize + MAX_UBO_SECTION_SIZE >= uniformBuffer->buffer->size) {
         uniformBuffer = VULKAN_INTERNAL_AcquireUniformBufferFromPool(commandBuffer);
 
         uniformBuffer->drawOffset = 0;
@@ -7790,8 +7420,8 @@ static void VULKAN_INTERNAL_PushUniformData(
     uniformBuffer->drawOffset = uniformBuffer->writeOffset;
 
     Uint8 *dst =
-        uniformBuffer->bufferHandle->vulkanBuffer->usedRegion->allocation->mapPointer +
-        uniformBuffer->bufferHandle->vulkanBuffer->usedRegion->resourceOffset +
+        uniformBuffer->buffer->usedRegion->allocation->mapPointer +
+        uniformBuffer->buffer->usedRegion->resourceOffset +
         uniformBuffer->writeOffset;
 
     SDL_memcpy(
@@ -7826,8 +7456,7 @@ static void VULKAN_BeginRenderPass(
 
     Uint32 w, h;
     VkClearValue *clearValues;
-    Uint32 clearCount = numColorTargets;
-    Uint32 multisampleAttachmentCount = 0;
+    Uint32 clearCount = 0;
     Uint32 totalColorAttachmentCount = 0;
     Uint32 i;
     SDL_GPUViewport defaultViewport;
@@ -7839,8 +7468,8 @@ static void VULKAN_BeginRenderPass(
     for (i = 0; i < numColorTargets; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)colorTargetInfos[i].texture;
 
-        w = textureContainer->activeTextureHandle->vulkanTexture->dimensions.width >> colorTargetInfos[i].mip_level;
-        h = textureContainer->activeTextureHandle->vulkanTexture->dimensions.height >> colorTargetInfos[i].mip_level;
+        w = textureContainer->header.info.width >> colorTargetInfos[i].mip_level;
+        h = textureContainer->header.info.height >> colorTargetInfos[i].mip_level;
 
         // The framebuffer cannot be larger than the smallest attachment.
 
@@ -7851,19 +7480,13 @@ static void VULKAN_BeginRenderPass(
         if (h < framebufferHeight) {
             framebufferHeight = h;
         }
-
-        // FIXME: validate this in gpu.c
-        if (!(textureContainer->header.info.usage & SDL_GPU_TEXTUREUSAGE_COLOR_TARGET)) {
-            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Color attachment texture was not designated as a target!");
-            return;
-        }
     }
 
     if (depthStencilTargetInfo != NULL) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)depthStencilTargetInfo->texture;
 
-        w = textureContainer->activeTextureHandle->vulkanTexture->dimensions.width;
-        h = textureContainer->activeTextureHandle->vulkanTexture->dimensions.height;
+        w = textureContainer->header.info.width;
+        h = textureContainer->header.info.height;
 
         // The framebuffer cannot be larger than the smallest attachment.
 
@@ -7874,12 +7497,6 @@ static void VULKAN_BeginRenderPass(
         if (h < framebufferHeight) {
             framebufferHeight = h;
         }
-
-        // FIXME: validate this in gpu.c
-        if (!(textureContainer->header.info.usage & SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET)) {
-            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Depth stencil attachment texture was not designated as a target!");
-            return;
-        }
     }
 
     for (i = 0; i < numColorTargets; i += 1) {
@@ -7893,26 +7510,31 @@ static void VULKAN_BeginRenderPass(
             colorTargetInfos[i].cycle,
             VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT);
 
-        if (subresource->msaaTexHandle != NULL) {
-            // Transition the multisample attachment
-            VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
+        vulkanCommandBuffer->colorAttachmentSubresources[vulkanCommandBuffer->colorAttachmentSubresourceCount] = subresource;
+        vulkanCommandBuffer->colorAttachmentSubresourceCount += 1;
+        VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, subresource->parent);
+        totalColorAttachmentCount += 1;
+        clearCount += 1;
+
+        if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+            VulkanTextureContainer *resolveContainer = (VulkanTextureContainer *)colorTargetInfos[i].resolve_texture;
+            VulkanTextureSubresource *resolveSubresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
                 renderer,
                 vulkanCommandBuffer,
-                VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT,
-                &subresource->msaaTexHandle->vulkanTexture->subresources[0]);
-
+                resolveContainer,
+                colorTargetInfos[i].resolve_layer,
+                colorTargetInfos[i].resolve_mip_level,
+                colorTargetInfos[i].cycle_resolve_texture,
+                VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT);
+
+            vulkanCommandBuffer->resolveAttachmentSubresources[vulkanCommandBuffer->resolveAttachmentSubresourceCount] = resolveSubresource;
+            vulkanCommandBuffer->resolveAttachmentSubresourceCount += 1;
+            VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, resolveSubresource->parent);
+            totalColorAttachmentCount += 1;
             clearCount += 1;
-            multisampleAttachmentCount += 1;
         }
-
-        vulkanCommandBuffer->colorAttachmentSubresources[i] = subresource;
-
-        VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, subresource->parent);
-        // TODO: do we need to track the msaa texture? or is it implicitly only used when the regular texture is used?
     }
 
-    vulkanCommandBuffer->colorAttachmentSubresourceCount = numColorTargets;
-
     if (depthStencilTargetInfo != NULL) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)depthStencilTargetInfo->texture;
         VulkanTextureSubresource *subresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
@@ -7924,11 +7546,9 @@ static void VULKAN_BeginRenderPass(
             depthStencilTargetInfo->cycle,
             VULKAN_TEXTURE_USAGE_MODE_DEPTH_STENCIL_ATTACHMENT);
 
-        clearCount += 1;
-
         vulkanCommandBuffer->depthStencilAttachmentSubresource = subresource;
-
         VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, subresource->parent);
+        clearCount += 1;
     }
 
     // Fetch required render objects
@@ -7955,25 +7575,14 @@ static void VULKAN_BeginRenderPass(
 
     clearValues = SDL_stack_alloc(VkClearValue, clearCount);
 
-    totalColorAttachmentCount = numColorTargets + multisampleAttachmentCount;
-
     for (i = 0; i < totalColorAttachmentCount; i += 1) {
         clearValues[i].color.float32[0] = colorTargetInfos[i].clear_color.r;
         clearValues[i].color.float32[1] = colorTargetInfos[i].clear_color.g;
         clearValues[i].color.float32[2] = colorTargetInfos[i].clear_color.b;
         clearValues[i].color.float32[3] = colorTargetInfos[i].clear_color.a;
 
-        VulkanTextureContainer *container = (VulkanTextureContainer *)colorTargetInfos[i].texture;
-        VulkanTextureSubresource *subresource = VULKAN_INTERNAL_FetchTextureSubresource(
-            container,
-            container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : colorTargetInfos[i].layer_or_depth_plane,
-            colorTargetInfos[i].mip_level);
-
-        if (subresource->parent->sampleCount > VK_SAMPLE_COUNT_1_BIT) {
-            clearValues[i + 1].color.float32[0] = colorTargetInfos[i].clear_color.r;
-            clearValues[i + 1].color.float32[1] = colorTargetInfos[i].clear_color.g;
-            clearValues[i + 1].color.float32[2] = colorTargetInfos[i].clear_color.b;
-            clearValues[i + 1].color.float32[3] = colorTargetInfos[i].clear_color.a;
+        if (colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE || colorTargetInfos[i].store_op == SDL_GPU_STOREOP_RESOLVE_AND_STORE) {
+            // Skip over the resolve texture, we're not clearing it
             i += 1;
         }
     }
@@ -8095,7 +7704,7 @@ static void VULKAN_BindVertexBuffers(
     Uint32 i;
 
     for (i = 0; i < numBindings; i += 1) {
-        currentVulkanBuffer = ((VulkanBufferContainer *)bindings[i].buffer)->activeBufferHandle->vulkanBuffer;
+        currentVulkanBuffer = ((VulkanBufferContainer *)bindings[i].buffer)->activeBuffer;
         buffers[i] = currentVulkanBuffer->buffer;
         offsets[i] = (VkDeviceSize)bindings[i].offset;
         VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, currentVulkanBuffer);
@@ -8119,7 +7728,7 @@ static void VULKAN_BindIndexBuffer(
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
     VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
-    VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)binding->buffer)->activeBufferHandle->vulkanBuffer;
+    VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)binding->buffer)->activeBuffer;
 
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
 
@@ -8181,6 +7790,15 @@ static void VULKAN_EndRenderPass(
     }
     vulkanCommandBuffer->colorAttachmentSubresourceCount = 0;
 
+    for (i = 0; i < vulkanCommandBuffer->resolveAttachmentSubresourceCount; i += 1) {
+        VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
+            renderer,
+            vulkanCommandBuffer,
+            VULKAN_TEXTURE_USAGE_MODE_COLOR_ATTACHMENT,
+            vulkanCommandBuffer->resolveAttachmentSubresources[i]);
+    }
+    vulkanCommandBuffer->resolveAttachmentSubresourceCount = 0;
+
     if (vulkanCommandBuffer->depthStencilAttachmentSubresource != NULL) {
         VULKAN_INTERNAL_TextureSubresourceTransitionToDefaultUsage(
             renderer,
@@ -8199,6 +7817,7 @@ static void VULKAN_EndRenderPass(
 
     // Reset bind state
     SDL_zeroa(vulkanCommandBuffer->colorAttachmentSubresources);
+    SDL_zeroa(vulkanCommandBuffer->resolveAttachmentSubresources);
     vulkanCommandBuffer->depthStencilAttachmentSubresource = NULL;
 
     SDL_zeroa(vulkanCommandBuffer->vertexSamplers);
@@ -8229,10 +7848,6 @@ static void VULKAN_BeginComputePass(
 
     for (i = 0; i < numStorageTextureBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextureBindings[i].texture;
-        if (!(textureContainer->activeTextureHandle->vulkanTexture->usageFlags & SDL_GPU_TEXTUREUSAGE_COMPUTE_STORAGE_WRITE)) {
-            SDL_LogError(SDL_LOG_CATEGORY_GPU, "Attempted to bind read-only texture as compute write texture");
-        }
-
         VulkanTextureSubresource *subresource = VULKAN_INTERNAL_PrepareTextureSubresourceForWrite(
             renderer,
             vulkanCommandBuffer,
@@ -8308,7 +7923,7 @@ static void VULKAN_BindComputeSamplers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
-        vulkanCommandBuffer->computeSamplerTextures[firstSlot + i] = textureContainer->activeTextureHandle->vulkanTexture;
+        vulkanCommandBuffer->computeSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
         vulkanCommandBuffer->computeSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
 
         VULKAN_INTERNAL_TrackSampler(
@@ -8317,7 +7932,7 @@ static void VULKAN_BindComputeSamplers(
 
         VULKAN_INTERNAL_TrackTexture(
             vulkanCommandBuffer,
-            textureContainer->activeTextureHandle->vulkanTexture);
+            textureContainer->activeTexture);
     }
 
     vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
@@ -8344,17 +7959,17 @@ static void VULKAN_BindComputeStorageTextures(
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
 
         vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] =
-            textureContainer->activeTextureHandle->vulkanTexture;
+            textureContainer->activeTexture;
 
         VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
             renderer,
             vulkanCommandBuffer,
             VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
-            textureContainer->activeTextureHandle->vulkanTexture);
+            textureContainer->activeTexture);
 
         VULKAN_INTERNAL_TrackTexture(
             vulkanCommandBuffer,
-            textureContainer->activeTextureHandle->vulkanTexture);
+            textureContainer->activeTexture);
     }
 
     vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
@@ -8382,17 +7997,17 @@ static void VULKAN_BindComputeStorageBuffers(
 
         bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
 
-        vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] = bufferContainer->activeBufferHandle->vulkanBuffer;
+        vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
 
         VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
             renderer,
             vulkanCommandBuffer,
             VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
-            bufferContainer->activeBufferHandle->vulkanBuffer);
+            bufferContainer->activeBuffer);
 
         VULKAN_INTERNAL_TrackBuffer(
             vulkanCommandBuffer,
-            bufferContainer->activeBufferHandle->vulkanBuffer);
+            bufferContainer->activeBuffer);
     }
 
     vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
@@ -8639,7 +8254,7 @@ static void VULKAN_INTERNAL_BindComputeDescriptorSets(
             currentWriteDescriptorSet->pTexelBufferView = NULL;
             currentWriteDescriptorSet->pImageInfo = NULL;
 
-            bufferInfos[bufferInfoCount].buffer = commandBuffer->computeUniformBuffers[i]->bufferHandle->vulkanBuffer->buffer;
+            bufferInfos[bufferInfoCount].buffer = commandBuffer->computeUniformBuffers[i]->buffer->buffer;
             bufferInfos[bufferInfoCount].offset = 0;
             bufferInfos[bufferInfoCount].range = MAX_UBO_SECTION_SIZE;
 
@@ -8707,7 +8322,7 @@ static void VULKAN_DispatchComputeIndirect(
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
     VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
-    VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBufferHandle->vulkanBuffer;
+    VulkanBuffer *vulkanBuffer = ((VulkanBufferContainer *)buffer)->activeBuffer;
 
     VULKAN_INTERNAL_BindComputeDescriptorSets(renderer, vulkanCommandBuffer);
 
@@ -8792,15 +8407,15 @@ static void *VULKAN_MapTransferBuffer(
 
     if (
         cycle &&
-        SDL_AtomicGet(&transferBufferContainer->activeBufferHandle->vulkanBuffer->referenceCount) > 0) {
+        SDL_AtomicGet(&transferBufferContainer->activeBuffer->referenceCount) > 0) {
         VULKAN_INTERNAL_CycleActiveBuffer(
             renderer,
             transferBufferContainer);
     }
 
     Uint8 *bufferPointer =
-        transferBufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->allocation->mapPointer +
-        transferBufferContainer->activeBufferHandle->vulkanBuffer->usedRegion->resourceOffset;
+        transferBufferContainer->activeBuffer->usedRegion->allocation->mapPointer +
+        transferBufferContainer->activeBuffer->usedRegion->resourceOffset;
 
     return bufferPointer;
 }
@@ -8861,7 +8476,7 @@ static void VULKAN_UploadToTexture(
 
     renderer->vkCmdCopyBufferToImage(
         vulkanCommandBuffer->commandBuffer,
-        transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer,
+        transferBufferContainer->activeBuffer->buffer,
         vulkanTextureSubresource->parent->image,
         VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
         1,
@@ -8873,7 +8488,7 @@ static void VULKAN_UploadToTexture(
         VULKAN_TEXTURE_USAGE_MODE_COPY_DESTINATION,
         vulkanTextureSubresource);
 
-    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer);
+    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
 }
 
@@ -8904,7 +8519,7 @@ static void VULKAN_UploadToBuffer(
 
     renderer->vkCmdCopyBuffer(
         vulkanCommandBuffer->commandBuffer,
-        transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer,
+        transferBufferContainer->activeBuffer->buffer,
         vulkanBuffer->buffer,
         1,
         &bufferCopy);
@@ -8915,7 +8530,7 @@ static void VULKAN_UploadToBuffer(
         VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
         vulkanBuffer);
 
-    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer);
+    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, vulkanBuffer);
 }
 
@@ -8963,7 +8578,7 @@ static void VULKAN_DownloadFromTexture(
         vulkanCommandBuffer->commandBuffer,
         vulkanTextureSubresource->parent->image,
         VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-        transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer,
+        transferBufferContainer->activeBuffer->buffer,
         1,
         &imageCopy);
 
@@ -8973,7 +8588,7 @@ static void VULKAN_DownloadFromTexture(
         VULKAN_TEXTURE_USAGE_MODE_COPY_SOURCE,
         vulkanTextureSubresource);
 
-    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer);
+    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
     VULKAN_INTERNAL_TrackTexture(vulkanCommandBuffer, vulkanTextureSubresource->parent);
 }
 
@@ -8994,7 +8609,7 @@ static void VULKAN_DownloadFromBuffer(
         renderer,
         vulkanCommandBuffer,
         VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
-        bufferContainer->activeBufferHandle->vulkanBuffer);
+        bufferContainer->activeBuffer);
 
     bufferCopy.srcOffset = source->offset;
     bufferCopy.dstOffset = destination->offset;
@@ -9002,8 +8617,8 @@ static void VULKAN_DownloadFromBuffer(
 
     renderer->vkCmdCopyBuffer(
         vulkanCommandBuffer->commandBuffer,
-        bufferContainer->activeBufferHandle->vulkanBuffer->buffer,
-        transferBufferContainer->activeBufferHandle->vulkanBuffer->buffer,
+        bufferContainer->activeBuffer->buffer,
+        transferBufferContainer->activeBuffer->buffer,
         1,
         &bufferCopy);
 
@@ -9011,10 +8626,10 @@ static void VULKAN_DownloadFromBuffer(
         renderer,
         vulkanCommandBuffer,
         VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
-        bufferContainer->activeBufferHandle->vulkanBuffer);
+        bufferContainer->activeBuffer);
 
-    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBufferHandle->vulkanBuffer);
-    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, bufferContainer->activeBufferHandle->vulkanBuffer);
+    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, transferBufferContainer->activeBuffer);
+    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, bufferContainer->activeBuffer);
 }
 
 static void VULKAN_CopyTextureToTexture(
@@ -9119,7 +8734,7 @@ static void VULKAN_CopyBufferToBuffer(
         renderer,
         vulkanCommandBuffer,
         VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
-        srcContainer->activeBufferHandle->vulkanBuffer);
+        srcContainer->activeBuffer);
 
     bufferCopy.srcOffset = source->offset;
     bufferCopy.dstOffset = destination->offset;
@@ -9127,7 +8742,7 @@ static void VULKAN_CopyBufferToBuffer(
 
     renderer->vkCmdCopyBuffer(
         vulkanCommandBuffer->commandBuffer,
-        srcContainer->activeBufferHandle->vulkanBuffer->buffer,
+        srcContainer->activeBuffer->buffer,
         dstBuffer->buffer,
         1,
         &bufferCopy);
@@ -9136,7 +8751,7 @@ static void VULKAN_CopyBufferToBuffer(
         renderer,
         vulkanCommandBuffer,
         VULKAN_BUFFER_USAGE_MODE_COPY_SOURCE,
-        srcContainer->activeBufferHandle->vulkanBuffer);
+        srcContainer->activeBuffer);
 
     VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
         renderer,
@@ -9144,7 +8759,7 @@ static void VULKAN_CopyBufferToBuffer(
         VULKAN_BUFFER_USAGE_MODE_COPY_DESTINATION,
         dstBuffer);
 
-    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, srcContainer->activeBufferHandle->vulkanBuffer);
+    VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, srcContainer->activeBuffer);
     VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, dstBuffer);
 }
 
@@ -9154,28 +8769,28 @@ static void VULKAN_GenerateMipmaps(
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
     VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
-    VulkanTexture *vulkanTexture = ((VulkanTextureContainer *)texture)->activeTextureHandle->vulkanTexture;
+    VulkanTextureContainer *container = (VulkanTextureContainer *)texture;
     VulkanTextureSubresource *srcTextureSubresource;
     VulkanTextureSubresource *dstTextureSubresource;
     VkImageBlit blit;
 
     // Blit each slice sequentially. Barriers, barriers everywhere!
-    for (Uint32 layerOrDepthIndex = 0; layerOrDepthIndex < vulkanTexture->layerCount; layerOrDepthIndex += 1)
-        for (Uint32 level = 1; level < vulkanTexture->numLevels; level += 1) {
-            Uint32 layer = vulkanTexture->type == SDL_GPU_TEXTURETYPE_3D ? 0 : layerOrDepthIndex;
-            Uint32 depth = vulkanTexture->type == SDL_GPU_TEXTURETYPE_3D ? layerOrDepthIndex : 0;
+    for (Uint32 layerOrDepthIndex = 0; layerOrDepthIndex < container->header.info.layer_count_or_depth; layerOrDepthIndex += 1)
+        for (Uint32 level = 1; level < container->header.info.num_levels; level += 1) {
+            Uint32 layer = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? 0 : layerOrDepthIndex;
+            Uint32 depth = container->header.info.type == SDL_GPU_TEXTURETYPE_3D ? layerOrDepthIndex : 0;
 
             Uint32 srcSubresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
                 level - 1,
                 layer,
-                vulkanTexture->numLevels);
+                container->header.info.num_levels);
             Uint32 dstSubresourceIndex = VULKAN_INTERNAL_GetTextureSubresourceIndex(
                 level,
                 layer,
-                vulkanTexture->numLevels);
+                container->header.info.num_levels);
 
-            srcTextureSubresource = &vulkanTexture->subresources[srcSubresourceIndex];
-            dstTextureSubresource = &vulkanTexture->subresources[dstSubresourceIndex];
+            srcTextureSubresource = &container->activeTexture->subresources[srcSubresourceIndex];
+            dstTextureSubresource = &container->activeTexture->subresources[dstSubresourceIndex];
 
             VULKAN_INTERNAL_TextureSubresourceTransitionFromDefaultUsage(
                 renderer,
@@ -9193,16 +8808,16 @@ static void VULKAN_GenerateMipmaps(
             blit.srcOffsets[0].y = 0;
             blit.srcOffsets[0].z = depth;
 
-            blit.srcOffsets[1].x = vulkanTexture->dimensions.width >> (level - 1);
-            blit.srcOffsets[1].y = vulkanTexture->dimensions.height >> (level - 1);
+            blit.srcOffsets[1].x = container->header.info.width >> (level - 1);
+            blit.srcOffsets[1].y = container->header.info.height >> (level - 1);
             blit.srcOffsets[1].z = depth + 1;
 
             blit.dstOffsets[0].x = 0;
             blit.dstOffsets[0].y = 0;
             blit.dstOffsets[0].z = depth;
 
-            blit.dstOffsets[1].x = vulkanTexture->dimensions.width >> level;
-            blit.dstOffsets[1].y = vulkanTexture->dimensions.height >> level;
+            blit.dstOffsets[1].x = container->header.info.width >> level;
+            blit.dstOffsets[1].y = container->header.info.height >> level;
             blit.dstOffsets[1].z = depth + 1;
 
             blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
@@ -9217,9 +8832,9 @@ static void VULKAN_GenerateMipmaps(
 
             renderer->vkCmdBlitImage(
                 vulkanCommandBuffer->commandBuffer,
-                vulkanTexture->image,
+                container->activeTexture->image,
                 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
-                vulkanTexture->image,
+                container->activeTexture->image,
                 VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                 1,
                 &blit,
@@ -9612,9 +9227,11 @@ static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer(
     commandBuffer->currentComputePipeline = NULL;
     commandBuffer->currentGraphicsPipeline = NULL;
 
-    for (i = 0; i < MAX_COLOR_TARGET_BINDINGS; i += 1) {
-        commandBuffer->colorAttachmentSubresources[i] = NULL;
-    }
+    SDL_zeroa(commandBuffer->colorAttachmentSubresources);
+    SDL_zeroa(commandBuffer->resolveAttachmentSubresources);
+    commandBuffer->depthStencilAttachmentSubresource = NULL;
+    commandBuffer->colorAttachmentSubresourceCount = 0;
+    commandBuffer->resolveAttachmentSubresourceCount = 0;
 
     for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
         commandBuffer->vertexUniformBuffers[i] = NULL;
@@ -9622,8 +9239,6 @@ static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer(
         commandBuffer->computeUniformBuffers[i] = NULL;
     }
 
-    commandBuffer->depthStencilAttachmentSubresource = NULL;
-
     commandBuffer->needNewVertexResourceDescriptorSet = true;
     commandBuffer->needNewVertexUniformDescriptorSet = true;
     commandBuffer->needNewVertexUniformOffsets = true;
@@ -10061,7 +9676,7 @@ static SDL_GPUTexture *VULKAN_AcquireSwapchainTexture(
     imageBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
     imageBarrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
     imageBarrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
-    imageBarrier.image = swapchainTextureContainer->activeTextureHandle->vulkanTexture->image;
+    imageBarrier.image = swapchainTextureContainer->activeTexture->image;
     imageBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
     imageBarrier.subresourceRange.baseMipLevel = 0;
     imageBarrier.subresourceRange.levelCount = 1;
@@ -10718,12 +10333,12 @@ static Uint8 VULKAN_INTERNAL_DefragmentMemory(
         currentRegion = allocation->usedRegions[i];
 
         if (currentRegion->isBuffer && !currentRegion->vulkanBuffer->markedForDestroy) {
-            currentRegion->vulkanBuffer->usageFlags |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
+            currentRegion->vulkanBuffer->usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT;
 
             newBuffer = VULKAN_INTERNAL_CreateBuffer(
                 renderer,
                 currentRegion->vulkanBuffer->size,
-                currentRegion->vulkanBuffer->usageFlags,
+                currentRegion->vulkanBuffer->usage,
                 currentRegion->vulkanBuffer->type);
 
             if (newBuffer == NULL) {
@@ -10734,13 +10349,12 @@ static Uint8 VULKAN_INTERNAL_DefragmentMemory(
             if (
                 renderer->debugMode &&
                 renderer->supportsDebugUtils &&
-                currentRegion->vulkanBuffer->handle != NULL &&
-                currentRegion->vulkanBuffer->handle->container != NULL &&
-                currentRegion->vulkanBuffer->handle->container->debugName != NULL) {
+                currentRegion->vulkanBuffer->container != NULL &&
+                currentRegion->vulkanBuffer->container->debugName != NULL) {
                 VULKAN_INTERNAL_SetBufferName(
                     renderer,
                     newBuffer,
-                    currentRegion->vulkanBuffer->handle->container->debugName);
+                    currentRegion->vulkanBuffer->container->debugName);
             }
 
             // Copy buffer contents if necessary
@@ -10780,34 +10394,29 @@ static Uint8 VULKAN_INTERNAL_DefragmentMemory(
             }
 
             // re-point original container to new buffer
-            if (currentRegion->vulkanBuffer->handle != NULL) {
-                newBuffer->handle = currentRegion->vulkanBuffer->handle;
-                newBuffer->handle->vulkanBuffer = newBuffer;
-                currentRegion->vulkanBuffer->handle = NULL;
+            newBuffer->container = currentRegion->vulkanBuffer->container;
+            newBuffer->containerIndex = currentRegion->vulkanBuffer->containerIndex;
+            if (newBuffer->type == VULKAN_BUFFER_TYPE_UNIFORM) {
+                ((VulkanUniformBuffer *)newBuffer->container)->buffer = newBuffer;
+            } else {
+                newBuffer->container->buffers[newBuffer->containerIndex] = newBuffer;
+                if (newBuffer->container->activeBuffer == currentRegion->vulkanBuffer) {
+                    newBuffer->container->activeBuffer = newBuffer;
+                }
             }
 
             VULKAN_INTERNAL_ReleaseBuffer(renderer, currentRegion->vulkanBuffer);
         } else if (!currentRegion->isBuffer && !currentRegion->vulkanTexture->markedForDestroy) {
             newTexture = VULKAN_INTERNAL_CreateTexture(
                 renderer,
-                currentRegion->vulkanTexture->dimensions.width,
-                currentRegion->vulkanTexture->dimensions.height,
-                currentRegion->vulkanTexture->depth,
-                currentRegion->vulkanTexture->type,
-                currentRegion->vulkanTexture->layerCount,
-                currentRegion->vulkanTexture->numLevels,
-                currentRegion->vulkanTexture->sampleCount,
-                currentRegion->vulkanTexture->format,
-                currentRegion->vulkanTexture->swizzle,
-                currentRegion->vulkanTexture->aspectFlags,
-                currentRegion->vulkanTexture->usageFlags,
-                currentRegion->vulkanTexture->isMSAAColorTarget);
+                &currentRegion->vulkanTexture->container->header.info);
 
             if (newTexture == NULL) {
                 SDL_LogError(SDL_LOG_CATEGORY_GPU, "Failed to create defrag texture!");
                 return 0;
             }
 
+            SDL_GPUTextureCreateInfo info = currentRegion->vulkanTexture->container->header.info;
             for (subresourceIndex = 0; subresourceIndex < currentRegion->vulkanTexture->subresourceCount; subresourceIndex += 1) {
                 // copy subresource if necessary
                 srcSubresource = &currentRegion->vulkanTexture->subresources[subresourceIndex];
@@ -10817,13 +10426,12 @@ static Uint8 VULKAN_INTERNAL_DefragmentMemory(
                 if (
                     renderer->debugMode &&
                     renderer->supportsDebugUtils &&
-                    srcSubresource->parent->handle != NULL &&
-                    srcSubresource->parent->handle->container != NULL &&
-                    srcSubresource->parent->handle->container->debugName != NULL) {
+                    srcSubresource->parent->container != NULL &&
+                    srcSubresource->parent->container->debugName != NULL) {
                     VULKAN_INTERNAL_SetTextureName(
                         renderer,
                         currentRegion->vulkanTexture,
-                        srcSubresource->parent->handle->container->debugName);
+                        srcSubresource->parent->container->debugName);
                 }
 
                 if (srcSubresource->transitioned) {
@@ -10846,9 +10454,9 @@ static Uint8 VULKAN_INTERNAL_DefragmentMemory(
                     imageCopy.srcSubresource.baseArrayLayer = srcSubresource->layer;
                     imageCopy.srcSubresource.layerCount = 1;
                     imageCopy.srcSubresource.mipLevel = srcSubresource->level;
-                    imageCopy.extent.width = SDL_max(1, srcSubresource->parent->dimensions.width >> srcSubresource->level);
-                    imageCopy.extent.height = SDL_max(1, srcSubresource->parent->dimensions.height >> srcSubresource->level);
-                    imageCopy.extent.depth = srcSubresource->parent->depth;
+                    imageCopy.extent.width = SDL_max(1, info.width >> srcSubresource->level);
+                    imageCopy.extent.height = SDL_max(1, info.height >> srcSubresource->level);
+                    imageCopy.extent.depth = info.type == SDL_GPU_TEXTURETYPE_3D ? info.layer_count_or_depth : 1;
                     imageCopy.dstOffset.x = 0;
                     imageCopy.dstOffset.y = 0;
                     imageCopy.dstOffset.z = 0;
@@ -10878,9 +10486,12 @@ static Uint8 VULKAN_INTERNAL_DefragmentMemory(
             }
 
             // re-point original container to new texture
-            newTexture->handle = currentRegion->vulkanTexture->handle;
-            newTexture->handle->vulkanTexture = newTexture;
-            currentRegion->vulkanTexture->handle = NULL;
+            newTexture->container = currentRegion->vulkanTexture->container;
+            newTexture->containerIndex = currentRegion->vulkanTexture->containerIndex;
+            newTexture->container->textures[currentRegion->vulkanTexture->containerIndex] = newTexture;
+            if (currentRegion->vulkanTexture == currentRegion->vulkanTexture->container->activeTexture) {
+                newTexture->container->activeTexture = newTexture;
+            }
 
             VULKAN_INTERNAL_ReleaseTexture(renderer, currentRegion->vulkanTexture);
         }
@@ -10903,7 +10514,7 @@ static bool VULKAN_SupportsTextureFormat(
     SDL_GPUTextureUsageFlags usage)
 {
     VulkanRenderer *renderer = (VulkanRenderer *)driverData;
-    VkFormat vulkanFormat = SDLToVK_SurfaceFormat[format];
+    VkFormat vulkanFormat = SDLToVK_TextureFormat[format];
     VkImageUsageFlags vulkanUsage = 0;
     VkImageCreateFlags createFlags = 0;
     VkImageFormatProperties properties;

+ 71 - 30
test/testgpu_spinning_cube.c

@@ -42,7 +42,7 @@ typedef struct RenderState
 typedef struct WindowState
 {
     int angle_x, angle_y, angle_z;
-    SDL_GPUTexture *tex_depth, *tex_msaa;
+    SDL_GPUTexture *tex_depth, *tex_msaa, *tex_resolve;
     Uint32 prev_drawablew, prev_drawableh;
 } WindowState;
 
@@ -59,6 +59,7 @@ static void shutdownGPU(void)
             WindowState *winstate = &window_states[i];
             SDL_ReleaseGPUTexture(gpu_device, winstate->tex_depth);
             SDL_ReleaseGPUTexture(gpu_device, winstate->tex_msaa);
+            SDL_ReleaseGPUTexture(gpu_device, winstate->tex_resolve);
             SDL_ReleaseWindowFromGPUDevice(gpu_device, state->windows[i]);
         }
         SDL_free(window_states);
@@ -249,20 +250,20 @@ static const VertexData vertex_data[] = {
 static SDL_GPUTexture*
 CreateDepthTexture(Uint32 drawablew, Uint32 drawableh)
 {
-    SDL_GPUTextureCreateInfo depthtex_createinfo;
+    SDL_GPUTextureCreateInfo createinfo;
     SDL_GPUTexture *result;
 
-    depthtex_createinfo.type = SDL_GPU_TEXTURETYPE_2D;
-    depthtex_createinfo.format = SDL_GPU_TEXTUREFORMAT_D16_UNORM;
-    depthtex_createinfo.width = drawablew;
-    depthtex_createinfo.height = drawableh;
-    depthtex_createinfo.layer_count_or_depth = 1;
-    depthtex_createinfo.num_levels = 1;
-    depthtex_createinfo.sample_count = render_state.sample_count;
-    depthtex_createinfo.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
-    depthtex_createinfo.props = 0;
-
-    result = SDL_CreateGPUTexture(gpu_device, &depthtex_createinfo);
+    createinfo.type = SDL_GPU_TEXTURETYPE_2D;
+    createinfo.format = SDL_GPU_TEXTUREFORMAT_D16_UNORM;
+    createinfo.width = drawablew;
+    createinfo.height = drawableh;
+    createinfo.layer_count_or_depth = 1;
+    createinfo.num_levels = 1;
+    createinfo.sample_count = render_state.sample_count;
+    createinfo.usage = SDL_GPU_TEXTUREUSAGE_DEPTH_STENCIL_TARGET;
+    createinfo.props = 0;
+
+    result = SDL_CreateGPUTexture(gpu_device, &createinfo);
     CHECK_CREATE(result, "Depth Texture")
 
     return result;
@@ -271,29 +272,55 @@ CreateDepthTexture(Uint32 drawablew, Uint32 drawableh)
 static SDL_GPUTexture*
 CreateMSAATexture(Uint32 drawablew, Uint32 drawableh)
 {
-    SDL_GPUTextureCreateInfo msaatex_createinfo;
+    SDL_GPUTextureCreateInfo createinfo;
     SDL_GPUTexture *result;
 
     if (render_state.sample_count == SDL_GPU_SAMPLECOUNT_1) {
         return NULL;
     }
 
-    msaatex_createinfo.type = SDL_GPU_TEXTURETYPE_2D;
-    msaatex_createinfo.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]);
-    msaatex_createinfo.width = drawablew;
-    msaatex_createinfo.height = drawableh;
-    msaatex_createinfo.layer_count_or_depth = 1;
-    msaatex_createinfo.num_levels = 1;
-    msaatex_createinfo.sample_count = render_state.sample_count;
-    msaatex_createinfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER;
-    msaatex_createinfo.props = 0;
-
-    result = SDL_CreateGPUTexture(gpu_device, &msaatex_createinfo);
+    createinfo.type = SDL_GPU_TEXTURETYPE_2D;
+    createinfo.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]);
+    createinfo.width = drawablew;
+    createinfo.height = drawableh;
+    createinfo.layer_count_or_depth = 1;
+    createinfo.num_levels = 1;
+    createinfo.sample_count = render_state.sample_count;
+    createinfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET;
+    createinfo.props = 0;
+
+    result = SDL_CreateGPUTexture(gpu_device, &createinfo);
     CHECK_CREATE(result, "MSAA Texture")
 
     return result;
 }
 
+static SDL_GPUTexture *
+CreateResolveTexture(Uint32 drawablew, Uint32 drawableh)
+{
+    SDL_GPUTextureCreateInfo createinfo;
+    SDL_GPUTexture *result;
+
+    if (render_state.sample_count == SDL_GPU_SAMPLECOUNT_1) {
+        return NULL;
+    }
+
+    createinfo.type = SDL_GPU_TEXTURETYPE_2D;
+    createinfo.format = SDL_GetGPUSwapchainTextureFormat(gpu_device, state->windows[0]);
+    createinfo.width = drawablew;
+    createinfo.height = drawableh;
+    createinfo.layer_count_or_depth = 1;
+    createinfo.num_levels = 1;
+    createinfo.sample_count = SDL_GPU_SAMPLECOUNT_1;
+    createinfo.usage = SDL_GPU_TEXTUREUSAGE_COLOR_TARGET | SDL_GPU_TEXTUREUSAGE_SAMPLER;
+    createinfo.props = 0;
+
+    result = SDL_CreateGPUTexture(gpu_device, &createinfo);
+    CHECK_CREATE(result, "Resolve Texture")
+
+    return result;
+}
+
 static void
 Render(SDL_Window *window, const int windownum)
 {
@@ -354,8 +381,10 @@ Render(SDL_Window *window, const int windownum)
     if (winstate->prev_drawablew != drawablew || winstate->prev_drawableh != drawableh) {
         SDL_ReleaseGPUTexture(gpu_device, winstate->tex_depth);
         SDL_ReleaseGPUTexture(gpu_device, winstate->tex_msaa);
+        SDL_ReleaseGPUTexture(gpu_device, winstate->tex_resolve);
         winstate->tex_depth = CreateDepthTexture(drawablew, drawableh);
         winstate->tex_msaa = CreateMSAATexture(drawablew, drawableh);
+        winstate->tex_resolve = CreateResolveTexture(drawablew, drawableh);
     }
     winstate->prev_drawablew = drawablew;
     winstate->prev_drawableh = drawableh;
@@ -364,14 +393,25 @@ Render(SDL_Window *window, const int windownum)
 
     SDL_zero(color_target);
     color_target.clear_color.a = 1.0f;
-    color_target.load_op = SDL_GPU_LOADOP_CLEAR;
-    color_target.store_op = SDL_GPU_STOREOP_STORE;
-    color_target.texture = winstate->tex_msaa ? winstate->tex_msaa : swapchain;
+    if (winstate->tex_msaa) {
+        color_target.load_op = SDL_GPU_LOADOP_CLEAR;
+        color_target.store_op = SDL_GPU_STOREOP_RESOLVE;
+        color_target.texture = winstate->tex_msaa;
+        color_target.resolve_texture = winstate->tex_resolve;
+        color_target.cycle = SDL_TRUE;
+        color_target.cycle_resolve_texture = SDL_TRUE;
+    } else {
+        color_target.load_op = SDL_GPU_LOADOP_CLEAR;
+        color_target.store_op = SDL_GPU_STOREOP_STORE;
+        color_target.texture = swapchain;
+    }
 
     SDL_zero(depth_target);
     depth_target.clear_depth = 1.0f;
     depth_target.load_op = SDL_GPU_LOADOP_CLEAR;
     depth_target.store_op = SDL_GPU_STOREOP_DONT_CARE;
+    depth_target.stencil_load_op = SDL_GPU_LOADOP_DONT_CARE;
+    depth_target.stencil_store_op = SDL_GPU_STOREOP_DONT_CARE;
     depth_target.texture = winstate->tex_depth;
     depth_target.cycle = SDL_TRUE;
 
@@ -390,10 +430,10 @@ Render(SDL_Window *window, const int windownum)
     SDL_DrawGPUPrimitives(pass, 36, 1, 0, 0);
     SDL_EndGPURenderPass(pass);
 
-    /* Blit MSAA to swapchain, if needed */
+    /* Blit MSAA resolve target to swapchain, if needed */
     if (render_state.sample_count > SDL_GPU_SAMPLECOUNT_1) {
         SDL_zero(blit_info);
-        blit_info.source.texture = winstate->tex_msaa;
+        blit_info.source.texture = winstate->tex_resolve;
         blit_info.source.w = drawablew;
         blit_info.source.h = drawableh;
 
@@ -609,6 +649,7 @@ init_render_state(int msaa)
         SDL_GetWindowSizeInPixels(state->windows[i], (int*) &drawablew, (int*) &drawableh);
         winstate->tex_depth = CreateDepthTexture(drawablew, drawableh);
         winstate->tex_msaa = CreateMSAATexture(drawablew, drawableh);
+        winstate->tex_resolve = CreateResolveTexture(drawablew, drawableh);
 
         /* make each window different */
         winstate->angle_x = (i * 10) % 360;