Bläddra i källkod

GPU: Resource binding state shadowing (#12138)

---------

Co-authored-by: Caleb Cornett <caleb.cornett@outlook.com>
Evan Hemsley 2 månader sedan
förälder
incheckning
8e766c9252
3 ändrade filer med 556 tillägg och 411 borttagningar
  1. 118 89
      src/gpu/d3d12/SDL_gpu_d3d12.c
  2. 276 203
      src/gpu/metal/SDL_gpu_metal.m
  3. 162 119
      src/gpu/vulkan/SDL_gpu_vulkan.c

+ 118 - 89
src/gpu/d3d12/SDL_gpu_d3d12.c

@@ -4552,15 +4552,18 @@ static void D3D12_BindVertexBuffers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         D3D12Buffer *currentBuffer = ((D3D12BufferContainer *)bindings[i].buffer)->activeBuffer;
-        d3d12CommandBuffer->vertexBuffers[firstSlot + i] = currentBuffer;
-        d3d12CommandBuffer->vertexBufferOffsets[firstSlot + i] = bindings[i].offset;
-        D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, currentBuffer);
+
+        if (d3d12CommandBuffer->vertexBuffers[firstSlot + i] != currentBuffer || d3d12CommandBuffer->vertexBufferOffsets[firstSlot + i] != bindings[i].offset) {
+            D3D12_INTERNAL_TrackBuffer(d3d12CommandBuffer, currentBuffer);
+
+            d3d12CommandBuffer->vertexBuffers[firstSlot + i] = currentBuffer;
+            d3d12CommandBuffer->vertexBufferOffsets[firstSlot + i] = bindings[i].offset;
+            d3d12CommandBuffer->needVertexBufferBind = true;
+        }
     }
 
     d3d12CommandBuffer->vertexBufferCount =
         SDL_max(d3d12CommandBuffer->vertexBufferCount, firstSlot + numBindings);
-
-    d3d12CommandBuffer->needVertexBufferBind = true;
 }
 
 static void D3D12_BindIndexBuffer(
@@ -4596,19 +4599,24 @@ static void D3D12_BindVertexSamplers(
         D3D12TextureContainer *container = (D3D12TextureContainer *)textureSamplerBindings[i].texture;
         D3D12Sampler *sampler = (D3D12Sampler *)textureSamplerBindings[i].sampler;
 
-        D3D12_INTERNAL_TrackTexture(
-            d3d12CommandBuffer,
-            container->activeTexture);
+        if (d3d12CommandBuffer->vertexSamplers[firstSlot + i] != sampler) {
+            D3D12_INTERNAL_TrackSampler(
+                d3d12CommandBuffer,
+                sampler);
 
-        D3D12_INTERNAL_TrackSampler(
-            d3d12CommandBuffer,
-            sampler);
+            d3d12CommandBuffer->vertexSamplers[firstSlot + i] = sampler;
+            d3d12CommandBuffer->needVertexSamplerBind = true;
+        }
 
-        d3d12CommandBuffer->vertexSamplers[firstSlot + i] = sampler;
-        d3d12CommandBuffer->vertexSamplerTextures[firstSlot + i] = container->activeTexture;
-    }
+        if (d3d12CommandBuffer->vertexSamplerTextures[firstSlot + i] != container->activeTexture) {
+            D3D12_INTERNAL_TrackTexture(
+                d3d12CommandBuffer,
+                container->activeTexture);
 
-    d3d12CommandBuffer->needVertexSamplerBind = true;
+            d3d12CommandBuffer->vertexSamplerTextures[firstSlot + i] = container->activeTexture;
+            d3d12CommandBuffer->needVertexSamplerBind = true;
+        }
+    }
 }
 
 static void D3D12_BindVertexStorageTextures(
@@ -4623,12 +4631,13 @@ static void D3D12_BindVertexStorageTextures(
         D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
         D3D12Texture *texture = container->activeTexture;
 
-        D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, texture);
+        if (d3d12CommandBuffer->vertexStorageTextures[firstSlot + i] != texture) {
+            D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, texture);
 
-        d3d12CommandBuffer->vertexStorageTextures[firstSlot + i] = texture;
+            d3d12CommandBuffer->vertexStorageTextures[firstSlot + i] = texture;
+            d3d12CommandBuffer->needVertexStorageTextureBind = true;
+        }
     }
-
-    d3d12CommandBuffer->needVertexStorageTextureBind = true;
 }
 
 static void D3D12_BindVertexStorageBuffers(
@@ -4641,15 +4650,15 @@ static void D3D12_BindVertexStorageBuffers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
+        if (d3d12CommandBuffer->vertexStorageBuffers[firstSlot + i] != container->activeBuffer) {
+            D3D12_INTERNAL_TrackBuffer(
+                d3d12CommandBuffer,
+                container->activeBuffer);
 
-        D3D12_INTERNAL_TrackBuffer(
-            d3d12CommandBuffer,
-            container->activeBuffer);
-
-        d3d12CommandBuffer->vertexStorageBuffers[firstSlot + i] = container->activeBuffer;
+            d3d12CommandBuffer->vertexStorageBuffers[firstSlot + i] = container->activeBuffer;
+            d3d12CommandBuffer->needVertexStorageBufferBind = true;
+        }
     }
-
-    d3d12CommandBuffer->needVertexStorageBufferBind = true;
 }
 
 static void D3D12_BindFragmentSamplers(
@@ -4664,19 +4673,24 @@ static void D3D12_BindFragmentSamplers(
         D3D12TextureContainer *container = (D3D12TextureContainer *)textureSamplerBindings[i].texture;
         D3D12Sampler *sampler = (D3D12Sampler *)textureSamplerBindings[i].sampler;
 
-        D3D12_INTERNAL_TrackTexture(
-            d3d12CommandBuffer,
-            container->activeTexture);
+        if (d3d12CommandBuffer->fragmentSamplers[firstSlot + i] != sampler) {
+            D3D12_INTERNAL_TrackSampler(
+                d3d12CommandBuffer,
+                sampler);
 
-        D3D12_INTERNAL_TrackSampler(
-            d3d12CommandBuffer,
-            sampler);
+            d3d12CommandBuffer->fragmentSamplers[firstSlot + i] = sampler;
+            d3d12CommandBuffer->needFragmentSamplerBind = true;
+        }
 
-        d3d12CommandBuffer->fragmentSamplers[firstSlot + i] = sampler;
-        d3d12CommandBuffer->fragmentSamplerTextures[firstSlot + i] = container->activeTexture;
-    }
+        if (d3d12CommandBuffer->fragmentSamplerTextures[firstSlot + i] != container->activeTexture) {
+            D3D12_INTERNAL_TrackTexture(
+                d3d12CommandBuffer,
+                container->activeTexture);
 
-    d3d12CommandBuffer->needFragmentSamplerBind = true;
+            d3d12CommandBuffer->fragmentSamplerTextures[firstSlot + i] = container->activeTexture;
+            d3d12CommandBuffer->needFragmentSamplerBind = true;
+        }
+    }
 }
 
 static void D3D12_BindFragmentStorageTextures(
@@ -4691,12 +4705,13 @@ static void D3D12_BindFragmentStorageTextures(
         D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
         D3D12Texture *texture = container->activeTexture;
 
-        D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, texture);
+        if (d3d12CommandBuffer->fragmentStorageTextures[firstSlot + i] != texture) {
+            D3D12_INTERNAL_TrackTexture(d3d12CommandBuffer, texture);
 
-        d3d12CommandBuffer->fragmentStorageTextures[firstSlot + i] = texture;
+            d3d12CommandBuffer->fragmentStorageTextures[firstSlot + i] = texture;
+            d3d12CommandBuffer->needFragmentStorageTextureBind = true;
+        }
     }
