Przeglądaj źródła

Each application palette corresponds to one hardware palette

When the application modifies the palette, any textures that use it will automatically be updated.
Sam Lantinga 10 miesięcy temu
rodzic
commit
b65590159b

+ 92 - 21
src/render/SDL_render.c

@@ -347,6 +347,15 @@ static bool FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
     return true;
 }
 
+static bool FlushRenderCommandsIfPaletteNeeded(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    if (palette->last_command_generation == renderer->render_command_generation) {
+        // the current command queue depends on this palette, flush the queue now before it changes
+        return FlushRenderCommands(renderer);
+    }
+    return true;
+}
+
 static bool FlushRenderCommandsIfGPURenderStateNeeded(SDL_GPURenderState *state)
 {
     SDL_Renderer *renderer = state->renderer;
@@ -700,20 +709,22 @@ static bool QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, co
 static bool UpdateTexturePalette(SDL_Texture *texture)
 {
     SDL_Renderer *renderer = texture->renderer;
+    SDL_Palette *public = texture->public_palette;
 
     if (!SDL_ISPIXELFORMAT_INDEXED(texture->format)) {
         return true;
     }
 
-    if (!texture->palette) {
+    if (!public) {
         return SDL_SetError("Texture doesn't have a palette");
     }
 
-    if (texture->palette_version == texture->palette->version) {
-        return true;
-    }
-
     if (texture->native) {
+        // Keep the native texture in sync with palette updates
+        if (texture->palette_version == public->version) {
+            return true;
+        }
+
         if (!FlushRenderCommandsIfTextureNeeded(texture->native)) {
             return false;
         }
@@ -727,17 +738,25 @@ static bool UpdateTexturePalette(SDL_Texture *texture)
         if (!result) {
             return false;
         }
-    } else {
-        if (!FlushRenderCommandsIfTextureNeeded(texture)) {
+        texture->palette_version = public->version;
+        return true;
+    }
+
+    SDL_TexturePalette *palette = texture->palette;
+    if (palette->version != public->version) {
+        // Keep the native palette in sync with palette updates
+        if (!FlushRenderCommandsIfPaletteNeeded(renderer, palette)) {
             return false;
         }
 
-        if (!renderer->UpdateTexturePalette(renderer, texture)) {
+        if (!renderer->UpdatePalette(renderer, palette, public->ncolors, public->colors)) {
             return false;
         }
+
+        palette->version = public->version;
     }
 
-    texture->palette_version = texture->palette->version;
+    palette->last_command_generation = renderer->render_command_generation;
     return true;
 }
 
@@ -1143,6 +1162,11 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
     UpdatePixelClipRect(renderer, &renderer->main_view);
     UpdateMainViewDimensions(renderer);
 
+    renderer->palettes = SDL_CreateHashTable(0, false, SDL_HashPointer, SDL_KeyMatchPointer, SDL_DestroyHashValue, NULL);
+    if (!renderer->palettes) {
+        goto error;
+    }
+
     // new textures start at zero, so we start at 1 so first render doesn't flush by accident.
     renderer->render_command_generation = 1;
 
@@ -1656,7 +1680,7 @@ static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, S
     bool direct_update;
 
     if (surface->format == texture->format &&
-        surface->palette == texture->palette &&
+        surface->palette == texture->public_palette &&
         SDL_GetSurfaceColorspace(surface) == texture->colorspace) {
         if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
             /* Surface and Renderer formats are identical.
@@ -1682,7 +1706,7 @@ static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, S
         }
     } else {
         // Set up a destination surface for the texture update
-        SDL_Surface *temp = SDL_ConvertSurfaceAndColorspace(surface, texture->format, texture->palette, texture->colorspace, SDL_GetSurfaceProperties(surface));
+        SDL_Surface *temp = SDL_ConvertSurfaceAndColorspace(surface, texture->format, texture->public_palette, texture->colorspace, SDL_GetSurfaceProperties(surface));
         if (temp) {
             SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
             SDL_DestroySurface(temp);
@@ -1901,16 +1925,56 @@ bool SDL_SetTexturePalette(SDL_Texture *texture, SDL_Palette *palette)
         return SDL_SetError("SDL_SetSurfacePalette() passed a palette that doesn't match the surface format");
     }
 
-    if (palette != texture->palette) {
-        if (texture->palette) {
-            SDL_DestroyPalette(texture->palette);
+    if (palette != texture->public_palette) {
+        SDL_Renderer *renderer = texture->renderer;
+
+        if (texture->public_palette) {
+            SDL_DestroyPalette(texture->public_palette);
+
+            if (!texture->native) {
+                // Clean up the texture palette
+                --texture->palette->refcount;
+                if (texture->palette->refcount == 0) {
+                    renderer->DestroyPalette(renderer, texture->palette);
+                    SDL_RemoveFromHashTable(renderer->palettes, texture->public_palette);
+                }
+                texture->palette = NULL;
+            }
         }
 
-        texture->palette = palette;
+        texture->public_palette = palette;
         texture->palette_version = 0;
 
-        if (texture->palette) {
-            ++texture->palette->refcount;
+        if (texture->public_palette) {
+            ++texture->public_palette->refcount;
+
+            if (!texture->native) {
+                if (SDL_FindInHashTable(renderer->palettes, palette, (const void **)&texture->palette)) {
+                    ++texture->palette->refcount;
+                } else {
+                    SDL_TexturePalette *texture_palette = (SDL_TexturePalette *)SDL_calloc(1, sizeof(*texture_palette));
+                    if (!texture_palette) {
+                        SDL_SetTexturePalette(texture, NULL);
+                        return false;
+                    }
+                    if (!renderer->CreatePalette(renderer, texture_palette)) {
+                        renderer->DestroyPalette(renderer, texture_palette);
+                        SDL_SetTexturePalette(texture, NULL);
+                        return false;
+                    }
+                    texture->palette = texture_palette;
+                    texture->palette->refcount = 1;
+
+                    if (!SDL_InsertIntoHashTable(renderer->palettes, palette, texture->palette, false)) {
+                        SDL_SetTexturePalette(texture, NULL);
+                        return false;
+                    }
+                }
+            }
+
+            if (!texture->native && renderer->ChangeTexturePalette) {
+                renderer->ChangeTexturePalette(renderer, texture);
+            }
         }
 
         if (texture->palette_surface) {
@@ -1924,7 +1988,7 @@ SDL_Palette *SDL_GetTexturePalette(SDL_Texture *texture)
 {
     CHECK_TEXTURE_MAGIC(texture, NULL);
 
-    return texture->palette;
+    return texture->public_palette;
 }
 
 bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b)
@@ -2602,8 +2666,8 @@ bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Su
         SDL_UnlockTexture(texture);
         return false;
     }
-    if (texture->palette) {
-        SDL_SetSurfacePalette(texture->locked_surface, texture->palette);
+    if (texture->public_palette) {
+        SDL_SetSurfacePalette(texture->locked_surface, texture->public_palette);
     }
 
     *surface = texture->locked_surface;
@@ -5513,7 +5577,7 @@ static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
 {
     SDL_Renderer *renderer;
 
-    if (texture->palette) {
+    if (texture->public_palette) {
         SDL_SetTexturePalette(texture, NULL);
     }
 
@@ -5634,6 +5698,13 @@ void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer)
         SDL_assert(tex != renderer->textures); // satisfy static analysis.
     }
 
+    // Free palette cache, which should be empty now
+    if (renderer->palettes) {
+        SDL_assert(SDL_HashTableEmpty(renderer->palettes));
+        SDL_DestroyHashTable(renderer->palettes);
+        renderer->palettes = NULL;
+    }
+
     // Clean up renderer-specific resources
     if (renderer->DestroyRenderer) {
         renderer->DestroyRenderer(renderer);

+ 18 - 2
src/render/SDL_sysrender.h

@@ -70,6 +70,15 @@ typedef struct SDL_RenderViewState
     SDL_FPoint current_scale;  // this is just `scale * logical_scale`, precalculated, since we use it a lot.
 } SDL_RenderViewState;
 
+// Define the SDL texture palette structure
+typedef struct SDL_TexturePalette
+{
+    int refcount;
+    Uint32 version;
+    Uint32 last_command_generation; // last command queue generation this palette was in.
+    void *internal;             // Driver specific palette representation
+} SDL_TexturePalette;
+
 // Define the SDL texture structure
 struct SDL_Texture
 {
@@ -90,7 +99,8 @@ struct SDL_Texture
     SDL_ScaleMode scaleMode;    // The texture scale mode
     SDL_FColor color;           // Texture modulation values
     SDL_RenderViewState view;   // Target texture view state
-    SDL_Palette *palette;
+    SDL_Palette *public_palette;
+    SDL_TexturePalette *palette;
     Uint32 palette_version;
     SDL_Surface *palette_surface;
 
@@ -235,7 +245,10 @@ struct SDL_Renderer
 
     void (*InvalidateCachedState)(SDL_Renderer *renderer);
     bool (*RunCommandQueue)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize);
-    bool (*UpdateTexturePalette)(SDL_Renderer *renderer, SDL_Texture *texture);
+    bool (*CreatePalette)(SDL_Renderer *renderer, SDL_TexturePalette *palette);
+    bool (*UpdatePalette)(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors);
+    void (*DestroyPalette)(SDL_Renderer *renderer, SDL_TexturePalette *palette);
+    bool (*ChangeTexturePalette)(SDL_Renderer *renderer, SDL_Texture *texture);
     bool (*UpdateTexture)(SDL_Renderer *renderer, SDL_Texture *texture,
                          const SDL_Rect *rect, const void *pixels,
                          int pitch);
@@ -301,6 +314,9 @@ struct SDL_Renderer
     SDL_Texture *target;
     SDL_Mutex *target_mutex;
 
+    // The list of palettes
+    SDL_HashTable *palettes;
+
     SDL_Colorspace output_colorspace;
     float SDR_white_point;
     float HDR_headroom;

+ 135 - 80
src/render/direct3d/SDL_render_d3d.c

@@ -48,6 +48,26 @@ typedef struct
     const float *shader_params;
 } D3D_DrawStateCache;
 
+typedef struct
+{
+    bool dirty;
+    int w, h;
+    DWORD usage;
+    Uint32 format;
+    D3DFORMAT d3dfmt;
+    IDirect3DTexture9 *texture;
+    IDirect3DTexture9 *staging;
+} D3D_TextureRep;
+
+struct D3D_PaletteData
+{
+    D3D_TextureRep texture;
+
+    struct D3D_PaletteData *prev;
+    struct D3D_PaletteData *next;
+};
+typedef struct D3D_PaletteData D3D_PaletteData;
+
 // Direct3D renderer implementation
 
 typedef struct
@@ -74,23 +94,12 @@ typedef struct
     int currentVertexBuffer;
     bool reportedVboProblem;
     D3D_DrawStateCache drawstate;
+    D3D_PaletteData *palettes;
 } D3D_RenderData;
 
-typedef struct
-{
-    bool dirty;
-    int w, h;
-    DWORD usage;
-    Uint32 format;
-    D3DFORMAT d3dfmt;
-    IDirect3DTexture9 *texture;
-    IDirect3DTexture9 *staging;
-} D3D_TextureRep;
-
 typedef struct
 {
     D3D_TextureRep texture;
-    D3D_TextureRep palette;
     D3D9_Shader shader;
     const float *shader_params;
 
@@ -532,6 +541,99 @@ static void D3D_DestroyTextureRep(D3D_TextureRep *texture)
     }
 }
 
+static bool D3D_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
+    D3D_PaletteData *palettedata = (D3D_PaletteData *)SDL_calloc(1, sizeof(*palettedata));
+    if (!palettedata) {
+        return false;
+    }
+    palette->internal = palettedata;
+
+    if (!D3D_CreateTextureRep(data->device, &palettedata->texture, 0, SDL_PIXELFORMAT_ARGB8888, D3DFMT_A8R8G8B8, 256, 1)) {
+        SDL_free(palettedata);
+        return false;
+    }
+
+    // Keep a reference to the palette so we can restore the texture if we lose the D3D device
+    if (data->palettes) {
+        palettedata->next = data->palettes;
+        data->palettes->prev = palettedata;
+    }
+    data->palettes = palettedata;
+    return true;
+}
+
+static bool D3D_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
+    D3D_PaletteData *palettedata = (D3D_PaletteData *)palette->internal;
+    bool retval;
+
+    Uint32 *entries = SDL_stack_alloc(Uint32, ncolors);
+    if (!entries) {
+        return false;
+    }
+    for (int i = 0; i < ncolors; ++i) {
+        entries[i] = (colors[i].a << 24) | (colors[i].r << 16) | (colors[i].g << 8) | colors[i].b;
+    }
+    retval = D3D_UpdateTextureRep(data->device, &palettedata->texture, 0, 0, ncolors, 1, entries, ncolors * sizeof(*entries));
+    SDL_stack_free(entries);
+    return retval;
+}
+
+static void D3D_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
+    D3D_PaletteData *palettedata = (D3D_PaletteData *)palette->internal;
+
+    if (palettedata) {
+        D3D_DestroyTextureRep(&palettedata->texture);
+
+        if (data->palettes == palettedata) {
+            data->palettes = palettedata->next;
+        } else if (palettedata->prev) {
+            palettedata->prev->next = palettedata->next;
+        }
+        if (palettedata->next) {
+            palettedata->next->prev = palettedata->prev;
+        }
+        SDL_free(palettedata);
+    }
+}
+
+static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
+                             const SDL_Rect *rect, const void *pixels, int pitch)
+{
+    D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
+    D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
+
+    if (!texturedata) {
+        return SDL_SetError("Texture is not currently available");
+    }
+
+    if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) {
+        return false;
+    }
+#ifdef SDL_HAVE_YUV
+    if (texturedata->yuv) {
+        // Skip to the correct offset into the next texture
+        pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
+
+        if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
+            return false;
+        }
+
+        // Skip to the correct offset into the next texture
+        pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
+        if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
+            return false;
+        }
+    }
+#endif
+    return true;
+}
+
 static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
@@ -555,10 +657,6 @@ static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         return false;
     }
     if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        if (!D3D_CreateTextureRep(data->device, &texturedata->palette, usage, SDL_PIXELFORMAT_ARGB8888, D3DFMT_A8R8G8B8, 256, 1)) {
-            return false;
-        }
-
         texturedata->shader = SHADER_PALETTE;
     }
 #ifdef SDL_HAVE_YUV
@@ -596,12 +694,6 @@ static bool D3D_RecreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
     if (!D3D_RecreateTextureRep(data->device, &texturedata->texture)) {
         return false;
     }
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        if (!D3D_RecreateTextureRep(data->device, &texturedata->palette)) {
-            return false;
-        }
-        texture->palette_version = 0;
-    }
 #ifdef SDL_HAVE_YUV
     if (texturedata->yuv) {
         if (!D3D_RecreateTextureRep(data->device, &texturedata->utexture)) {
@@ -616,56 +708,6 @@ static bool D3D_RecreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
     return true;
 }
 
-static bool D3D_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
-    D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
-    const int ncolors = texture->palette->ncolors;
-    const SDL_Color *colors = texture->palette->colors;
-    Uint32 palette[256];
-
-    if (!texturedata) {
-        return SDL_SetError("Texture is not currently available");
-    }
-
-    for (int i = 0; i < ncolors; ++i) {
-        palette[i] = (colors[i].a << 24) | (colors[i].r << 16) | (colors[i].g << 8) | colors[i].b;
-    }
-    return D3D_UpdateTextureRep(data->device, &texturedata->palette, 0, 0, 256, 1, palette, sizeof(palette));
-}
-
-static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
-                             const SDL_Rect *rect, const void *pixels, int pitch)
-{
-    D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
-    D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
-
-    if (!texturedata) {
-        return SDL_SetError("Texture is not currently available");
-    }
-
-    if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) {
-        return false;
-    }
-#ifdef SDL_HAVE_YUV
-    if (texturedata->yuv) {
-        // Skip to the correct offset into the next texture
-        pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
-
-        if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
-            return false;
-        }
-
-        // Skip to the correct offset into the next texture
-        pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2));
-        if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) {
-            return false;
-        }
-    }
-#endif
-    return true;
-}
-
 #ifdef SDL_HAVE_YUV
 static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
                                 const SDL_Rect *rect,
@@ -1013,8 +1055,9 @@ static bool SetupTextureState(D3D_RenderData *data, SDL_Texture *texture, D3D9_S
     if (!BindTextureRep(data->device, &texturedata->texture, 0)) {
         return false;
     }
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        if (!BindTextureRep(data->device, &texturedata->palette, 1)) {
+    if (texture->palette) {
+        D3D_PaletteData *palette = (D3D_PaletteData *)texture->palette->internal;
+        if (!BindTextureRep(data->device, &palette->texture, 1)) {
             return false;
         }
     }
@@ -1046,8 +1089,8 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd)
         if (!texture) {
             IDirect3DDevice9_SetTexture(data->device, 0, NULL);
         }
-        if ((!newtexturedata || (texture->format != SDL_PIXELFORMAT_INDEX8)) &&
-            (oldtexturedata && (data->drawstate.texture->format == SDL_PIXELFORMAT_INDEX8))) {
+        if ((!newtexturedata || !texture->palette) &&
+            (oldtexturedata && (data->drawstate.texture->palette))) {
             IDirect3DDevice9_SetTexture(data->device, 1, NULL);
         }
 #ifdef SDL_HAVE_YUV
@@ -1086,8 +1129,9 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd)
         D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
         if (texturedata) {
             UpdateDirtyTexture(data->device, &texturedata->texture);
-            if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-                UpdateDirtyTexture(data->device, &texturedata->palette);
+            if (texture->palette) {
+                D3D_PaletteData *palettedata = (D3D_PaletteData *)texture->palette->internal;
+                UpdateDirtyTexture(data->device, &palettedata->texture);
             }
 #ifdef SDL_HAVE_YUV
             if (texturedata->yuv) {
@@ -1488,7 +1532,7 @@ static void D3D_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
         renderdata->drawstate.shader_params = NULL;
         IDirect3DDevice9_SetPixelShader(renderdata->device, NULL);
         IDirect3DDevice9_SetTexture(renderdata->device, 0, NULL);
-        if (texture->format == SDL_PIXELFORMAT_INDEX8) {
+        if (texture->palette) {
             IDirect3DDevice9_SetTexture(renderdata->device, 1, NULL);
         }
 #ifdef SDL_HAVE_YUV
@@ -1520,6 +1564,9 @@ static void D3D_DestroyRenderer(SDL_Renderer *renderer)
     if (data) {
         int i;
 
+        // Make sure the palettes have been freed
+        SDL_assert(!data->palettes);
+
         // Release the render target
         if (data->defaultRenderTarget) {
             IDirect3DSurface9_Release(data->defaultRenderTarget);
@@ -1560,6 +1607,7 @@ static bool D3D_Reset(SDL_Renderer *renderer)
     const Float4X4 d3dmatrix = MatrixIdentity();
     HRESULT result;
     SDL_Texture *texture;
+    D3D_PaletteData *palette;
     int i;
 
     // Cancel any scene that we've started
@@ -1587,6 +1635,11 @@ static bool D3D_Reset(SDL_Renderer *renderer)
         }
     }
 
+    // Release all palettes
+    for (palette = data->palettes; palette; palette = palette->next) {
+        D3D_RecreateTextureRep(data->device, &palette->texture);
+    }
+
     // Release all vertex buffers
     for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
         if (data->vertexBuffers[i]) {
@@ -1711,8 +1764,10 @@ static bool D3D_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
 
     renderer->WindowEvent = D3D_WindowEvent;
     renderer->SupportsBlendMode = D3D_SupportsBlendMode;
+    renderer->CreatePalette = D3D_CreatePalette;
+    renderer->UpdatePalette = D3D_UpdatePalette;
+    renderer->DestroyPalette = D3D_DestroyPalette;
     renderer->CreateTexture = D3D_CreateTexture;
-    renderer->UpdateTexturePalette = D3D_UpdateTexturePalette;
     renderer->UpdateTexture = D3D_UpdateTexture;
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = D3D_UpdateTextureYUV;

+ 83 - 49
src/render/direct3d11/SDL_render_d3d11.c

@@ -114,6 +114,13 @@ typedef struct
     SDL_FColor color;
 } D3D11_VertexPositionColor;
 
+// Per-palette data
+typedef struct
+{
+    ID3D11Texture2D *texture;
+    ID3D11ShaderResourceView *resourceView;
+} D3D11_PaletteData;
+
 // Per-texture data
 typedef struct
 {
@@ -121,8 +128,6 @@ typedef struct
     ID3D11Texture2D *mainTexture;
     ID3D11ShaderResourceView *mainTextureResourceView;
     ID3D11RenderTargetView *mainTextureRenderTargetView;
-    ID3D11Texture2D *paletteTexture;
-    ID3D11ShaderResourceView *paletteTextureResourceView;
     ID3D11Texture2D *stagingTexture;
     int lockedTexturePositionX;
     int lockedTexturePositionY;
@@ -232,6 +237,8 @@ static const GUID SDL_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe
 #pragma GCC diagnostic pop
 #endif
 
+static bool D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Texture2D *texture, int bpp, int x, int y, int w, int h, const void *pixels, int pitch);
+
 SDL_PixelFormat D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat)
 {
     switch (dxgiFormat) {
@@ -1179,6 +1186,73 @@ static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D11T
     return true;
 }
 
+static bool D3D11_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
+    D3D11_PaletteData *palettedata = (D3D11_PaletteData *)SDL_calloc(1, sizeof(*palettedata));
+    if (!palettedata) {
+        return false;
+    }
+    palette->internal = palettedata;
+
+    if (!data->d3dDevice) {
+        return SDL_SetError("Device lost and couldn't be recovered");
+    }
+
+    D3D11_TEXTURE2D_DESC textureDesc;
+    SDL_zero(textureDesc);
+    textureDesc.Width = 256;
+    textureDesc.Height = 1;
+    textureDesc.MipLevels = 1;
+    textureDesc.ArraySize = 1;
+    textureDesc.Format = SDLPixelFormatToDXGITextureFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace);
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.MiscFlags = 0;
+    textureDesc.Usage = D3D11_USAGE_DEFAULT;
+    textureDesc.CPUAccessFlags = 0;
+    textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
+
+    HRESULT result = ID3D11Device_CreateTexture2D(data->d3dDevice, &textureDesc, NULL, &palettedata->texture);
+    if (FAILED(result)) {
+        return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
+    }
+
+    D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
+    SDL_zero(resourceViewDesc);
+    resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace);
+    resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
+    resourceViewDesc.Texture2D.MostDetailedMip = 0;
+    resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels;
+    result = ID3D11Device_CreateShaderResourceView(data->d3dDevice,
+                                                   (ID3D11Resource *)palettedata->texture,
+                                                   &resourceViewDesc,
+                                                   &palettedata->resourceView);
+    if (FAILED(result)) {
+        return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result);
+    }
+    return true;
+}
+
+static bool D3D11_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal;
+    D3D11_PaletteData *palettedata = (D3D11_PaletteData *)palette->internal;
+
+    return D3D11_UpdateTextureInternal(data, palettedata->texture, 4, 0, 0, ncolors, 1, colors, ncolors * sizeof(*colors));
+}
+
+static void D3D11_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    D3D11_PaletteData *palettedata = (D3D11_PaletteData *)palette->internal;
+
+    if (palettedata) {
+        SAFE_RELEASE(palettedata->texture);
+        SAFE_RELEASE(palettedata->resourceView);
+        SDL_free(palettedata);
+    }
+}
+
 static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
@@ -1252,24 +1326,6 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
     }
     SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER, textureData->mainTexture);
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        textureDesc.Width = 256;
-        textureDesc.Height = 1;
-        if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
-            textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
-        } else {
-            textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
-        }
-
-        result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice,
-                                              &textureDesc,
-                                              NULL,
-                                              &textureData->paletteTexture);
-        if (FAILED(result)) {
-            return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result);
-        }
-    }
-
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
         texture->format == SDL_PIXELFORMAT_IYUV) {
@@ -1345,17 +1401,6 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
         return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result);
     }
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        resourceViewDesc.Format = textureDesc.Format;
-        result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice,
-                                                       (ID3D11Resource *)textureData->paletteTexture,
-                                                       &resourceViewDesc,
-                                                       &textureData->paletteTextureResourceView);
-        if (FAILED(result)) {
-            return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result);
-        }
-    }
-
 #ifdef SDL_HAVE_YUV
     if (textureData->yuv) {
         result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice,
@@ -1424,8 +1469,6 @@ static void D3D11_DestroyTexture(SDL_Renderer *renderer,
     SAFE_RELEASE(data->mainTexture);
     SAFE_RELEASE(data->mainTextureResourceView);
     SAFE_RELEASE(data->mainTextureRenderTargetView);
-    SAFE_RELEASE(data->paletteTexture);
-    SAFE_RELEASE(data->paletteTextureResourceView);
     SAFE_RELEASE(data->stagingTexture);
 #ifdef SDL_HAVE_YUV
     SAFE_RELEASE(data->mainTextureU);
@@ -1542,19 +1585,6 @@ static bool D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Te
     return true;
 }
 
-static bool D3D11_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
-    D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal;
-    SDL_Palette *palette = texture->palette;
-
-    if (!textureData) {
-        return SDL_SetError("Texture is not currently available");
-    }
-
-    return D3D11_UpdateTextureInternal(rendererData, textureData->paletteTexture, 4, 0, 0, palette->ncolors, 1, palette->colors, palette->ncolors * sizeof(*palette->colors));
-}
-
 #ifdef SDL_HAVE_YUV
 static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
                                  const SDL_Rect *rect,
@@ -2491,8 +2521,10 @@ static bool D3D11_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *
     }
     ++numShaderSamplers;
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        shaderResources[numShaderResources++] = textureData->paletteTextureResourceView;
+    if (texture->palette) {
+        D3D11_PaletteData *palette = (D3D11_PaletteData *)texture->palette->internal;
+
+        shaderResources[numShaderResources++] = palette->resourceView;
 
         shaderSamplers[numShaderSamplers] = D3D11_GetSamplerState(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
         if (!shaderSamplers[numShaderSamplers]) {
@@ -2846,8 +2878,10 @@ static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
 
     renderer->WindowEvent = D3D11_WindowEvent;
     renderer->SupportsBlendMode = D3D11_SupportsBlendMode;
+    renderer->CreatePalette = D3D11_CreatePalette;
+    renderer->UpdatePalette = D3D11_UpdatePalette;
+    renderer->DestroyPalette = D3D11_DestroyPalette;
     renderer->CreateTexture = D3D11_CreateTexture;
-    renderer->UpdateTexturePalette = D3D11_UpdateTexturePalette;
     renderer->UpdateTexture = D3D11_UpdateTexture;
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = D3D11_UpdateTextureYUV;

+ 102 - 62
src/render/direct3d12/SDL_render_d3d12.c

@@ -109,6 +109,15 @@ typedef struct
     SDL_FColor color;
 } D3D12_VertexPositionColor;
 
+// Per-palette data
+typedef struct
+{
+    ID3D12Resource *texture;
+    D3D12_CPU_DESCRIPTOR_HANDLE resourceView;
+    D3D12_RESOURCE_STATES resourceState;
+    SIZE_T SRVIndex;
+} D3D12_PaletteData;
+
 // Per-texture data
 typedef struct
 {
@@ -119,10 +128,6 @@ typedef struct
     SIZE_T mainSRVIndex;
     D3D12_CPU_DESCRIPTOR_HANDLE mainTextureRenderTargetView;
     DXGI_FORMAT mainTextureFormat;
-    ID3D12Resource *paletteTexture;
-    D3D12_CPU_DESCRIPTOR_HANDLE paletteTextureResourceView;
-    D3D12_RESOURCE_STATES paletteResourceState;
-    SIZE_T paletteSRVIndex;
     ID3D12Resource *stagingBuffer;
     D3D12_RESOURCE_STATES stagingResourceState;
     const float *YCbCr_matrix;
@@ -289,6 +294,8 @@ static const GUID SDL_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9
 #pragma GCC diagnostic pop
 #endif
 
+static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Resource *texture, int plane, int x, int y, int w, int h, const void *pixels, int pitch, D3D12_RESOURCE_STATES *resourceState);
+
 static UINT D3D12_Align(UINT location, UINT alignment)
 {
     return (location + (alignment - 1)) & ~(alignment - 1);
@@ -1537,6 +1544,88 @@ static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D12R
     return true;
 }
 
+static bool D3D12_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
+    D3D12_PaletteData *palettedata = (D3D12_PaletteData *)SDL_calloc(1, sizeof(*palettedata));
+    if (!palettedata) {
+        return false;
+    }
+    palette->internal = palettedata;
+
+    if (!data->d3dDevice) {
+        return SDL_SetError("Device lost and couldn't be recovered");
+    }
+
+    D3D12_RESOURCE_DESC textureDesc;
+    SDL_zero(textureDesc);
+    textureDesc.Width = 256;
+    textureDesc.Height = 1;
+    textureDesc.MipLevels = 1;
+    textureDesc.DepthOrArraySize = 1;
+    textureDesc.Format = SDLPixelFormatToDXGITextureFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace);
+    textureDesc.SampleDesc.Count = 1;
+    textureDesc.SampleDesc.Quality = 0;
+    textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+    textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+    D3D12_HEAP_PROPERTIES heapProps;
+    SDL_zero(heapProps);
+    heapProps.Type = D3D12_HEAP_TYPE_DEFAULT;
+    heapProps.CreationNodeMask = 1;
+    heapProps.VisibleNodeMask = 1;
+
+    HRESULT result = ID3D12Device1_CreateCommittedResource(data->d3dDevice,
+                      &heapProps,
+                      D3D12_HEAP_FLAG_NONE,
+                      &textureDesc,
+                      D3D12_RESOURCE_STATE_COPY_DEST,
+                      NULL,
+                      D3D_GUID(SDL_IID_ID3D12Resource),
+                      (void **)&palettedata->texture);
+    if (FAILED(result)) {
+        return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [texture]"), result);
+    }
+    palettedata->resourceState = D3D12_RESOURCE_STATE_COPY_DEST;
+
+    D3D12_SHADER_RESOURCE_VIEW_DESC resourceViewDesc;
+    SDL_zero(resourceViewDesc);
+    resourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
+    resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace);
+    resourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
+    resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels;
+
+    D3D_CALL_RET(data->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &palettedata->resourceView);
+    palettedata->SRVIndex = D3D12_GetAvailableSRVIndex(renderer);
+    palettedata->resourceView.ptr += palettedata->SRVIndex * data->srvDescriptorSize;
+
+    ID3D12Device1_CreateShaderResourceView(data->d3dDevice,
+             palettedata->texture,
+             &resourceViewDesc,
+             palettedata->resourceView);
+
+    return true;
+}
+
+static bool D3D12_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal;
+    D3D12_PaletteData *palettedata = (D3D12_PaletteData *)palette->internal;
+
+    return D3D12_UpdateTextureInternal(data, palettedata->texture, 0, 0, 0, ncolors, 1, colors, ncolors * sizeof(*colors), &palettedata->resourceState);
+}
+
+static void D3D12_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    D3D12_PaletteData *palettedata = (D3D12_PaletteData *)palette->internal;
+
+    if (palettedata) {
+        D3D_SAFE_RELEASE(palettedata->texture);
+        D3D12_FreeSRVIndex(renderer, palettedata->SRVIndex);
+        SDL_free(palettedata);
+    }
+}
+
 static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
@@ -1612,29 +1701,6 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
     textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST;
     SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER, textureData->mainTexture);
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        textureDesc.Width = 256;
-        textureDesc.Height = 1;
-        if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
-            textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
-        } else {
-            textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
-        }
-
-        result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice,
-                          &heapProps,
-                          D3D12_HEAP_FLAG_NONE,
-                          &textureDesc,
-                          D3D12_RESOURCE_STATE_COPY_DEST,
-                          NULL,
-                          D3D_GUID(SDL_IID_ID3D12Resource),
-                          (void **)&textureData->paletteTexture);
-        if (FAILED(result)) {
-            return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [texture]"), result);
-        }
-        textureData->paletteResourceState = D3D12_RESOURCE_STATE_COPY_DEST;
-    }
-
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
         texture->format == SDL_PIXELFORMAT_IYUV) {
@@ -1723,19 +1789,6 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
              &resourceViewDesc,
              textureData->mainTextureResourceView);
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        resourceViewDesc.Format = textureDesc.Format;
-
-        D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->paletteTextureResourceView);
-        textureData->paletteSRVIndex = D3D12_GetAvailableSRVIndex(renderer);
-        textureData->paletteTextureResourceView.ptr += textureData->paletteSRVIndex * rendererData->srvDescriptorSize;
-
-        ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice,
-                 textureData->paletteTexture,
-                 &resourceViewDesc,
-                 textureData->paletteTextureResourceView);
-    }
-
 #ifdef SDL_HAVE_YUV
     if (textureData->yuv) {
         D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewU);
@@ -1811,10 +1864,6 @@ static void D3D12_DestroyTexture(SDL_Renderer *renderer,
     D3D_SAFE_RELEASE(textureData->mainTexture);
     D3D_SAFE_RELEASE(textureData->stagingBuffer);
     D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndex);
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        D3D_SAFE_RELEASE(textureData->paletteTexture);
-        D3D12_FreeSRVIndex(renderer, textureData->paletteSRVIndex);
-    }
 #ifdef SDL_HAVE_YUV
     D3D_SAFE_RELEASE(textureData->mainTextureU);
     D3D_SAFE_RELEASE(textureData->mainTextureV);
@@ -1969,19 +2018,6 @@ static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Re
     return true;
 }
 
-static bool D3D12_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
-    D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal;
-    SDL_Palette *palette = texture->palette;
-
-    if (!textureData) {
-        return SDL_SetError("Texture is not currently available");
-    }
-
-    return D3D12_UpdateTextureInternal(rendererData, textureData->paletteTexture, 0, 0, 0, palette->ncolors, 1, palette->colors, palette->ncolors * sizeof(*palette->colors), &textureData->paletteResourceState);
-}
-
 static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                                const SDL_Rect *rect, const void *srcPixels,
                                int srcPitch)
@@ -2902,10 +2938,12 @@ static bool D3D12_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *
     }
     shaderSamplers[numShaderSamplers++] = *textureSampler;
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        D3D12_TransitionResource(rendererData, textureData->paletteTexture, textureData->paletteResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
-        textureData->paletteResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
-        shaderResources[numShaderResources++] = textureData->paletteTextureResourceView;
+    if (texture->palette) {
+        D3D12_PaletteData *palette = (D3D12_PaletteData *)texture->palette->internal;
+
+        D3D12_TransitionResource(rendererData, palette->texture, palette->resourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
+        palette->resourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
+        shaderResources[numShaderResources++] = palette->resourceView;
 
         textureSampler = D3D12_GetSamplerState(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
         if (!textureSampler) {
@@ -3366,8 +3404,10 @@ bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Proper
 
     renderer->WindowEvent = D3D12_WindowEvent;
     renderer->SupportsBlendMode = D3D12_SupportsBlendMode;
+    renderer->CreatePalette = D3D12_CreatePalette;
+    renderer->UpdatePalette = D3D12_UpdatePalette;
+    renderer->DestroyPalette = D3D12_DestroyPalette;
     renderer->CreateTexture = D3D12_CreateTexture;
-    renderer->UpdateTexturePalette = D3D12_UpdateTexturePalette;
     renderer->UpdateTexture = D3D12_UpdateTexture;
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = D3D12_UpdateTextureYUV;

+ 94 - 61
src/render/gpu/SDL_render_gpu.c

@@ -86,10 +86,14 @@ typedef struct GPU_RenderData
     SDL_GPUSampler *samplers[RENDER_SAMPLER_COUNT];
 } GPU_RenderData;
 
+typedef struct GPU_PaletteData
+{
+    SDL_GPUTexture *texture;
+} GPU_PaletteData;
+
 typedef struct GPU_TextureData
 {
     SDL_GPUTexture *texture;
-    SDL_GPUTexture *palette;
     SDL_GPUTextureFormat format;
     GPU_FragmentShaderID shader;
     void *pixels;
@@ -118,6 +122,88 @@ static bool GPU_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMod
     return true;
 }
 
+static bool GPU_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    GPU_RenderData *data = (GPU_RenderData *)renderer->internal;
+    GPU_PaletteData *palettedata = (GPU_PaletteData *)SDL_calloc(1, sizeof(*palettedata));
+    if (!palettedata) {
+        return false;
+    }
+    palette->internal = palettedata;
+
+    SDL_GPUTextureCreateInfo tci;
+    SDL_zero(tci);
+    tci.format = SDL_GetGPUTextureFormatFromPixelFormat(SDL_PIXELFORMAT_RGBA32);
+    tci.layer_count_or_depth = 1;
+    tci.num_levels = 1;
+    tci.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;
+    tci.width = 256;
+    tci.height = 1;
+    tci.sample_count = SDL_GPU_SAMPLECOUNT_1;
+
+    palettedata->texture = SDL_CreateGPUTexture(data->device, &tci);
+    if (!palettedata->texture) {
+        return false;
+    }
+    return true;
+}
+
+static bool GPU_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    GPU_RenderData *data = (GPU_RenderData *)renderer->internal;
+    GPU_PaletteData *palettedata = (GPU_PaletteData *)palette->internal;
+    const Uint32 data_size = ncolors * sizeof(*colors);
+
+    SDL_GPUTransferBufferCreateInfo tbci;
+    SDL_zero(tbci);
+    tbci.size = data_size;
+    tbci.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
+
+    SDL_GPUTransferBuffer *tbuf = SDL_CreateGPUTransferBuffer(data->device, &tbci);
+    if (tbuf == NULL) {
+        return false;
+    }
+
+    Uint8 *output = SDL_MapGPUTransferBuffer(data->device, tbuf, false);
+    SDL_memcpy(output, colors, data_size);
+    SDL_UnmapGPUTransferBuffer(data->device, tbuf);
+
+    SDL_GPUCommandBuffer *cbuf = data->state.command_buffer;
+    SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf);
+
+    SDL_GPUTextureTransferInfo tex_src;
+    SDL_zero(tex_src);
+    tex_src.transfer_buffer = tbuf;
+    tex_src.rows_per_layer = 1;
+    tex_src.pixels_per_row = ncolors;
+
+    SDL_GPUTextureRegion tex_dst;
+    SDL_zero(tex_dst);
+    tex_dst.texture = palettedata->texture;
+    tex_dst.x = 0;
+    tex_dst.y = 0;
+    tex_dst.w = ncolors;
+    tex_dst.h = 1;
+    tex_dst.d = 1;
+
+    SDL_UploadToGPUTexture(cpass, &tex_src, &tex_dst, false);
+    SDL_EndGPUCopyPass(cpass);
+    SDL_ReleaseGPUTransferBuffer(data->device, tbuf);
+
+    return true;
+}
+
+static void GPU_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    GPU_RenderData *data = (GPU_RenderData *)renderer->internal;
+    GPU_PaletteData *palettedata = (GPU_PaletteData *)palette->internal;
+
+    if (palettedata) {
+        SDL_ReleaseGPUTexture(data->device, palettedata->texture);
+        SDL_free(palettedata);
+    }
+}
+
 static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal;
@@ -184,16 +270,6 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         return false;
     }
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        tci.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM;
-        tci.width = 256;
-        tci.height = 1;
-        data->palette = SDL_CreateGPUTexture(renderdata->device, &tci);
-        if (!data->palette) {
-            return false;
-        }
-    }
-
     SDL_PropertiesID props = SDL_GetTextureProperties(texture);
     SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_POINTER, data->texture);
 
@@ -206,52 +282,6 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
     return true;
 }
 
-static bool GPU_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal;
-    GPU_TextureData *data = (GPU_TextureData *)texture->internal;
-    SDL_Palette *palette = texture->palette;
-    const Uint32 data_size = palette->ncolors * sizeof(*palette->colors);
-
-    SDL_GPUTransferBufferCreateInfo tbci;
-    SDL_zero(tbci);
-    tbci.size = data_size;
-    tbci.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD;
-
-    SDL_GPUTransferBuffer *tbuf = SDL_CreateGPUTransferBuffer(renderdata->device, &tbci);
-    if (tbuf == NULL) {
-        return false;
-    }
-
-    Uint8 *output = SDL_MapGPUTransferBuffer(renderdata->device, tbuf, false);
-    SDL_memcpy(output, palette->colors, data_size);
-    SDL_UnmapGPUTransferBuffer(renderdata->device, tbuf);
-
-    SDL_GPUCommandBuffer *cbuf = renderdata->state.command_buffer;
-    SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf);
-
-    SDL_GPUTextureTransferInfo tex_src;
-    SDL_zero(tex_src);
-    tex_src.transfer_buffer = tbuf;
-    tex_src.rows_per_layer = 1;
-    tex_src.pixels_per_row = palette->ncolors;
-
-    SDL_GPUTextureRegion tex_dst;
-    SDL_zero(tex_dst);
-    tex_dst.texture = data->palette;
-    tex_dst.x = 0;
-    tex_dst.y = 0;
-    tex_dst.w = palette->ncolors;
-    tex_dst.h = 1;
-    tex_dst.d = 1;
-
-    SDL_UploadToGPUTexture(cpass, &tex_src, &tex_dst, false);
-    SDL_EndGPUCopyPass(cpass);
-    SDL_ReleaseGPUTransferBuffer(renderdata->device, tbuf);
-
-    return true;
-}
-
 static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                               const SDL_Rect *rect, const void *pixels, int pitch)
 {
@@ -663,9 +693,11 @@ static void Draw(
         sampler_bind.texture = tdata->texture;
         SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1);
 
-        if (texture->format == SDL_PIXELFORMAT_INDEX8) {
+        if (texture->palette) {
+            GPU_PaletteData *palette = (GPU_PaletteData *)texture->palette->internal;
+
             sampler_bind.sampler = GetSampler(data, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
-            sampler_bind.texture = tdata->palette;
+            sampler_bind.texture = palette->texture;
             SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1);
         }
     }
@@ -1128,7 +1160,6 @@ static void GPU_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
     }
 
     SDL_ReleaseGPUTexture(renderdata->device, data->texture);
-    SDL_ReleaseGPUTexture(renderdata->device, data->palette);
     SDL_free(data->pixels);
     SDL_free(data);
     texture->internal = NULL;
@@ -1242,8 +1273,10 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
     }
 
     renderer->SupportsBlendMode = GPU_SupportsBlendMode;
+    renderer->CreatePalette = GPU_CreatePalette;
+    renderer->UpdatePalette = GPU_UpdatePalette;
+    renderer->DestroyPalette = GPU_DestroyPalette;
     renderer->CreateTexture = GPU_CreateTexture;
-    renderer->UpdateTexturePalette = GPU_UpdateTexturePalette;
     renderer->UpdateTexture = GPU_UpdateTexture;
     renderer->LockTexture = GPU_LockTexture;
     renderer->UnlockTexture = GPU_UnlockTexture;

+ 86 - 47
src/render/metal/SDL_render_metal.m

@@ -144,6 +144,14 @@ typedef struct METAL_ShaderPipelines
 @implementation SDL3METAL_RenderData
 @end
 
+@interface SDL3METAL_PaletteData : NSObject
+@property(nonatomic, retain) id<MTLTexture> mtltexture;
+@property(nonatomic, assign) BOOL hasdata;
+@end
+
+@implementation SDL3METAL_PaletteData
+@end
+
 @interface SDL3METAL_TextureData : NSObject
 @property(nonatomic, retain) id<MTLTexture> mtltexture;
 @property(nonatomic, retain) id<MTLTexture> mtlpalette;
@@ -162,6 +170,10 @@ typedef struct METAL_ShaderPipelines
 @implementation SDL3METAL_TextureData
 @end
 
+static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, BOOL hasdata,
+                                        id<MTLTexture> texture, SDL_Rect rect, int slice,
+                                        const void *pixels, int pitch);
+
 static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
 static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
 
@@ -611,13 +623,68 @@ size_t GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, in
     return 0;
 }
 
+static bool METAL_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    @autoreleasepool {
+        SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal;
+        MTLPixelFormat pixfmt;
+        MTLTextureDescriptor *mtltexdesc;
+        id<MTLTexture> mtltexture = nil;
+        SDL3METAL_PaletteData *palettedata;
+
+        if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
+            pixfmt = MTLPixelFormatRGBA8Unorm_sRGB;
+        } else {
+            pixfmt = MTLPixelFormatRGBA8Unorm;
+        }
+        mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt
+                                                                        width:256
+                                                                       height:1
+                                                                    mipmapped:NO];
+        mtltexdesc.usage = MTLTextureUsageShaderRead;
+
+        mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
+        if (mtltexture == nil) {
+            return SDL_SetError("Palette allocation failed");
+        }
+
+        palettedata = [[SDL3METAL_PaletteData alloc] init];
+        palettedata.mtltexture = mtltexture;
+        palette->internal = (void *)CFBridgingRetain(palettedata);
+
+        return true;
+    }
+}
+
+static bool METAL_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    @autoreleasepool {
+        SDL3METAL_PaletteData *palettedata = (__bridge SDL3METAL_PaletteData *)palette->internal;
+        SDL_Rect rect = { 0, 0, ncolors, 1 };
+
+        if (!METAL_UpdateTextureInternal(renderer, palettedata.hasdata, palettedata.mtltexture, rect, 0, colors, ncolors * sizeof(*colors))) {
+            return false;
+        }
+        palettedata.hasdata = true;
+        return true;
+    }
+}
+
+static void METAL_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    @autoreleasepool {
+        CFBridgingRelease(palette->internal);
+        palette->internal = NULL;
+    }
+}
+
 static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     @autoreleasepool {
         SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal;
         MTLPixelFormat pixfmt;
         MTLTextureDescriptor *mtltexdesc;
-        id<MTLTexture> mtltexture = nil, mtltextureUv = nil, mtlpalette = nil;
+        id<MTLTexture> mtltexture = nil, mtltextureUv = nil;
         SDL3METAL_TextureData *texturedata;
         CVPixelBufferRef pixelbuffer = nil;
         IOSurfaceRef surface = nil;
@@ -688,25 +755,6 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
             return SDL_SetError("Texture allocation failed");
         }
 
-        if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-            if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
-                pixfmt = MTLPixelFormatBGRA8Unorm_sRGB;
-            } else {
-                pixfmt = MTLPixelFormatBGRA8Unorm;
-            }
-            mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt
-                                                                            width:256
-                                                                           height:1
-                                                                        mipmapped:NO];
-
-            mtltexdesc.usage = MTLTextureUsageShaderRead;
-
-            mtlpalette = [data.mtldevice newTextureWithDescriptor:mtltexdesc];
-            if (mtlpalette == nil) {
-                return SDL_SetError("Palette allocation failed");
-            }
-        }
-
         mtltextureUv = nil;
 #ifdef SDL_HAVE_YUV
         BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12);
@@ -753,7 +801,6 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
             texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY;
         }
         texturedata.mtltexture = mtltexture;
-        texturedata.mtlpalette = mtlpalette;
         texturedata.mtltextureUv = mtltextureUv;
 #ifdef SDL_HAVE_YUV
         texturedata.yuv = yuv;
@@ -788,7 +835,7 @@ static MTLStorageMode METAL_GetStorageMode(id<MTLResource> resource)
     return resource.storageMode;
 }
 
-static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, SDL3METAL_TextureData *texturedata,
+static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, BOOL hasdata,
                                        id<MTLTexture> texture, SDL_Rect rect, int slice,
                                        const void *pixels, int pitch)
 {
@@ -801,7 +848,7 @@ static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, SDL3METAL_Textur
     /* If the texture is managed or shared and this is the first upload, we can
      * use replaceRegion to upload to it directly. Otherwise we upload the data
      * to a staging texture and copy that over. */
-    if (!texturedata.hasdata && METAL_GetStorageMode(texture) != MTLStorageModePrivate) {
+    if (!hasdata && METAL_GetStorageMode(texture) != MTLStorageModePrivate) {
         METAL_UploadTextureData(texture, rect, slice, pixels, pitch);
         return true;
     }
@@ -856,24 +903,13 @@ static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, SDL3METAL_Textur
     return true;
 }
 
-static bool METAL_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    @autoreleasepool {
-        SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal;
-        SDL_Palette *palette = texture->palette;
-        SDL_Rect rect = { 0, 0, palette->ncolors, 1 };
-
-        return METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtlpalette, rect, 0, palette->colors, palette->ncolors * sizeof(*palette->colors));
-    }
-}
-
 static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                                const SDL_Rect *rect, const void *pixels, int pitch)
 {
     @autoreleasepool {
         SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal;
 
-        if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, pixels, pitch)) {
+        if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltexture, *rect, 0, pixels, pitch)) {
             return false;
         }
 #ifdef SDL_HAVE_YUV
@@ -885,13 +921,13 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
 
             // Skip to the correct offset into the next texture
             pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
-            if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, Uslice, pixels, UVpitch)) {
+            if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Uslice, pixels, UVpitch)) {
                 return false;
             }
 
             // Skip to the correct offset into the next texture
             pixels = (const void *)((const Uint8 *)pixels + UVrect.h * UVpitch);
-            if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, Vslice, pixels, UVpitch)) {
+            if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Vslice, pixels, UVpitch)) {
                 return false;
             }
         }
@@ -902,7 +938,7 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
 
             // Skip to the correct offset into the next texture
             pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
-            if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, 0, pixels, UVpitch)) {
+            if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, 0, pixels, UVpitch)) {
                 return false;
             }
         }
@@ -931,13 +967,13 @@ static bool METAL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
             return true;
         }
 
-        if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) {
+        if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) {
             return false;
         }
-        if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, Uslice, Uplane, Upitch)) {
+        if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Uslice, Uplane, Upitch)) {
             return false;
         }
-        if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, Vslice, Vplane, Vpitch)) {
+        if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Vslice, Vplane, Vpitch)) {
             return false;
         }
 
@@ -961,11 +997,11 @@ static bool METAL_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
             return true;
         }
 
-        if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) {
+        if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) {
             return false;
         }
 
-        if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, 0, UVplane, UVpitch)) {
+        if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, 0, UVplane, UVpitch)) {
             return false;
         }
 
@@ -1568,8 +1604,9 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c
 
     if (texture != statecache->texture) {
         [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
-        if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-            [data.mtlcmdencoder setFragmentTexture:texturedata.mtlpalette atIndex:1];
+        if (texture->palette) {
+            SDL3METAL_PaletteData *palette = (__bridge SDL3METAL_PaletteData *)texture->palette->internal;
+            [data.mtlcmdencoder setFragmentTexture:palette.mtltexture atIndex:1];
         }
 #ifdef SDL_HAVE_YUV
         if (texturedata.yuv || texturedata.nv12) {
@@ -1593,7 +1630,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c
         statecache->texture_address_mode_u = cmd->data.draw.texture_address_mode_u;
         statecache->texture_address_mode_v = cmd->data.draw.texture_address_mode_v;
     }
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
+    if (texture->palette) {
         if (!statecache->texture_palette) {
             id<MTLSamplerState> mtlsampler = GetSampler(data, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
             if (mtlsampler == nil) {
@@ -2201,8 +2238,10 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
         renderer->WindowEvent = METAL_WindowEvent;
         renderer->GetOutputSize = METAL_GetOutputSize;
         renderer->SupportsBlendMode = METAL_SupportsBlendMode;
+        renderer->CreatePalette = METAL_CreatePalette;
+        renderer->UpdatePalette = METAL_UpdatePalette;
+        renderer->DestroyPalette = METAL_DestroyPalette;
         renderer->CreateTexture = METAL_CreateTexture;
-        renderer->UpdateTexturePalette = METAL_UpdateTexturePalette;
         renderer->UpdateTexture = METAL_UpdateTexture;
 #ifdef SDL_HAVE_YUV
         renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;

+ 62 - 33
src/render/opengl/SDL_render_gl.c

@@ -125,6 +125,11 @@ typedef struct
     GL_DrawStateCache drawstate;
 } GL_RenderData;
 
+typedef struct
+{
+    GLuint texture;
+} GL_PaletteData;
+
 typedef struct
 {
     GLuint texture;
@@ -133,7 +138,6 @@ typedef struct
     GLfloat texh;
     GLenum format;
     GLenum formattype;
-    GLuint palette;
     GL_Shader shader;
     float texel_size[4];
     const float *shader_params;
@@ -481,6 +485,58 @@ static void SetTextureAddressMode(GL_RenderData *data, GLenum textype, SDL_Textu
     data->glTexParameteri(textype, GL_TEXTURE_WRAP_T, TranslateAddressMode(addressModeV));
 }
 
+static bool GL_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    GL_RenderData *data = (GL_RenderData *)renderer->internal;
+    GL_PaletteData *palettedata = (GL_PaletteData *)SDL_calloc(1, sizeof(*palettedata));
+    if (!palettedata) {
+        return false;
+    }
+    palette->internal = palettedata;
+
+    data->drawstate.texture = NULL; // we trash this state.
+
+    const GLenum textype = data->textype;
+    data->glGenTextures(1, &palettedata->texture);
+    data->glBindTexture(textype, palettedata->texture);
+    data->glTexImage2D(textype, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    if (!GL_CheckError("glTexImage2D()", renderer)) {
+        return false;
+    }
+    SetTextureScaleMode(data, textype, SDL_SCALEMODE_NEAREST);
+    SetTextureAddressMode(data, textype, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
+    return true;
+}
+
+static bool GL_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    GL_RenderData *data = (GL_RenderData *)renderer->internal;
+    GL_PaletteData *palettedata = (GL_PaletteData *)palette->internal;
+
+    GL_ActivateRenderer(renderer);
+
+    data->drawstate.texture = NULL; // we trash this state.
+
+    const GLenum textype = data->textype;
+    data->glBindTexture(textype, palettedata->texture);
+    data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    data->glPixelStorei(GL_UNPACK_ROW_LENGTH, ncolors);
+    data->glTexSubImage2D(textype, 0, 0, 0, ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, colors);
+
+    return GL_CheckError("glTexSubImage2D()", renderer);
+}
+
+static void GL_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    GL_RenderData *data = (GL_RenderData *)renderer->internal;
+    GL_PaletteData *palettedata = (GL_PaletteData *)palette->internal;
+
+    if (palettedata) {
+        data->glDeleteTextures(1, &palettedata->texture);
+        SDL_free(palettedata);
+    }
+}
+
 static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     GL_RenderData *renderdata = (GL_RenderData *)renderer->internal;
@@ -621,14 +677,6 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P
     SetTextureScaleMode(renderdata, textype, data->texture_scale_mode);
     SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v);
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        renderdata->glGenTextures(1, &data->palette);
-        renderdata->glBindTexture(textype, data->palette);
-        renderdata->glTexImage2D(textype, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-        SetTextureScaleMode(renderdata, textype, SDL_SCALEMODE_NEAREST);
-        SetTextureAddressMode(renderdata, textype, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
-    }
-
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
         texture->format == SDL_PIXELFORMAT_IYUV) {
@@ -723,25 +771,6 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P
     return GL_CheckError("", renderer);
 }
 
-static bool GL_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    GL_RenderData *renderdata = (GL_RenderData *)renderer->internal;
-    const GLenum textype = renderdata->textype;
-    GL_TextureData *data = (GL_TextureData *)texture->internal;
-    SDL_Palette *palette = texture->palette;
-
-    GL_ActivateRenderer(renderer);
-
-    renderdata->drawstate.texture = NULL; // we trash this state.
-
-    renderdata->glBindTexture(textype, data->palette);
-    renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-    renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, palette->ncolors);
-    renderdata->glTexSubImage2D(textype, 0, 0, 0, palette->ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, palette->colors);
-
-    return GL_CheckError("glTexSubImage2D()", renderer);
-}
-
 static bool GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                             const SDL_Rect *rect, const void *pixels, int pitch)
 {
@@ -1205,8 +1234,9 @@ static bool SetCopyState(GL_RenderData *data, const SDL_RenderCommand *cmd)
         }
 #endif
         if (texture->palette) {
+            GL_PaletteData *palette = (GL_PaletteData *)texture->palette->internal;
             data->glActiveTextureARB(GL_TEXTURE1_ARB);
-            data->glBindTexture(textype, texturedata->palette);
+            data->glBindTexture(textype, palette->texture);
         }
         if (data->GL_ARB_multitexture_supported) {
             data->glActiveTextureARB(GL_TEXTURE0_ARB);
@@ -1631,9 +1661,6 @@ static void GL_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
     if (data->texture && !data->texture_external) {
         renderdata->glDeleteTextures(1, &data->texture);
     }
-    if (data->palette) {
-        renderdata->glDeleteTextures(1, &data->palette);
-    }
 #ifdef SDL_HAVE_YUV
     if (data->yuv) {
         if (!data->utexture_external) {
@@ -1753,8 +1780,10 @@ static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pr
 
     renderer->WindowEvent = GL_WindowEvent;
     renderer->SupportsBlendMode = GL_SupportsBlendMode;
+    renderer->CreatePalette = GL_CreatePalette;
+    renderer->UpdatePalette = GL_UpdatePalette;
+    renderer->DestroyPalette = GL_DestroyPalette;
     renderer->CreateTexture = GL_CreateTexture;
-    renderer->UpdateTexturePalette = GL_UpdateTexturePalette;
     renderer->UpdateTexture = GL_UpdateTexture;
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = GL_UpdateTextureYUV;

+ 63 - 42
src/render/opengles2/SDL_render_gles2.c

@@ -58,14 +58,18 @@ struct GLES2_FBOList
     GLES2_FBOList *next;
 };
 
-typedef struct GLES2_TextureData
+typedef struct
+{
+    GLuint texture;
+} GLES2_PaletteData;
+
+typedef struct
 {
     GLuint texture;
     bool texture_external;
     GLenum texture_type;
     GLenum pixel_format;
     GLenum pixel_type;
-    GLuint palette;
     void *pixel_data;
     int pitch;
 #ifdef SDL_HAVE_YUV
@@ -1277,8 +1281,9 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v
         }
 #endif
         if (texture->palette) {
+            GLES2_PaletteData *palette = (GLES2_PaletteData *)texture->palette->internal;
             data->glActiveTexture(GL_TEXTURE1);
-            data->glBindTexture(tdata->texture_type, tdata->palette);
+            data->glBindTexture(tdata->texture_type, palette->texture);
 
             data->glActiveTexture(GL_TEXTURE0);
         }
@@ -1633,6 +1638,58 @@ static void GLES2_DestroyRenderer(SDL_Renderer *renderer)
     }
 }
 
+static bool GLES2_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
+    GLES2_PaletteData *palettedata = (GLES2_PaletteData *)SDL_calloc(1, sizeof(*palettedata));
+    if (!palettedata) {
+        return false;
+    }
+    palette->internal = palettedata;
+
+    data->drawstate.texture = NULL; // we trash this state.
+
+    data->glGenTextures(1, &palettedata->texture);
+    if (!GL_CheckError("glGenTexures()", renderer)) {
+        return false;
+    }
+    data->glActiveTexture(GL_TEXTURE1);
+    data->glBindTexture(GL_TEXTURE_2D, palettedata->texture);
+    data->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+    if (!GL_CheckError("glTexImage2D()", renderer)) {
+        return false;
+    }
+    SetTextureScaleMode(data, GL_TEXTURE_2D, SDL_SCALEMODE_NEAREST);
+    SetTextureAddressMode(data, GL_TEXTURE_2D, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
+    return true;
+}
+
+static bool GLES2_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
+    GLES2_PaletteData *palettedata = (GLES2_PaletteData *)palette->internal;
+
+    GLES2_ActivateRenderer(renderer);
+
+    data->drawstate.texture = NULL; // we trash this state.
+
+    data->glBindTexture(GL_TEXTURE_2D, palettedata->texture);
+    data->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, colors);
+
+    return GL_CheckError("glTexSubImage2D()", renderer);
+}
+
+static void GLES2_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
+    GLES2_PaletteData *palettedata = (GLES2_PaletteData *)palette->internal;
+
+    if (palettedata) {
+        data->glDeleteTextures(1, &palettedata->texture);
+        SDL_free(palettedata);
+    }
+}
+
 static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     GLES2_RenderData *renderdata = (GLES2_RenderData *)renderer->internal;