-
-    d3d12CommandBuffer->needFragmentStorageTextureBind = true;
 }
 
 static void D3D12_BindFragmentStorageBuffers(
@@ -4710,14 +4725,15 @@ static void D3D12_BindFragmentStorageBuffers(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
 
-        D3D12_INTERNAL_TrackBuffer(
-            d3d12CommandBuffer,
-            container->activeBuffer);
+        if (d3d12CommandBuffer->fragmentStorageBuffers[firstSlot + i] != container->activeBuffer) {
+            D3D12_INTERNAL_TrackBuffer(
+                d3d12CommandBuffer,
+                container->activeBuffer);
 
-        d3d12CommandBuffer->fragmentStorageBuffers[firstSlot + i] = container->activeBuffer;
+            d3d12CommandBuffer->fragmentStorageBuffers[firstSlot + i] = container->activeBuffer;
+            d3d12CommandBuffer->needFragmentStorageBufferBind = true;
+        }
     }
-
-    d3d12CommandBuffer->needFragmentStorageBufferBind = true;
 }
 
 static void D3D12_PushVertexUniformData(
@@ -5330,20 +5346,26 @@ static void D3D12_BindComputeSamplers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         D3D12TextureContainer *container = (D3D12TextureContainer *)textureSamplerBindings[i].texture;
+        D3D12Sampler *sampler = (D3D12Sampler *)textureSamplerBindings[i].sampler;
 
-        D3D12_INTERNAL_TrackSampler(
-            d3d12CommandBuffer,
-            (D3D12Sampler *)textureSamplerBindings[i].sampler);
+        if (d3d12CommandBuffer->computeSamplers[firstSlot + i] != sampler) {
+            D3D12_INTERNAL_TrackSampler(
+                d3d12CommandBuffer,
+                (D3D12Sampler *)textureSamplerBindings[i].sampler);
 
-        D3D12_INTERNAL_TrackTexture(
-            d3d12CommandBuffer,
-            container->activeTexture);
+            d3d12CommandBuffer->computeSamplers[firstSlot + i] = (D3D12Sampler *)textureSamplerBindings[i].sampler;
+            d3d12CommandBuffer->needComputeSamplerBind = true;
+        }
 
-        d3d12CommandBuffer->computeSamplerTextures[firstSlot + i] = container->activeTexture;
-        d3d12CommandBuffer->computeSamplers[firstSlot + i] = (D3D12Sampler *)textureSamplerBindings[i].sampler;
-    }
+        if (d3d12CommandBuffer->computeSamplerTextures[firstSlot + i] != container->activeTexture) {
+            D3D12_INTERNAL_TrackTexture(
+                d3d12CommandBuffer,
+                container->activeTexture);
 
-    d3d12CommandBuffer->needComputeSamplerBind = true;
+            d3d12CommandBuffer->computeSamplerTextures[firstSlot + i] = container->activeTexture;
+            d3d12CommandBuffer->needComputeSamplerBind = true;
+        }
+    }
 }
 
 static void D3D12_BindComputeStorageTextures(
@@ -5355,27 +5377,31 @@ static void D3D12_BindComputeStorageTextures(
     D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
-        if (d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] != NULL) {
-            D3D12_INTERNAL_TextureTransitionToDefaultUsage(
+        D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
+
+        if (d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] != container->activeTexture) {
+            /* If a different texture was in this slot, transition it back to its default usage */
+            if (d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] != NULL) {
+                D3D12_INTERNAL_TextureTransitionToDefaultUsage(
+                    d3d12CommandBuffer,
+                    D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
+                    d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i]);
+            }
+
+            /* Then transition the new texture and prepare it for binding */
+            D3D12_INTERNAL_TextureTransitionFromDefaultUsage(
                 d3d12CommandBuffer,
                 D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
-                d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i]);
-        }
+                container->activeTexture);
 
-        D3D12TextureContainer *container = (D3D12TextureContainer *)storageTextures[i];
-        d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] = container->activeTexture;
-
-        D3D12_INTERNAL_TextureTransitionFromDefaultUsage(
-            d3d12CommandBuffer,
-            D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
-            container->activeTexture);
+            D3D12_INTERNAL_TrackTexture(
+                d3d12CommandBuffer,
+                container->activeTexture);
 
-        D3D12_INTERNAL_TrackTexture(
-            d3d12CommandBuffer,
-            container->activeTexture);
+            d3d12CommandBuffer->computeReadOnlyStorageTextures[firstSlot + i] = container->activeTexture;
+            d3d12CommandBuffer->needComputeReadOnlyStorageTextureBind = true;
+        }
     }
-
-    d3d12CommandBuffer->needComputeReadOnlyStorageTextureBind = true;
 }
 
 static void D3D12_BindComputeStorageBuffers(
@@ -5387,29 +5413,32 @@ static void D3D12_BindComputeStorageBuffers(
     D3D12CommandBuffer *d3d12CommandBuffer = (D3D12CommandBuffer *)commandBuffer;
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
-        if (d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] != NULL) {
-            D3D12_INTERNAL_BufferTransitionToDefaultUsage(
-                d3d12CommandBuffer,
-                D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
-                d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i]);
-        }
-
         D3D12BufferContainer *container = (D3D12BufferContainer *)storageBuffers[i];
         D3D12Buffer *buffer = container->activeBuffer;
 
-        d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] = buffer;
+        if (d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] != buffer) {
+            /* If a different buffer was in this slot, transition it back to its default usage */
+            if (d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] != NULL) {
+                D3D12_INTERNAL_BufferTransitionToDefaultUsage(
+                    d3d12CommandBuffer,
+                    D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
+                    d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i]);
+            }
 
-        D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
-            d3d12CommandBuffer,
-            D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
-            buffer);
+            /* Then transition the new buffer and prepare it for binding */
+            D3D12_INTERNAL_BufferTransitionFromDefaultUsage(
+                d3d12CommandBuffer,
+                D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE,
+                buffer);
 
-        D3D12_INTERNAL_TrackBuffer(
-            d3d12CommandBuffer,
-            buffer);
-    }
+            D3D12_INTERNAL_TrackBuffer(
+                d3d12CommandBuffer,
+                buffer);
 