@@ -1731,25 +1788,6 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
     data->texel_size[0] = 1.0f / data->texel_size[2];
     data->texel_size[1] = 1.0f / data->texel_size[3];
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        renderdata->glGenTextures(1, &data->palette);
-        if (!GL_CheckError("glGenTexures()", renderer)) {
-            SDL_free(data->pixel_data);
-            SDL_free(data);
-            return false;
-        }
-        renderdata->glActiveTexture(GL_TEXTURE1);
-        renderdata->glBindTexture(data->texture_type, data->palette);
-        renderdata->glTexImage2D(data->texture_type, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
-        if (!GL_CheckError("glTexImage2D()", renderer)) {
-            SDL_free(data->pixel_data);
-            SDL_free(data);
-            return false;
-        }
-        SetTextureScaleMode(renderdata, data->texture_type, SDL_SCALEMODE_NEAREST);
-        SetTextureAddressMode(renderdata, data->texture_type, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
-    }
-
 #ifdef SDL_HAVE_YUV
     if (data->yuv) {
         data->texture_v = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER, 0);
@@ -1904,22 +1942,6 @@ static bool GLES2_TexSubImage2D(GLES2_RenderData *data, GLenum target, GLint xof
     return true;
 }
 
-static bool GLES2_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal;
-    GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal;
-    SDL_Palette *palette = texture->palette;
-
-    GLES2_ActivateRenderer(renderer);
-
-    data->drawstate.texture = NULL; /* we trash this state. */
-
-    data->glBindTexture(tdata->texture_type, tdata->palette);
-    data->glTexSubImage2D(tdata->texture_type, 0, 0, 0, palette->ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, palette->colors);
-
-    return GL_CheckError("glTexSubImage2D()", renderer);
-}
-
 static bool GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
                                const void *pixels, int pitch)
 {
@@ -2160,9 +2182,6 @@ static void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
         if (tdata->texture && !tdata->texture_external) {
             data->glDeleteTextures(1, &tdata->texture);
         }
-        if (tdata->palette) {
-            data->glDeleteTextures(1, &tdata->palette);
-        }
 #ifdef SDL_HAVE_YUV
         if (tdata->texture_v && !tdata->texture_v_external) {
             data->glDeleteTextures(1, &tdata->texture_v);
@@ -2290,8 +2309,10 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
     // Populate the function pointers for the module
     renderer->WindowEvent = GLES2_WindowEvent;
     renderer->SupportsBlendMode = GLES2_SupportsBlendMode;
+    renderer->CreatePalette = GLES2_CreatePalette;
+    renderer->UpdatePalette = GLES2_UpdatePalette;
+    renderer->DestroyPalette = GLES2_DestroyPalette;
     renderer->CreateTexture = GLES2_CreateTexture;
-    renderer->UpdateTexturePalette = GLES2_UpdateTexturePalette;
     renderer->UpdateTexture = GLES2_UpdateTexture;
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = GLES2_UpdateTextureYUV;

+ 36 - 9
src/render/software/SDL_render_sw.c

@@ -99,6 +99,38 @@ static bool SW_GetOutputSize(SDL_Renderer *renderer, int *w, int *h)
     return SDL_SetError("Software renderer doesn't have an output surface");
 }
 
+static bool SW_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    SDL_Palette *surface_palette = SDL_CreatePalette(256);
+    if (!surface_palette) {
+        return false;
+    }
+    palette->internal = surface_palette;
+    return true;
+}
+
+static bool SW_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    SDL_Palette *surface_palette = (SDL_Palette *)palette->internal;
+    return SDL_SetPaletteColors(surface_palette, colors, 0, ncolors);
+}
+
+static void SW_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    SDL_Palette *surface_palette = (SDL_Palette *)palette->internal;
+    SDL_DestroyPalette(surface_palette);
+}
+
+static bool SW_ChangeTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
+{
+    SDL_Surface *surface = (SDL_Surface *)texture->internal;
+    SDL_Palette *surface_palette = NULL;
+    if (texture->palette) {
+        surface_palette = (SDL_Palette *)texture->palette->internal;
+    }
+    return SDL_SetSurfacePalette(surface, surface_palette);
+}
+
 static bool SW_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     SDL_Surface *surface = SDL_CreateSurface(texture->w, texture->h, texture->format);
@@ -132,14 +164,6 @@ static bool SW_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P
     return true;
 }
 
-static bool SW_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    SDL_Surface *surface = (SDL_Surface *)texture->internal;
-    SDL_Palette *palette = texture->palette;
-
-    return SDL_SetPaletteColors(surface->palette, palette->colors, 0, palette->ncolors);
-}
-
 static bool SW_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                             const SDL_Rect *rect, const void *pixels, int pitch)
 {
@@ -1158,8 +1182,11 @@ bool SW_CreateRendererForSurface(SDL_Renderer *renderer, SDL_Surface *surface, S
 
     renderer->WindowEvent = SW_WindowEvent;
     renderer->GetOutputSize = SW_GetOutputSize;
+    renderer->CreatePalette = SW_CreatePalette;
+    renderer->UpdatePalette = SW_UpdatePalette;
+    renderer->DestroyPalette = SW_DestroyPalette;
+    renderer->ChangeTexturePalette = SW_ChangeTexturePalette;
     renderer->CreateTexture = SW_CreateTexture;
-    renderer->UpdateTexturePalette = SW_UpdateTexturePalette;
     renderer->UpdateTexture = SW_UpdateTexture;
     renderer->LockTexture = SW_LockTexture;
     renderer->UnlockTexture = SW_UnlockTexture;

+ 56 - 40
src/render/vulkan/SDL_render_vulkan.c

@@ -240,11 +240,16 @@ typedef struct
     VkFormat format;
 } VULKAN_Image;
 
+// Per-palette data
+typedef struct
+{
+    VULKAN_Image image;
+} VULKAN_PaletteData;
+
 // Per-texture data
 typedef struct
 {
     VULKAN_Image mainImage;
-    VULKAN_Image paletteImage;
     VkRenderPass mainRenderpasses[VULKAN_RENDERPASS_COUNT];
     VkFramebuffer mainFramebuffer;
     VULKAN_Buffer stagingBuffer;
@@ -384,6 +389,8 @@ typedef struct
     bool issueBatch;
 } VULKAN_RenderData;
 
+static bool VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImage image, VkFormat format, int plane, int x, int y, int w, int h, const void *pixels, int pitch, VkImageLayout *imageLayout);
+
 static SDL_PixelFormat VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat)
 {
     switch (vkFormat) {
@@ -2533,6 +2540,45 @@ static bool VULKAN_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blend
     return true;
 }
 
+static bool VULKAN_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal;
+    VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)SDL_calloc(1, sizeof(*palettedata));
+    if (!palettedata) {
+        return false;
+    }
+    palette->internal = palettedata;
+
+    VkFormat format = SDLPixelFormatToVkTextureFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace);
+    VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+    VkComponentMapping imageViewSwizzle = data->identitySwizzle;
+    VkResult result = VULKAN_AllocateImage(data, 0, 256, 1, format, usage, imageViewSwizzle, VK_NULL_HANDLE, &palettedata->image);
+    if (result != VK_SUCCESS) {
+        SET_ERROR_CODE("VULKAN_AllocateImage()", result);
+        return false;
+    }
+    return true;
+}
+
+static bool VULKAN_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors)
+{
+    VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal;
+    VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)palette->internal;
+
+    return VULKAN_UpdateTextureInternal(data, palettedata->image.image, palettedata->image.format, 0, 0, 0, ncolors, 1, colors, ncolors * sizeof(*colors), &palettedata->image.imageLayout);
+}
+
+static void VULKAN_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette)
+{
+    VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal;
+    VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)palette->internal;
+
+    if (palettedata) {
+        VULKAN_DestroyImage(data, &palettedata->image);
+        SDL_free(palettedata);
+    }
+}
+
 static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
     VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