-    d3d12CommandBuffer->needComputeReadOnlyStorageBufferBind = true;
+            d3d12CommandBuffer->computeReadOnlyStorageBuffers[firstSlot + i] = buffer;
+            d3d12CommandBuffer->needComputeReadOnlyStorageBufferBind = true;
+        }
+    }
 }
 
 static void D3D12_PushComputeUniformData(

+ 276 - 203
src/gpu/metal/SDL_gpu_metal.m

@@ -567,30 +567,37 @@ typedef struct MetalCommandBuffer
     MetalComputePipeline *compute_pipeline;
 
     // Resource slot state
+    bool needVertexBufferBind;
     bool needVertexSamplerBind;
     bool needVertexStorageTextureBind;
     bool needVertexStorageBufferBind;
-    bool needVertexUniformBind;
+    bool needVertexUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
 
     bool needFragmentSamplerBind;
     bool needFragmentStorageTextureBind;
     bool needFragmentStorageBufferBind;
-    bool needFragmentUniformBind;
+    bool needFragmentUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
 
     bool needComputeSamplerBind;
-    bool needComputeTextureBind;
-    bool needComputeBufferBind;
-    bool needComputeUniformBind;
+    bool needComputeReadOnlyStorageTextureBind;
+    bool needComputeReadOnlyStorageBufferBind;
+    bool needComputeUniformBufferBind[MAX_UNIFORM_BUFFERS_PER_STAGE];
+
+    id<MTLBuffer> vertexBuffers[MAX_VERTEX_BUFFERS];
+    Uint32 vertexBufferOffsets[MAX_VERTEX_BUFFERS];
+    Uint32 vertexBufferCount;
 
     id<MTLSamplerState> vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
     id<MTLTexture> vertexTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
     id<MTLTexture> vertexStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
     id<MTLBuffer> vertexStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
+    MetalUniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
 
     id<MTLSamplerState> fragmentSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
     id<MTLTexture> fragmentTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
     id<MTLTexture> fragmentStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
     id<MTLBuffer> fragmentStorageBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
+    MetalUniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
 
     id<MTLTexture> computeSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
     id<MTLSamplerState> computeSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
@@ -598,10 +605,6 @@ typedef struct MetalCommandBuffer
     id<MTLBuffer> computeReadOnlyBuffers[MAX_STORAGE_BUFFERS_PER_STAGE];
     id<MTLTexture> computeReadWriteTextures[MAX_COMPUTE_WRITE_TEXTURES];
     id<MTLBuffer> computeReadWriteBuffers[MAX_COMPUTE_WRITE_BUFFERS];
-
-    // Uniform buffers
-    MetalUniformBuffer *vertexUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
-    MetalUniformBuffer *fragmentUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
     MetalUniformBuffer *computeUniformBuffers[MAX_UNIFORM_BUFFERS_PER_STAGE];
 
     MetalUniformBuffer **usedUniformBuffers;
@@ -2130,20 +2133,6 @@ static SDL_GPUCommandBuffer *METAL_AcquireCommandBuffer(
             commandBuffer->computeUniformBuffers[i] = NULL;
         }
 
-        // FIXME: Do we actually need to set this?
-        commandBuffer->needVertexSamplerBind = true;
-        commandBuffer->needVertexStorageTextureBind = true;
-        commandBuffer->needVertexStorageBufferBind = true;
-        commandBuffer->needVertexUniformBind = true;
-        commandBuffer->needFragmentSamplerBind = true;
-        commandBuffer->needFragmentStorageTextureBind = true;
-        commandBuffer->needFragmentStorageBufferBind = true;
-        commandBuffer->needFragmentUniformBind = true;
-        commandBuffer->needComputeSamplerBind = true;
-        commandBuffer->needComputeBufferBind = true;
-        commandBuffer->needComputeTextureBind = true;
-        commandBuffer->needComputeUniformBind = true;
-
         commandBuffer->autoReleaseFence = true;
 
         SDL_UnlockMutex(renderer->acquireCommandBufferLock);
@@ -2397,73 +2386,71 @@ static void METAL_BindGraphicsPipeline(
 {
     @autoreleasepool {
         MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
-        MetalGraphicsPipeline *metalGraphicsPipeline = (MetalGraphicsPipeline *)graphicsPipeline;
-        SDL_GPURasterizerState *rast = &metalGraphicsPipeline->rasterizerState;
+        MetalGraphicsPipeline *pipeline = (MetalGraphicsPipeline *)graphicsPipeline;
+        SDL_GPURasterizerState *rast = &pipeline->rasterizerState;
+        Uint32 i;
 
-        metalCommandBuffer->graphics_pipeline = metalGraphicsPipeline;
+        metalCommandBuffer->graphics_pipeline = pipeline;
 
-        [metalCommandBuffer->renderEncoder setRenderPipelineState:metalGraphicsPipeline->handle];
+        [metalCommandBuffer->renderEncoder setRenderPipelineState:pipeline->handle];
 
         // Apply rasterizer state
-        [metalCommandBuffer->renderEncoder setTriangleFillMode:SDLToMetal_PolygonMode[metalGraphicsPipeline->rasterizerState.fill_mode]];
-        [metalCommandBuffer->renderEncoder setCullMode:SDLToMetal_CullMode[metalGraphicsPipeline->rasterizerState.cull_mode]];
-        [metalCommandBuffer->renderEncoder setFrontFacingWinding:SDLToMetal_FrontFace[metalGraphicsPipeline->rasterizerState.front_face]];
-        [metalCommandBuffer->renderEncoder setDepthClipMode:SDLToMetal_DepthClipMode(metalGraphicsPipeline->rasterizerState.enable_depth_clip)];
+        [metalCommandBuffer->renderEncoder setTriangleFillMode:SDLToMetal_PolygonMode[pipeline->rasterizerState.fill_mode]];
+        [metalCommandBuffer->renderEncoder setCullMode:SDLToMetal_CullMode[pipeline->rasterizerState.cull_mode]];
+        [metalCommandBuffer->renderEncoder setFrontFacingWinding:SDLToMetal_FrontFace[pipeline->rasterizerState.front_face]];
+        [metalCommandBuffer->renderEncoder setDepthClipMode:SDLToMetal_DepthClipMode(pipeline->rasterizerState.enable_depth_clip)];
         [metalCommandBuffer->renderEncoder
             setDepthBias:((rast->enable_depth_bias) ? rast->depth_bias_constant_factor : 0)
               slopeScale:((rast->enable_depth_bias) ? rast->depth_bias_slope_factor : 0)
               clamp:((rast->enable_depth_bias) ? rast->depth_bias_clamp : 0)];
 
         // Apply depth-stencil state
-        if (metalGraphicsPipeline->depth_stencil_state != NULL) {
+        if (pipeline->depth_stencil_state != NULL) {
             [metalCommandBuffer->renderEncoder
-                setDepthStencilState:metalGraphicsPipeline->depth_stencil_state];
+                setDepthStencilState:pipeline->depth_stencil_state];
+        }
+
+        for (i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
+            metalCommandBuffer->needVertexUniformBufferBind[i] = true;
+            metalCommandBuffer->needFragmentUniformBufferBind[i] = true;
         }
 
-        for (Uint32 i = 0; i < metalGraphicsPipeline->vertexUniformBufferCount; i += 1) {
+        for (i = 0; i < pipeline->vertexUniformBufferCount; i += 1) {
             if (metalCommandBuffer->vertexUniformBuffers[i] == NULL) {
                 metalCommandBuffer->vertexUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool(
                     metalCommandBuffer);
             }
         }
 
-        for (Uint32 i = 0; i < metalGraphicsPipeline->fragmentUniformBufferCount; i += 1) {
+        for (i = 0; i < pipeline->fragmentUniformBufferCount; i += 1) {
             if (metalCommandBuffer->fragmentUniformBuffers[i] == NULL) {
                 metalCommandBuffer->fragmentUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool(
                     metalCommandBuffer);
             }
         }
-
-        metalCommandBuffer->needVertexUniformBind = true;
-        metalCommandBuffer->needFragmentUniformBind = true;
     }
 }
 
 static void METAL_BindVertexBuffers(
     SDL_GPUCommandBuffer *commandBuffer,
-    Uint32 firstBinding,
+    Uint32 firstSlot,
     const SDL_GPUBufferBinding *bindings,
     Uint32 numBindings)
 {
-    @autoreleasepool {
-        MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
-        id<MTLBuffer> metalBuffers[MAX_VERTEX_BUFFERS];
-        NSUInteger bufferOffsets[MAX_VERTEX_BUFFERS];
-        NSRange range = NSMakeRange(METAL_FIRST_VERTEX_BUFFER_SLOT + firstBinding, numBindings);
-
-        if (range.length == 0) {
-            return;
-        }
+    MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
 
-        for (Uint32 i = 0; i < numBindings; i += 1) {
-            MetalBuffer *currentBuffer = ((MetalBufferContainer *)bindings[i].buffer)->activeBuffer;
-            metalBuffers[firstBinding + i] = currentBuffer->handle;
-            bufferOffsets[firstBinding + i] = bindings[i].offset;
+    for (Uint32 i = 0; i < numBindings; i += 1) {
+        MetalBuffer *currentBuffer = ((MetalBufferContainer *)bindings[i].buffer)->activeBuffer;
+        if (metalCommandBuffer->vertexBuffers[firstSlot + i] != currentBuffer->handle || metalCommandBuffer->vertexBufferOffsets[firstSlot + i] != bindings[i].offset) {
+            metalCommandBuffer->vertexBuffers[firstSlot + i] = currentBuffer->handle;
+            metalCommandBuffer->vertexBufferOffsets[firstSlot + i] = bindings[i].offset;
+            metalCommandBuffer->needVertexBufferBind = true;
             METAL_INTERNAL_TrackBuffer(metalCommandBuffer, currentBuffer);
         }
-
-        [metalCommandBuffer->renderEncoder setVertexBuffers:metalBuffers offsets:bufferOffsets withRange:range];
     }
+
+    metalCommandBuffer->vertexBufferCount =
+        SDL_max(metalCommandBuffer->vertexBufferCount, firstSlot + numBindings);
 }
 
 static void METAL_BindIndexBuffer(
@@ -2487,22 +2474,28 @@ static void METAL_BindVertexSamplers(
 {
     MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
     MetalTextureContainer *textureContainer;
+    MetalSampler *sampler;
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         textureContainer = (MetalTextureContainer *)textureSamplerBindings[i].texture;
+        sampler = (MetalSampler *)textureSamplerBindings[i].sampler;
 
-        METAL_INTERNAL_TrackTexture(
-            metalCommandBuffer,
-            textureContainer->activeTexture);
+        if (metalCommandBuffer->vertexSamplers[firstSlot + i] != sampler->handle) {
+            metalCommandBuffer->vertexSamplers[firstSlot + i] = sampler->handle;
+            metalCommandBuffer->needVertexSamplerBind  = true;
+        }
 
-        metalCommandBuffer->vertexSamplers[firstSlot + i] =
-            ((MetalSampler *)textureSamplerBindings[i].sampler)->handle;
+        if (metalCommandBuffer->vertexTextures[firstSlot + i] != textureContainer->activeTexture->handle) {
+            METAL_INTERNAL_TrackTexture(
+                metalCommandBuffer,
+                textureContainer->activeTexture);
 
-        metalCommandBuffer->vertexTextures[firstSlot + i] =
-            textureContainer->activeTexture->handle;
-    }
+            metalCommandBuffer->vertexTextures[firstSlot + i] =
+                textureContainer->activeTexture->handle;
 
-    metalCommandBuffer->needVertexSamplerBind = true;
+            metalCommandBuffer->needVertexSamplerBind  = true;
+        }
+    }
 }
 
 static void METAL_BindVertexStorageTextures(
@@ -2517,15 +2510,17 @@ static void METAL_BindVertexStorageTextures(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         textureContainer = (MetalTextureContainer *)storageTextures[i];
 
-        METAL_INTERNAL_TrackTexture(
-            metalCommandBuffer,
-            textureContainer->activeTexture);
+        if (metalCommandBuffer->vertexStorageTextures[firstSlot + i] != textureContainer->activeTexture->handle) {
+            METAL_INTERNAL_TrackTexture(
+                metalCommandBuffer,
+                textureContainer->activeTexture);
 
-        metalCommandBuffer->vertexStorageTextures[firstSlot + i] =
-            textureContainer->activeTexture->handle;
-    }
+            metalCommandBuffer->vertexStorageTextures[firstSlot + i] =
+                textureContainer->activeTexture->handle;
 
-    metalCommandBuffer->needVertexStorageTextureBind = true;
+            metalCommandBuffer->needVertexStorageTextureBind = true;
+        }
+    }
 }
 
 static void METAL_BindVertexStorageBuffers(
@@ -2540,15 +2535,17 @@ static void METAL_BindVertexStorageBuffers(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         bufferContainer = (MetalBufferContainer *)storageBuffers[i];
 
-        METAL_INTERNAL_TrackBuffer(
-            metalCommandBuffer,
-            bufferContainer->activeBuffer);
+        if (metalCommandBuffer->vertexStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer->handle) {
+            METAL_INTERNAL_TrackBuffer(
+                metalCommandBuffer,
+                bufferContainer->activeBuffer);
+
+            metalCommandBuffer->vertexStorageBuffers[firstSlot + i] =
+                bufferContainer->activeBuffer->handle;
 
-        metalCommandBuffer->vertexStorageBuffers[firstSlot + i] =
-            bufferContainer->activeBuffer->handle;
+            metalCommandBuffer->needVertexStorageBufferBind = true;
+        }
     }
-
-    metalCommandBuffer->needVertexStorageBufferBind = true;
 }
 
 static void METAL_BindFragmentSamplers(
@@ -2559,22 +2556,28 @@ static void METAL_BindFragmentSamplers(
 {
     MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
     MetalTextureContainer *textureContainer;
+    MetalSampler *sampler;
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         textureContainer = (MetalTextureContainer *)textureSamplerBindings[i].texture;
+        sampler = (MetalSampler *)textureSamplerBindings[i].sampler;
 
-        METAL_INTERNAL_TrackTexture(
-            metalCommandBuffer,
-            textureContainer->activeTexture);
+        if (metalCommandBuffer->fragmentSamplers[firstSlot + i] != sampler->handle) {
+            metalCommandBuffer->fragmentSamplers[firstSlot + i] = sampler->handle;
+            metalCommandBuffer->needFragmentSamplerBind  = true;
+        }
 
-        metalCommandBuffer->fragmentSamplers[firstSlot + i] =
-            ((MetalSampler *)textureSamplerBindings[i].sampler)->handle;
+        if (metalCommandBuffer->fragmentTextures[firstSlot + i] != textureContainer->activeTexture->handle) {
+            METAL_INTERNAL_TrackTexture(
+                metalCommandBuffer,
+                textureContainer->activeTexture);
 
-        metalCommandBuffer->fragmentTextures[firstSlot + i] =
-            textureContainer->activeTexture->handle;
-    }
+            metalCommandBuffer->fragmentTextures[firstSlot + i] =
+                textureContainer->activeTexture->handle;
 
-    metalCommandBuffer->needFragmentSamplerBind = true;
+            metalCommandBuffer->needFragmentSamplerBind  = true;
+        }
+    }
 }
 
 static void METAL_BindFragmentStorageTextures(
@@ -2589,15 +2592,17 @@ static void METAL_BindFragmentStorageTextures(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         textureContainer = (MetalTextureContainer *)storageTextures[i];
 
-        METAL_INTERNAL_TrackTexture(
-            metalCommandBuffer,
-            textureContainer->activeTexture);
+        if (metalCommandBuffer->fragmentStorageTextures[firstSlot + i] != textureContainer->activeTexture->handle) {
+            METAL_INTERNAL_TrackTexture(
+                metalCommandBuffer,
+                textureContainer->activeTexture);
+
+            metalCommandBuffer->fragmentStorageTextures[firstSlot + i] =
+                textureContainer->activeTexture->handle;
 
-        metalCommandBuffer->fragmentStorageTextures[firstSlot + i] =
-            textureContainer->activeTexture->handle;
+            metalCommandBuffer->needFragmentStorageTextureBind = true;
+        }
     }
-
-    metalCommandBuffer->needFragmentStorageTextureBind = true;
 }
 
 static void METAL_BindFragmentStorageBuffers(
@@ -2612,15 +2617,17 @@ static void METAL_BindFragmentStorageBuffers(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         bufferContainer = (MetalBufferContainer *)storageBuffers[i];
 
-        METAL_INTERNAL_TrackBuffer(
-            metalCommandBuffer,
-            bufferContainer->activeBuffer);
+        if (metalCommandBuffer->fragmentStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer->handle) {
+            METAL_INTERNAL_TrackBuffer(
+                metalCommandBuffer,
+                bufferContainer->activeBuffer);
 
-        metalCommandBuffer->fragmentStorageBuffers[firstSlot + i] =
-            bufferContainer->activeBuffer->handle;
-    }
+            metalCommandBuffer->fragmentStorageBuffers[firstSlot + i] =
+                bufferContainer->activeBuffer->handle;
 
-    metalCommandBuffer->needFragmentStorageBufferBind = true;
+            metalCommandBuffer->needFragmentStorageBufferBind = true;
+        }
+    }
 }
 
 // This function assumes that it's called from within an autorelease pool
@@ -2630,85 +2637,115 @@ static void METAL_INTERNAL_BindGraphicsResources(
     MetalGraphicsPipeline *graphicsPipeline = commandBuffer->graphics_pipeline;
     NSUInteger offsets[MAX_STORAGE_BUFFERS_PER_STAGE] = { 0 };
 
+    // Vertex Buffers
+    if (commandBuffer->needVertexBufferBind) {
+        id<MTLBuffer> metalBuffers[MAX_VERTEX_BUFFERS];
+        NSUInteger bufferOffsets[MAX_VERTEX_BUFFERS];
+        NSRange range = NSMakeRange(METAL_FIRST_VERTEX_BUFFER_SLOT, commandBuffer->vertexBufferCount);
+        for (Uint32 i = 0; i < commandBuffer->vertexBufferCount; i += 1) {
+            metalBuffers[i] = commandBuffer->vertexBuffers[i];
+            bufferOffsets[i] = commandBuffer->vertexBufferOffsets[i];
+        }
+        [commandBuffer->renderEncoder setVertexBuffers:metalBuffers offsets:bufferOffsets withRange:range];
+        commandBuffer->needVertexBufferBind = false;
+    }
+
     // Vertex Samplers+Textures
 
-    if (graphicsPipeline->vertexSamplerCount > 0 && commandBuffer->needVertexSamplerBind) {
-        [commandBuffer->renderEncoder setVertexSamplerStates:commandBuffer->vertexSamplers
-                                                   withRange:NSMakeRange(0, graphicsPipeline->vertexSamplerCount)];
-        [commandBuffer->renderEncoder setVertexTextures:commandBuffer->vertexTextures
-                                              withRange:NSMakeRange(0, graphicsPipeline->vertexSamplerCount)];
+    if (commandBuffer->needVertexSamplerBind) {
+        if (graphicsPipeline->vertexSamplerCount > 0) {
+            [commandBuffer->renderEncoder setVertexSamplerStates:commandBuffer->vertexSamplers
+                                                       withRange:NSMakeRange(0, graphicsPipeline->vertexSamplerCount)];
+            [commandBuffer->renderEncoder setVertexTextures:commandBuffer->vertexTextures
+                                                  withRange:NSMakeRange(0, graphicsPipeline->vertexSamplerCount)];
+        }
         commandBuffer->needVertexSamplerBind = false;
     }
 
     // Vertex Storage Textures
 
-    if (graphicsPipeline->vertexStorageTextureCount > 0 && commandBuffer->needVertexStorageTextureBind) {
-        [commandBuffer->renderEncoder setVertexTextures:commandBuffer->vertexStorageTextures
-                                              withRange:NSMakeRange(graphicsPipeline->vertexSamplerCount,
-                                                                    graphicsPipeline->vertexStorageTextureCount)];
+    if (commandBuffer->needVertexStorageTextureBind) {
+        if (graphicsPipeline->vertexStorageTextureCount > 0) {
+            [commandBuffer->renderEncoder setVertexTextures:commandBuffer->vertexStorageTextures
+                                                  withRange:NSMakeRange(graphicsPipeline->vertexSamplerCount,
+                                                                        graphicsPipeline->vertexStorageTextureCount)];
+        }
         commandBuffer->needVertexStorageTextureBind = false;
     }
 
     // Vertex Storage Buffers
 
-    if (graphicsPipeline->vertexStorageBufferCount > 0 && commandBuffer->needVertexStorageBufferBind) {
-        [commandBuffer->renderEncoder setVertexBuffers:commandBuffer->vertexStorageBuffers
-                                               offsets:offsets
-                                             withRange:NSMakeRange(graphicsPipeline->vertexUniformBufferCount,
-                                                                   graphicsPipeline->vertexStorageBufferCount)];
+    if (commandBuffer->needVertexStorageBufferBind) {
+        if (graphicsPipeline->vertexStorageBufferCount > 0) {
+            [commandBuffer->renderEncoder setVertexBuffers:commandBuffer->vertexStorageBuffers
+                                                   offsets:offsets
+                                                 withRange:NSMakeRange(graphicsPipeline->vertexUniformBufferCount,
+                                                                       graphicsPipeline->vertexStorageBufferCount)];
+        }
         commandBuffer->needVertexStorageBufferBind = false;
     }
 
     // Vertex Uniform Buffers
 
-    if (graphicsPipeline->vertexUniformBufferCount > 0 && commandBuffer->needVertexUniformBind) {
-        for (Uint32 i = 0; i < graphicsPipeline->vertexUniformBufferCount; i += 1) {
-            [commandBuffer->renderEncoder
-                setVertexBuffer:commandBuffer->vertexUniformBuffers[i]->handle
-                         offset:commandBuffer->vertexUniformBuffers[i]->drawOffset
-                        atIndex:i];
+    for (Uint32 i = 0; i < graphicsPipeline->vertexUniformBufferCount; i += 1) {
+        if (commandBuffer->needVertexUniformBufferBind[i]) {
+            if (graphicsPipeline->vertexUniformBufferCount > i) {
+                [commandBuffer->renderEncoder
+                    setVertexBuffer:commandBuffer->vertexUniformBuffers[i]->handle
+                             offset:commandBuffer->vertexUniformBuffers[i]->drawOffset
+                            atIndex:i];
+            }
+            commandBuffer->needVertexUniformBufferBind[i] = false;
         }
-        commandBuffer->needVertexUniformBind = false;
     }
 
     // Fragment Samplers+Textures
 
-    if (graphicsPipeline->fragmentSamplerCount > 0 && commandBuffer->needFragmentSamplerBind) {
-        [commandBuffer->renderEncoder setFragmentSamplerStates:commandBuffer->fragmentSamplers
-                                                     withRange:NSMakeRange(0, graphicsPipeline->fragmentSamplerCount)];
-        [commandBuffer->renderEncoder setFragmentTextures:commandBuffer->fragmentTextures
-                                                withRange:NSMakeRange(0, graphicsPipeline->fragmentSamplerCount)];
+    if (commandBuffer->needFragmentSamplerBind) {
+        if (graphicsPipeline->fragmentSamplerCount > 0) {
+            [commandBuffer->renderEncoder setFragmentSamplerStates:commandBuffer->fragmentSamplers
+                                                         withRange:NSMakeRange(0, graphicsPipeline->fragmentSamplerCount)];
+            [commandBuffer->renderEncoder setFragmentTextures:commandBuffer->fragmentTextures
+                                                    withRange:NSMakeRange(0, graphicsPipeline->fragmentSamplerCount)];
+        }
         commandBuffer->needFragmentSamplerBind = false;
     }
 
     // Fragment Storage Textures
 
-    if (graphicsPipeline->fragmentStorageTextureCount > 0 && commandBuffer->needFragmentStorageTextureBind) {
-        [commandBuffer->renderEncoder setFragmentTextures:commandBuffer->fragmentStorageTextures
-                                                withRange:NSMakeRange(graphicsPipeline->fragmentSamplerCount,
-                                                                      graphicsPipeline->fragmentStorageTextureCount)];
+    if (commandBuffer->needFragmentStorageTextureBind) {
+        if (graphicsPipeline->fragmentStorageTextureCount > 0) {
+            [commandBuffer->renderEncoder setFragmentTextures:commandBuffer->fragmentStorageTextures
+                                                    withRange:NSMakeRange(graphicsPipeline->fragmentSamplerCount,
+                                                                          graphicsPipeline->fragmentStorageTextureCount)];
+        }
         commandBuffer->needFragmentStorageTextureBind = false;
     }
 
     // Fragment Storage Buffers
 
-    if (graphicsPipeline->fragmentStorageBufferCount > 0 && commandBuffer->needFragmentStorageBufferBind) {
-        [commandBuffer->renderEncoder setFragmentBuffers:commandBuffer->fragmentStorageBuffers
-                                                 offsets:offsets
-                                               withRange:NSMakeRange(graphicsPipeline->fragmentUniformBufferCount,
-                                                                     graphicsPipeline->fragmentStorageBufferCount)];
+    if (commandBuffer->needFragmentStorageBufferBind) {
+        if (graphicsPipeline->fragmentStorageBufferCount > 0) {
+            [commandBuffer->renderEncoder setFragmentBuffers:commandBuffer->fragmentStorageBuffers
+                                                     offsets:offsets
+                                                   withRange:NSMakeRange(graphicsPipeline->fragmentUniformBufferCount,
+                                                                         graphicsPipeline->fragmentStorageBufferCount)];
+        }
         commandBuffer->needFragmentStorageBufferBind = false;
     }
 
     // Fragment Uniform Buffers
-    if (graphicsPipeline->fragmentUniformBufferCount > 0 && commandBuffer->needFragmentUniformBind) {
-        for (Uint32 i = 0; i < graphicsPipeline->fragmentUniformBufferCount; i += 1) {
-            [commandBuffer->renderEncoder
-                setFragmentBuffer:commandBuffer->fragmentUniformBuffers[i]->handle
-                           offset:commandBuffer->fragmentUniformBuffers[i]->drawOffset
-                          atIndex:i];
+
+    for (Uint32 i = 0; i < graphicsPipeline->fragmentUniformBufferCount; i += 1) {
+        if (commandBuffer->needFragmentUniformBufferBind[i]) {
+            if (graphicsPipeline->fragmentUniformBufferCount > i) {
+                [commandBuffer->renderEncoder
+                    setFragmentBuffer:commandBuffer->fragmentUniformBuffers[i]->handle
+                            offset:commandBuffer->fragmentUniformBuffers[i]->drawOffset
+                            atIndex:i];
+            }
+            commandBuffer->needFragmentUniformBufferBind[i] = false;
         }
-        commandBuffer->needFragmentUniformBind = false;
     }
 }
 
@@ -2720,7 +2757,6 @@ static void METAL_INTERNAL_BindComputeResources(
     NSUInteger offsets[MAX_STORAGE_BUFFERS_PER_STAGE] = { 0 };
 
     if (commandBuffer->needComputeSamplerBind) {
-        // Bind sampler textures
         if (computePipeline->numSamplers > 0) {
             [commandBuffer->computeEncoder setTextures:commandBuffer->computeSamplerTextures
                                              withRange:NSMakeRange(0, computePipeline->numSamplers)];
@@ -2730,54 +2766,36 @@ static void METAL_INTERNAL_BindComputeResources(
         commandBuffer->needComputeSamplerBind = false;
     }
 
-    if (commandBuffer->needComputeTextureBind) {
-        // Bind read-only textures
+    if (commandBuffer->needComputeReadOnlyStorageTextureBind) {
         if (computePipeline->numReadonlyStorageTextures > 0) {
             [commandBuffer->computeEncoder setTextures:commandBuffer->computeReadOnlyTextures
                                              withRange:NSMakeRange(
                                                            computePipeline->numSamplers,
                                                            computePipeline->numReadonlyStorageTextures)];
         }
-
-        // Bind write-only textures
-        if (computePipeline->numReadWriteStorageTextures > 0) {
-            [commandBuffer->computeEncoder setTextures:commandBuffer->computeReadWriteTextures
-                                             withRange:NSMakeRange(
-                                                           computePipeline->numSamplers + computePipeline->numReadonlyStorageTextures,
-                                                           computePipeline->numReadWriteStorageTextures)];
-        }
-        commandBuffer->needComputeTextureBind = false;
+        commandBuffer->needComputeReadOnlyStorageTextureBind = false;
     }
 
-    if (commandBuffer->needComputeBufferBind) {
-        // Bind read-only buffers
+    if (commandBuffer->needComputeReadOnlyStorageBufferBind) {
         if (computePipeline->numReadonlyStorageBuffers > 0) {
             [commandBuffer->computeEncoder setBuffers:commandBuffer->computeReadOnlyBuffers
                                               offsets:offsets
                                             withRange:NSMakeRange(computePipeline->numUniformBuffers,
                                                                   computePipeline->numReadonlyStorageBuffers)];
         }
-        // Bind write-only buffers
-        if (computePipeline->numReadWriteStorageBuffers > 0) {
-            [commandBuffer->computeEncoder setBuffers:commandBuffer->computeReadWriteBuffers
-                                              offsets:offsets
-                                            withRange:NSMakeRange(
-                                                          computePipeline->numUniformBuffers +
-                                                              computePipeline->numReadonlyStorageBuffers,
-                                                          computePipeline->numReadWriteStorageBuffers)];
-        }
-        commandBuffer->needComputeBufferBind = false;
+        commandBuffer->needComputeReadOnlyStorageBufferBind = false;
     }
 
-    if (commandBuffer->needComputeUniformBind) {
-        for (Uint32 i = 0; i < computePipeline->numUniformBuffers; i += 1) {
-            [commandBuffer->computeEncoder
-                setBuffer:commandBuffer->computeUniformBuffers[i]->handle
-                   offset:commandBuffer->computeUniformBuffers[i]->drawOffset
-                  atIndex:i];
+    for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
+        if (commandBuffer->needComputeUniformBufferBind[i]) {
+            if (computePipeline->numUniformBuffers > i) {
+                [commandBuffer->computeEncoder
+                    setBuffer:commandBuffer->computeUniformBuffers[i]->handle
+                    offset:commandBuffer->computeUniformBuffers[i]->drawOffset
+                    atIndex:i];
+            }
         }
-
-        commandBuffer->needComputeUniformBind = false;
+        commandBuffer->needComputeUniformBufferBind[i] = false;
     }
 }
 
@@ -2892,6 +2910,11 @@ static void METAL_EndRenderPass(
         [metalCommandBuffer->renderEncoder endEncoding];
         metalCommandBuffer->renderEncoder = nil;
 
+        for (Uint32 i = 0; i < MAX_VERTEX_BUFFERS; i += 1) {
+            metalCommandBuffer->vertexBuffers[i] = nil;
+            metalCommandBuffer->vertexBufferOffsets[i] = 0;
+            metalCommandBuffer->vertexBufferCount = 0;
+        }
         for (Uint32 i = 0; i < MAX_TEXTURE_SAMPLERS_PER_STAGE; i += 1) {
             metalCommandBuffer->vertexSamplers[i] = nil;
             metalCommandBuffer->vertexTextures[i] = nil;
@@ -2976,11 +2999,11 @@ static void METAL_INTERNAL_PushUniformData(
     metalUniformBuffer->writeOffset += alignedDataLength;
 
     if (shaderStage == SDL_GPU_SHADERSTAGE_VERTEX) {
-        metalCommandBuffer->needVertexUniformBind = true;
+        metalCommandBuffer->needVertexUniformBufferBind[slotIndex] = true;
     } else if (shaderStage == SDL_GPU_SHADERSTAGE_FRAGMENT) {
-        metalCommandBuffer->needFragmentUniformBind = true;
+        metalCommandBuffer->needFragmentUniformBufferBind[slotIndex] = true;
     } else if (shaderStage == SDL_GPU_SHADERSTAGE_COMPUTE) {
-        metalCommandBuffer->needComputeUniformBind = true;
+        metalCommandBuffer->needComputeUniformBufferBind[slotIndex] = true;
     } else {
         SDL_LogError(SDL_LOG_CATEGORY_GPU, "Unrecognized shader stage!");
     }
@@ -3078,7 +3101,6 @@ static void METAL_BeginComputePass(
                                                                   slices:NSMakeRange(storageTextureBindings[i].layer, 1)];
 
             metalCommandBuffer->computeReadWriteTextures[i] = textureView;
-            metalCommandBuffer->needComputeTextureBind = true;
         }
 
         for (Uint32 i = 0; i < numStorageBufferBindings; i += 1) {
@@ -3094,7 +3116,6 @@ static void METAL_BeginComputePass(
                 buffer);
 
             metalCommandBuffer->computeReadWriteBuffers[i] = buffer->handle;
-            metalCommandBuffer->needComputeBufferBind = true;
         }
     }
 }
@@ -3111,6 +3132,10 @@ static void METAL_BindComputePipeline(
 
         [metalCommandBuffer->computeEncoder setComputePipelineState:pipeline->handle];
 
+        for (Uint32 i = 0; i < MAX_UNIFORM_BUFFERS_PER_STAGE; i += 1) {
+            metalCommandBuffer->needComputeUniformBufferBind[i] = true;
+        }
+
         for (Uint32 i = 0; i < pipeline->numUniformBuffers; i += 1) {
             if (metalCommandBuffer->computeUniformBuffers[i] == NULL) {
                 metalCommandBuffer->computeUniformBuffers[i] = METAL_INTERNAL_AcquireUniformBufferFromPool(
@@ -3118,7 +3143,24 @@ static void METAL_BindComputePipeline(
             }
         }
 
-        metalCommandBuffer->needComputeUniformBind = true;
+        // Bind write-only resources
+        if (pipeline->numReadWriteStorageTextures > 0) {
+            [metalCommandBuffer->computeEncoder setTextures:metalCommandBuffer->computeReadWriteTextures
+                                                  withRange:NSMakeRange(
+                                                        pipeline->numSamplers +
+                                                            pipeline->numReadonlyStorageTextures,
+                                                        pipeline->numReadWriteStorageTextures)];
+        }
+
+        NSUInteger offsets[MAX_COMPUTE_WRITE_BUFFERS] = { 0 };
+        if (pipeline->numReadWriteStorageBuffers > 0) {
+            [metalCommandBuffer->computeEncoder setBuffers:metalCommandBuffer->computeReadWriteBuffers
+                                                   offsets:offsets
+                                                 withRange:NSMakeRange(
+                                                        pipeline->numUniformBuffers +
+                                                            pipeline->numReadonlyStorageBuffers,
+                                                        pipeline->numReadWriteStorageBuffers)];
+        }
     }
 }
 
@@ -3130,22 +3172,28 @@ static void METAL_BindComputeSamplers(
 {
     MetalCommandBuffer *metalCommandBuffer = (MetalCommandBuffer *)commandBuffer;
     MetalTextureContainer *textureContainer;
+    MetalSampler *sampler;
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         textureContainer = (MetalTextureContainer *)textureSamplerBindings[i].texture;
+        sampler = (MetalSampler *)textureSamplerBindings[i].sampler;
 
-        METAL_INTERNAL_TrackTexture(
-            metalCommandBuffer,
-            textureContainer->activeTexture);
+        if (metalCommandBuffer->computeSamplers[firstSlot + i] != sampler->handle) {
+            metalCommandBuffer->computeSamplers[firstSlot + i] = sampler->handle;
+            metalCommandBuffer->needComputeSamplerBind = true;
+        }
 
-        metalCommandBuffer->computeSamplers[firstSlot + i] =
-            ((MetalSampler *)textureSamplerBindings[i].sampler)->handle;
+        if (metalCommandBuffer->computeSamplerTextures[firstSlot + i] != textureContainer->activeTexture->handle) {
+            METAL_INTERNAL_TrackTexture(
+                metalCommandBuffer,
+                textureContainer->activeTexture);
 
-        metalCommandBuffer->computeSamplerTextures[firstSlot + i] =
-            textureContainer->activeTexture->handle;
-    }
+            metalCommandBuffer->computeSamplerTextures[firstSlot + i] =
+                textureContainer->activeTexture->handle;
 
-    metalCommandBuffer->needComputeSamplerBind = true;
+            metalCommandBuffer->needComputeSamplerBind = true;
+        }
+    }
 }
 
 static void METAL_BindComputeStorageTextures(
@@ -3160,15 +3208,17 @@ static void METAL_BindComputeStorageTextures(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         textureContainer = (MetalTextureContainer *)storageTextures[i];
 
-        METAL_INTERNAL_TrackTexture(
-            metalCommandBuffer,
-            textureContainer->activeTexture);
+        if (metalCommandBuffer->computeReadOnlyTextures[firstSlot + i] != textureContainer->activeTexture->handle) {
+            METAL_INTERNAL_TrackTexture(
+                metalCommandBuffer,
+                textureContainer->activeTexture);
+
+            metalCommandBuffer->computeReadOnlyTextures[firstSlot + i] =
+                textureContainer->activeTexture->handle;
 
-        metalCommandBuffer->computeReadOnlyTextures[firstSlot + i] =
-            textureContainer->activeTexture->handle;
+            metalCommandBuffer->needComputeReadOnlyStorageTextureBind = true;
+        }
     }
-
-    metalCommandBuffer->needComputeTextureBind = true;
 }
 
 static void METAL_BindComputeStorageBuffers(
@@ -3183,15 +3233,17 @@ static void METAL_BindComputeStorageBuffers(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         bufferContainer = (MetalBufferContainer *)storageBuffers[i];
 
-        METAL_INTERNAL_TrackBuffer(
-            metalCommandBuffer,
-            bufferContainer->activeBuffer);
+        if (metalCommandBuffer->computeReadOnlyBuffers[firstSlot + i] != bufferContainer->activeBuffer->handle) {
+            METAL_INTERNAL_TrackBuffer(
+                metalCommandBuffer,
+                bufferContainer->activeBuffer);
 
-        metalCommandBuffer->computeReadOnlyBuffers[firstSlot + i] =
-            bufferContainer->activeBuffer->handle;
-    }
+            metalCommandBuffer->computeReadOnlyBuffers[firstSlot + i] =
+                bufferContainer->activeBuffer->handle;
 
-    metalCommandBuffer->needComputeBufferBind = true;
+            metalCommandBuffer->needComputeReadOnlyStorageBufferBind = true;
+        }
+    }
 }
 
 static void METAL_PushComputeUniformData(
@@ -3368,6 +3420,11 @@ static void METAL_INTERNAL_CleanCommandBuffer(
     commandBuffer->windowDataCount = 0;
 
     // Reset bindings
+    for (i = 0; i < MAX_VERTEX_BUFFERS; i += 1) {
+        commandBuffer->vertexBuffers[i] = nil;
+        commandBuffer->vertexBufferOffsets[i] = 0;
+    }
+    commandBuffer->vertexBufferCount = 0;
     commandBuffer->indexBuffer = NULL;
     for (i = 0; i < MAX_TEXTURE_SAMPLERS_PER_STAGE; i += 1) {
         commandBuffer->vertexSamplers[i] = nil;
@@ -3394,6 +3451,22 @@ static void METAL_INTERNAL_CleanCommandBuffer(
         commandBuffer->computeReadWriteBuffers[i] = nil;
     }
 
+    commandBuffer->needVertexBufferBind = false;
+    commandBuffer->needVertexSamplerBind = false;
+    commandBuffer->needVertexStorageBufferBind = false;
+    commandBuffer->needVertexStorageTextureBind = false;
+    SDL_zeroa(commandBuffer->needVertexUniformBufferBind);
+
+    commandBuffer->needFragmentSamplerBind = false;
+    commandBuffer->needFragmentStorageBufferBind = false;
+    commandBuffer->needFragmentStorageTextureBind = false;
+    SDL_zeroa(commandBuffer->needFragmentUniformBufferBind);
+
+    commandBuffer->needComputeSamplerBind = false;
+    commandBuffer->needComputeReadOnlyStorageBufferBind = false;
+    commandBuffer->needComputeReadOnlyStorageTextureBind = false;
+    SDL_zeroa(commandBuffer->needComputeUniformBufferBind);
+
     // The fence is now available (unless SubmitAndAcquireFence was called)
     if (commandBuffer->autoReleaseFence) {
         METAL_ReleaseFence(

+ 162 - 119
src/gpu/vulkan/SDL_gpu_vulkan.c

@@ -1035,6 +1035,11 @@ typedef struct VulkanCommandBuffer
     VkDescriptorSet computeReadWriteDescriptorSet;
     VkDescriptorSet computeUniformDescriptorSet;
 
+    VkBuffer vertexBuffers[MAX_VERTEX_BUFFERS];
+    VkDeviceSize vertexBufferOffsets[MAX_VERTEX_BUFFERS];
+    Uint32 vertexBufferCount;
+    bool needVertexBufferBind;
+
     VulkanTexture *vertexSamplerTextures[MAX_TEXTURE_SAMPLERS_PER_STAGE];
     VulkanSampler *vertexSamplers[MAX_TEXTURE_SAMPLERS_PER_STAGE];
     VulkanTexture *vertexStorageTextures[MAX_STORAGE_TEXTURES_PER_STAGE];
@@ -5027,6 +5032,7 @@ static void VULKAN_INTERNAL_BindGraphicsDescriptorSets(
     Uint32 dynamicOffsetCount = 0;
 
     if (
+        !commandBuffer->needVertexBufferBind &&
         !commandBuffer->needNewVertexResourceDescriptorSet &&
         !commandBuffer->needNewVertexUniformDescriptorSet &&
         !commandBuffer->needNewVertexUniformOffsets &&
@@ -5037,6 +5043,17 @@ static void VULKAN_INTERNAL_BindGraphicsDescriptorSets(
         return;
     }
 
+    if (commandBuffer->needVertexBufferBind && commandBuffer->vertexBufferCount > 0) {
+        renderer->vkCmdBindVertexBuffers(
+            commandBuffer->commandBuffer,
+            0,
+            commandBuffer->vertexBufferCount,
+            commandBuffer->vertexBuffers,
+            commandBuffer->vertexBufferOffsets);
+
+        commandBuffer->needVertexBufferBind = false;
+    }
+
     resourceLayout = commandBuffer->currentGraphicsPipeline->resourceLayout;
 
     if (commandBuffer->needNewVertexResourceDescriptorSet) {
@@ -7404,19 +7421,26 @@ static void VULKAN_BindVertexSamplers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
-        vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
-        vulkanCommandBuffer->vertexSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
+        VulkanSampler *sampler = (VulkanSampler *)textureSamplerBindings[i].sampler;
 
-        VULKAN_INTERNAL_TrackSampler(
-            vulkanCommandBuffer,
-            (VulkanSampler *)textureSamplerBindings[i].sampler);
+        if (vulkanCommandBuffer->vertexSamplers[firstSlot + i] != sampler) {
+            VULKAN_INTERNAL_TrackSampler(
+                vulkanCommandBuffer,
+                (VulkanSampler *)textureSamplerBindings[i].sampler);
 
-        VULKAN_INTERNAL_TrackTexture(
-            vulkanCommandBuffer,
-            textureContainer->activeTexture);
-    }
+            vulkanCommandBuffer->vertexSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
+            vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
+        }
 
-    vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
+        if (vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] != textureContainer->activeTexture) {
+            VULKAN_INTERNAL_TrackTexture(
+                vulkanCommandBuffer,
+                textureContainer->activeTexture);
+
+            vulkanCommandBuffer->vertexSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
+            vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
+        }
+    }
 }
 
 static void VULKAN_BindVertexStorageTextures(
@@ -7430,14 +7454,15 @@ static void VULKAN_BindVertexStorageTextures(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
 
-        vulkanCommandBuffer->vertexStorageTextures[firstSlot + i] = textureContainer->activeTexture;
+        if (vulkanCommandBuffer->vertexStorageTextures[firstSlot + i] != textureContainer->activeTexture) {
+            VULKAN_INTERNAL_TrackTexture(
+                vulkanCommandBuffer,
+                textureContainer->activeTexture);
 
-        VULKAN_INTERNAL_TrackTexture(
-            vulkanCommandBuffer,
-            textureContainer->activeTexture);
+            vulkanCommandBuffer->vertexStorageTextures[firstSlot + i] = textureContainer->activeTexture;
+            vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
+        }
     }
-
-    vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
 }
 
 static void VULKAN_BindVertexStorageBuffers(
@@ -7447,20 +7472,19 @@ static void VULKAN_BindVertexStorageBuffers(
     Uint32 numBindings)
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
-    VulkanBufferContainer *bufferContainer;
-    Uint32 i;
 
-    for (i = 0; i < numBindings; i += 1) {
-        bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
+    for (Uint32 i = 0; i < numBindings; i += 1) {
+        VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
 
-        vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
+        if (vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer) {
+            VULKAN_INTERNAL_TrackBuffer(
+                vulkanCommandBuffer,
+                bufferContainer->activeBuffer);
 
-        VULKAN_INTERNAL_TrackBuffer(
-            vulkanCommandBuffer,
-            bufferContainer->activeBuffer);
+            vulkanCommandBuffer->vertexStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
+            vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
+        }
     }
-
-    vulkanCommandBuffer->needNewVertexResourceDescriptorSet = true;
 }
 
 static void VULKAN_BindFragmentSamplers(
@@ -7473,19 +7497,26 @@ static void VULKAN_BindFragmentSamplers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
-        vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
-        vulkanCommandBuffer->fragmentSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
+        VulkanSampler *sampler = (VulkanSampler *)textureSamplerBindings[i].sampler;
 
-        VULKAN_INTERNAL_TrackSampler(
-            vulkanCommandBuffer,
-            (VulkanSampler *)textureSamplerBindings[i].sampler);
+        if (vulkanCommandBuffer->fragmentSamplers[firstSlot + i] != sampler) {
+            VULKAN_INTERNAL_TrackSampler(
+                vulkanCommandBuffer,
+                (VulkanSampler *)textureSamplerBindings[i].sampler);
 
-        VULKAN_INTERNAL_TrackTexture(
-            vulkanCommandBuffer,
-            textureContainer->activeTexture);
-    }
+            vulkanCommandBuffer->fragmentSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
+            vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
+        }
 
-    vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
+        if (vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] != textureContainer->activeTexture) {
+            VULKAN_INTERNAL_TrackTexture(
+                vulkanCommandBuffer,
+                textureContainer->activeTexture);
+
+            vulkanCommandBuffer->fragmentSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
+            vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
+        }
+    }
 }
 
 static void VULKAN_BindFragmentStorageTextures(
@@ -7499,15 +7530,15 @@ static void VULKAN_BindFragmentStorageTextures(
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
 
-        vulkanCommandBuffer->fragmentStorageTextures[firstSlot + i] =
-            textureContainer->activeTexture;
+        if (vulkanCommandBuffer->fragmentStorageTextures[firstSlot + i] != textureContainer->activeTexture) {
+            VULKAN_INTERNAL_TrackTexture(
+                vulkanCommandBuffer,
+                textureContainer->activeTexture);
 
-        VULKAN_INTERNAL_TrackTexture(
-            vulkanCommandBuffer,
-            textureContainer->activeTexture);
+            vulkanCommandBuffer->fragmentStorageTextures[firstSlot + i] = textureContainer->activeTexture;
+            vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
+        }
     }
-
-    vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
 }
 
 static void VULKAN_BindFragmentStorageBuffers(
@@ -7523,14 +7554,15 @@ static void VULKAN_BindFragmentStorageBuffers(
     for (i = 0; i < numBindings; i += 1) {
         bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
 
-        vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
+        if (vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer) {
+            VULKAN_INTERNAL_TrackBuffer(
+                vulkanCommandBuffer,
+                bufferContainer->activeBuffer);
 
-        VULKAN_INTERNAL_TrackBuffer(
-            vulkanCommandBuffer,
-            bufferContainer->activeBuffer);
+            vulkanCommandBuffer->fragmentStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
+            vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
+        }
     }
-
-    vulkanCommandBuffer->needNewFragmentResourceDescriptorSet = true;
 }
 
 static VulkanUniformBuffer *VULKAN_INTERNAL_AcquireUniformBufferFromPool(
@@ -7922,28 +7954,20 @@ static void VULKAN_BindVertexBuffers(
     Uint32 numBindings)
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
-    VulkanRenderer *renderer = (VulkanRenderer *)vulkanCommandBuffer->renderer;
-    VulkanBuffer *currentVulkanBuffer;
-    VkBuffer *buffers = SDL_stack_alloc(VkBuffer, numBindings);
-    VkDeviceSize *offsets = SDL_stack_alloc(VkDeviceSize, numBindings);
-    Uint32 i;
 
-    for (i = 0; i < numBindings; i += 1) {
-        currentVulkanBuffer = ((VulkanBufferContainer *)bindings[i].buffer)->activeBuffer;
-        buffers[i] = currentVulkanBuffer->buffer;
-        offsets[i] = (VkDeviceSize)bindings[i].offset;
-        VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, currentVulkanBuffer);
-    }
+    for (Uint32 i = 0; i < numBindings; i += 1) {
+        VulkanBuffer *buffer = ((VulkanBufferContainer *)bindings[i].buffer)->activeBuffer;
+        if (vulkanCommandBuffer->vertexBuffers[i] != buffer->buffer || vulkanCommandBuffer->vertexBufferOffsets[i] != bindings[i].offset) {
+            VULKAN_INTERNAL_TrackBuffer(vulkanCommandBuffer, buffer);
 
-    renderer->vkCmdBindVertexBuffers(
-        vulkanCommandBuffer->commandBuffer,
-        firstSlot,
-        numBindings,
-        buffers,
-        offsets);
+            vulkanCommandBuffer->vertexBuffers[i] = buffer->buffer;
+            vulkanCommandBuffer->vertexBufferOffsets[i] = bindings[i].offset;
+            vulkanCommandBuffer->needVertexBufferBind = true;
+        }
+    }
 
-    SDL_stack_free(buffers);
-    SDL_stack_free(offsets);
+    vulkanCommandBuffer->vertexBufferCount =
+        SDL_max(vulkanCommandBuffer->vertexBufferCount, firstSlot + numBindings);
 }
 
 static void VULKAN_BindIndexBuffer(
@@ -8045,6 +8069,10 @@ static void VULKAN_EndRenderPass(
     SDL_zeroa(vulkanCommandBuffer->resolveAttachmentSubresources);
     vulkanCommandBuffer->depthStencilAttachmentSubresource = NULL;
 
+    SDL_zeroa(vulkanCommandBuffer->vertexBuffers);
+    SDL_zeroa(vulkanCommandBuffer->vertexBufferOffsets);
+    vulkanCommandBuffer->vertexBufferCount = 0;
+
     SDL_zeroa(vulkanCommandBuffer->vertexSamplers);
     SDL_zeroa(vulkanCommandBuffer->vertexSamplerTextures);
     SDL_zeroa(vulkanCommandBuffer->vertexStorageTextures);
@@ -8148,19 +8176,26 @@ static void VULKAN_BindComputeSamplers(
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
         VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)textureSamplerBindings[i].texture;
-        vulkanCommandBuffer->computeSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
-        vulkanCommandBuffer->computeSamplers[firstSlot + i] = (VulkanSampler *)textureSamplerBindings[i].sampler;
+        VulkanSampler *sampler = (VulkanSampler *)textureSamplerBindings[i].sampler;
 
-        VULKAN_INTERNAL_TrackSampler(
-            vulkanCommandBuffer,
-            (VulkanSampler *)textureSamplerBindings[i].sampler);
+        if (vulkanCommandBuffer->computeSamplers[firstSlot + i] != sampler) {
+            VULKAN_INTERNAL_TrackSampler(
+                vulkanCommandBuffer,
+                sampler);
 
-        VULKAN_INTERNAL_TrackTexture(
-            vulkanCommandBuffer,
-            textureContainer->activeTexture);
-    }
+            vulkanCommandBuffer->computeSamplers[firstSlot + i] = sampler;
+            vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
+        }
 
-    vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
+        if (vulkanCommandBuffer->computeSamplerTextures[firstSlot + i] != textureContainer->activeTexture) {
+            VULKAN_INTERNAL_TrackTexture(
+                vulkanCommandBuffer,
+                textureContainer->activeTexture);
+
+            vulkanCommandBuffer->computeSamplerTextures[firstSlot + i] = textureContainer->activeTexture;
+            vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
+        }
+    }
 }
 
 static void VULKAN_BindComputeStorageTextures(
@@ -8173,31 +8208,34 @@ static void VULKAN_BindComputeStorageTextures(
     VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
 
     for (Uint32 i = 0; i < numBindings; i += 1) {
-        if (vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] != NULL) {
-            VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
+        VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
+
+        if (vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] != textureContainer->activeTexture) {
+            /* If a different texture as in this slot, transition it back to its default usage */
+            if (vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] != NULL) {
+                VULKAN_INTERNAL_TextureTransitionToDefaultUsage(
+                    renderer,
+                    vulkanCommandBuffer,
+                    VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
+                    vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i]);
+            }
+
+            /* Then transition the new texture and prepare it for binding */
+            VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
                 renderer,
                 vulkanCommandBuffer,
                 VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
-                vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i]);
-        }
-
-        VulkanTextureContainer *textureContainer = (VulkanTextureContainer *)storageTextures[i];
+                textureContainer->activeTexture);
 
-        vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] =
-            textureContainer->activeTexture;
 
-        VULKAN_INTERNAL_TextureTransitionFromDefaultUsage(
-            renderer,
-            vulkanCommandBuffer,
-            VULKAN_TEXTURE_USAGE_MODE_COMPUTE_STORAGE_READ,
-            textureContainer->activeTexture);
+            VULKAN_INTERNAL_TrackTexture(
+                vulkanCommandBuffer,
+                textureContainer->activeTexture);
 
-        VULKAN_INTERNAL_TrackTexture(
-            vulkanCommandBuffer,
-            textureContainer->activeTexture);
+            vulkanCommandBuffer->readOnlyComputeStorageTextures[firstSlot + i] = textureContainer->activeTexture;
+            vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
+        }
     }
-
-    vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
 }
 
 static void VULKAN_BindComputeStorageBuffers(
@@ -8208,34 +8246,35 @@ static void VULKAN_BindComputeStorageBuffers(
 {
     VulkanCommandBuffer *vulkanCommandBuffer = (VulkanCommandBuffer *)commandBuffer;
     VulkanRenderer *renderer = vulkanCommandBuffer->renderer;
-    VulkanBufferContainer *bufferContainer;
-    Uint32 i;
 
-    for (i = 0; i < numBindings; i += 1) {
-        if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] != NULL) {
-            VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
+    for (Uint32 i = 0; i < numBindings; i += 1) {
+        VulkanBufferContainer *bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
+
+        if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] != bufferContainer->activeBuffer) {
+            /* If a different buffer was in this slot, transition it back to its default usage */
+            if (vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] != NULL) {
+                VULKAN_INTERNAL_BufferTransitionToDefaultUsage(
+                    renderer,
+                    vulkanCommandBuffer,
+                    VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
+                    vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i]);
+            }
+
+            /* Then transition the new buffer and prepare it for binding */
+            VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
                 renderer,
                 vulkanCommandBuffer,
                 VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
-                vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i]);
-        }
-
-        bufferContainer = (VulkanBufferContainer *)storageBuffers[i];
-
-        vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
+                bufferContainer->activeBuffer);
 
-        VULKAN_INTERNAL_BufferTransitionFromDefaultUsage(
-            renderer,
-            vulkanCommandBuffer,
-            VULKAN_BUFFER_USAGE_MODE_COMPUTE_STORAGE_READ,
-            bufferContainer->activeBuffer);
+            VULKAN_INTERNAL_TrackBuffer(
+                vulkanCommandBuffer,
+                bufferContainer->activeBuffer);
 
-        VULKAN_INTERNAL_TrackBuffer(
-            vulkanCommandBuffer,
-            bufferContainer->activeBuffer);
+            vulkanCommandBuffer->readOnlyComputeStorageBuffers[firstSlot + i] = bufferContainer->activeBuffer;
+            vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
+        }
     }
-
-    vulkanCommandBuffer->needNewComputeReadOnlyDescriptorSet = true;
 }
 
 static void VULKAN_PushComputeUniformData(
@@ -9226,6 +9265,7 @@ static bool VULKAN_INTERNAL_AllocateCommandBuffer(
 
     // Resource bind tracking
 
+    commandBuffer->needVertexBufferBind = false;
     commandBuffer->needNewVertexResourceDescriptorSet = true;
     commandBuffer->needNewVertexUniformDescriptorSet = true;
     commandBuffer->needNewVertexUniformOffsets = true;
@@ -9419,6 +9459,7 @@ static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer(
         commandBuffer->computeUniformBuffers[i] = NULL;
     }
 
+    commandBuffer->needVertexBufferBind = false;
     commandBuffer->needNewVertexResourceDescriptorSet = true;
     commandBuffer->needNewVertexUniformDescriptorSet = true;
     commandBuffer->needNewVertexUniformOffsets = true;
@@ -9439,6 +9480,10 @@ static SDL_GPUCommandBuffer *VULKAN_AcquireCommandBuffer(
     commandBuffer->computeReadWriteDescriptorSet = VK_NULL_HANDLE;
     commandBuffer->computeUniformDescriptorSet = VK_NULL_HANDLE;
 
+    SDL_zeroa(commandBuffer->vertexBuffers);
+    SDL_zeroa(commandBuffer->vertexBufferOffsets);
+    commandBuffer->vertexBufferCount = 0;
+
     SDL_zeroa(commandBuffer->vertexSamplerTextures);
     SDL_zeroa(commandBuffer->vertexSamplers);
     SDL_zeroa(commandBuffer->vertexStorageTextures);
@@ -9868,8 +9913,6 @@ static bool VULKAN_INTERNAL_AcquireSwapchainTexture(
             VK_NULL_HANDLE,
             &swapchainImageIndex);
 
-        //if (acquireResult == VK_ERROR_OUT_OF_DATE_KHR) { SDL_Log("VULKAN SWAPCHAIN OUT OF DATE"); }
-
         if (acquireResult == VK_SUCCESS || acquireResult == VK_SUBOPTIMAL_KHR) {
             break;  // we got the next image!
         }