@@ -2694,21 +2740,6 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
         return false;
     }
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        VkFormat paletteFormat;
-
-        if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) {
-            paletteFormat = VK_FORMAT_R8G8B8A8_SRGB;
-        } else {
-            paletteFormat = VK_FORMAT_R8G8B8A8_UNORM;
-        }
-        result = VULKAN_AllocateImage(rendererData, 0, 256, 1, paletteFormat, usage, imageViewSwizzle, textureData->samplerYcbcrConversion, &textureData->paletteImage);
-        if (result != VK_SUCCESS) {
-            SET_ERROR_CODE("VULKAN_AllocateImage()", result);
-            return false;
-        }
-    }
-
     SDL_PropertiesID props = SDL_GetTextureProperties(texture);
     SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER, (Sint64)textureData->mainImage.image);
 
@@ -2746,10 +2777,6 @@ static void VULKAN_DestroyTexture(SDL_Renderer *renderer,
 
     VULKAN_DestroyImage(rendererData, &textureData->mainImage);
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        VULKAN_DestroyImage(rendererData, &textureData->paletteImage);
-    }
-
 #ifdef SDL_HAVE_YUV
     if (textureData->samplerYcbcrConversion != VK_NULL_HANDLE) {
         vkDestroySamplerYcbcrConversionKHR(rendererData->device, textureData->samplerYcbcrConversion, NULL);
@@ -2876,20 +2903,6 @@ static bool VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImag
     return true;
 }
 
-
-static bool VULKAN_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture)
-{
-    VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
-    VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal;
-    SDL_Palette *palette = texture->palette;
-
-    if (!textureData) {
-        return SDL_SetError("Texture is not currently available");
-    }
-
-    return VULKAN_UpdateTextureInternal(rendererData, textureData->paletteImage.image, textureData->paletteImage.format, 0, 0, 0, palette->ncolors, 1, palette->colors, palette->ncolors * sizeof(*palette->colors), &textureData->paletteImage.imageLayout);
-}
-
 static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                                const SDL_Rect *rect, const void *srcPixels,
                                int srcPitch)
@@ -3900,8 +3913,9 @@ static bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand
     }
     ++numSamplers;
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        if (textureData->paletteImage.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
+    if (texture->palette) {
+        VULKAN_PaletteData *palette = (VULKAN_PaletteData *)texture->palette->internal;
+        if (palette->image.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) {
             bool stoppedRenderPass = false;
             if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
                 vkCmdEndRenderPass(rendererData->currentCommandBuffer);
@@ -3915,14 +3929,14 @@ static bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand
                 VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
                 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
-                textureData->paletteImage.image,
-                &textureData->paletteImage.imageLayout);
+                palette->image.image,
+                &palette->image.imageLayout);
 
             if (stoppedRenderPass) {
                 VULKAN_BeginRenderPass(rendererData, VK_ATTACHMENT_LOAD_OP_LOAD, NULL);
             }
         }
-        imageViews[numImageViews++] = textureData->paletteImage.imageView;
+        imageViews[numImageViews++] = palette->image.imageView;
 
         samplers[numSamplers] = VULKAN_GetSampler(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
         if (samplers[numSamplers] == VK_NULL_HANDLE) {
@@ -4391,8 +4405,10 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD
 
     renderer->WindowEvent = VULKAN_WindowEvent;
     renderer->SupportsBlendMode = VULKAN_SupportsBlendMode;
+    renderer->CreatePalette = VULKAN_CreatePalette;
+    renderer->UpdatePalette = VULKAN_UpdatePalette;
+    renderer->DestroyPalette = VULKAN_DestroyPalette;
     renderer->CreateTexture = VULKAN_CreateTexture;
-    renderer->UpdateTexturePalette = VULKAN_UpdateTexturePalette;
     renderer->UpdateTexture = VULKAN_UpdateTexture;
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = VULKAN_UpdateTextureYUV;