Browse Source

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 1 week ago
parent
commit
b65590159b

+ 92 - 21
src/render/SDL_render.c

@@ -347,6 +347,15 @@ static bool FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture)
     return true;
     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)
 static bool FlushRenderCommandsIfGPURenderStateNeeded(SDL_GPURenderState *state)
 {
 {
     SDL_Renderer *renderer = state->renderer;
     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)
 static bool UpdateTexturePalette(SDL_Texture *texture)
 {
 {
     SDL_Renderer *renderer = texture->renderer;
     SDL_Renderer *renderer = texture->renderer;
+    SDL_Palette *public = texture->public_palette;
 
 
     if (!SDL_ISPIXELFORMAT_INDEXED(texture->format)) {
     if (!SDL_ISPIXELFORMAT_INDEXED(texture->format)) {
         return true;
         return true;
     }
     }
 
 
-    if (!texture->palette) {
+    if (!public) {
         return SDL_SetError("Texture doesn't have a palette");
         return SDL_SetError("Texture doesn't have a palette");
     }
     }
 
 
-    if (texture->palette_version == texture->palette->version) {
-        return true;
-    }
-
     if (texture->native) {
     if (texture->native) {
+        // Keep the native texture in sync with palette updates
+        if (texture->palette_version == public->version) {
+            return true;
+        }
+
         if (!FlushRenderCommandsIfTextureNeeded(texture->native)) {
         if (!FlushRenderCommandsIfTextureNeeded(texture->native)) {
             return false;
             return false;
         }
         }
@@ -727,17 +738,25 @@ static bool UpdateTexturePalette(SDL_Texture *texture)
         if (!result) {
         if (!result) {
             return false;
             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;
             return false;
         }
         }
 
 
-        if (!renderer->UpdateTexturePalette(renderer, texture)) {
+        if (!renderer->UpdatePalette(renderer, palette, public->ncolors, public->colors)) {
             return false;
             return false;
         }
         }
+
+        palette->version = public->version;
     }
     }
 
 
-    texture->palette_version = texture->palette->version;
+    palette->last_command_generation = renderer->render_command_generation;
     return true;
     return true;
 }
 }
 
 
@@ -1143,6 +1162,11 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props)
     UpdatePixelClipRect(renderer, &renderer->main_view);
     UpdatePixelClipRect(renderer, &renderer->main_view);
     UpdateMainViewDimensions(renderer);
     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.
     // new textures start at zero, so we start at 1 so first render doesn't flush by accident.
     renderer->render_command_generation = 1;
     renderer->render_command_generation = 1;
 
 
@@ -1656,7 +1680,7 @@ static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, S
     bool direct_update;
     bool direct_update;
 
 
     if (surface->format == texture->format &&
     if (surface->format == texture->format &&
-        surface->palette == texture->palette &&
+        surface->palette == texture->public_palette &&
         SDL_GetSurfaceColorspace(surface) == texture->colorspace) {
         SDL_GetSurfaceColorspace(surface) == texture->colorspace) {
         if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
         if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
             /* Surface and Renderer formats are identical.
             /* Surface and Renderer formats are identical.
@@ -1682,7 +1706,7 @@ static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, S
         }
         }
     } else {
     } else {
         // Set up a destination surface for the texture update
         // 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) {
         if (temp) {
             SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
             SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
             SDL_DestroySurface(temp);
             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");
         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;
         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) {
         if (texture->palette_surface) {
@@ -1924,7 +1988,7 @@ SDL_Palette *SDL_GetTexturePalette(SDL_Texture *texture)
 {
 {
     CHECK_TEXTURE_MAGIC(texture, NULL);
     CHECK_TEXTURE_MAGIC(texture, NULL);
 
 
-    return texture->palette;
+    return texture->public_palette;
 }
 }
 
 
 bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b)
 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);
         SDL_UnlockTexture(texture);
         return false;
         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;
     *surface = texture->locked_surface;
@@ -5513,7 +5577,7 @@ static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
 {
 {
     SDL_Renderer *renderer;
     SDL_Renderer *renderer;
 
 
-    if (texture->palette) {
+    if (texture->public_palette) {
         SDL_SetTexturePalette(texture, NULL);
         SDL_SetTexturePalette(texture, NULL);
     }
     }
 
 
@@ -5634,6 +5698,13 @@ void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer)
         SDL_assert(tex != renderer->textures); // satisfy static analysis.
         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
     // Clean up renderer-specific resources
     if (renderer->DestroyRenderer) {
     if (renderer->DestroyRenderer) {
         renderer->DestroyRenderer(renderer);
         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_FPoint current_scale;  // this is just `scale * logical_scale`, precalculated, since we use it a lot.
 } SDL_RenderViewState;
 } 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
 // Define the SDL texture structure
 struct SDL_Texture
 struct SDL_Texture
 {
 {
@@ -90,7 +99,8 @@ struct SDL_Texture
     SDL_ScaleMode scaleMode;    // The texture scale mode
     SDL_ScaleMode scaleMode;    // The texture scale mode
     SDL_FColor color;           // Texture modulation values
     SDL_FColor color;           // Texture modulation values
     SDL_RenderViewState view;   // Target texture view state
     SDL_RenderViewState view;   // Target texture view state
-    SDL_Palette *palette;
+    SDL_Palette *public_palette;
+    SDL_TexturePalette *palette;
     Uint32 palette_version;
     Uint32 palette_version;
     SDL_Surface *palette_surface;
     SDL_Surface *palette_surface;
 
 
@@ -235,7 +245,10 @@ struct SDL_Renderer
 
 
     void (*InvalidateCachedState)(SDL_Renderer *renderer);
     void (*InvalidateCachedState)(SDL_Renderer *renderer);
     bool (*RunCommandQueue)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize);
     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,
     bool (*UpdateTexture)(SDL_Renderer *renderer, SDL_Texture *texture,
                          const SDL_Rect *rect, const void *pixels,
                          const SDL_Rect *rect, const void *pixels,
                          int pitch);
                          int pitch);
@@ -301,6 +314,9 @@ struct SDL_Renderer
     SDL_Texture *target;
     SDL_Texture *target;
     SDL_Mutex *target_mutex;
     SDL_Mutex *target_mutex;
 
 
+    // The list of palettes
+    SDL_HashTable *palettes;
+
     SDL_Colorspace output_colorspace;
     SDL_Colorspace output_colorspace;
     float SDR_white_point;
     float SDR_white_point;
     float HDR_headroom;
     float HDR_headroom;

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

@@ -48,6 +48,26 @@ typedef struct
     const float *shader_params;
     const float *shader_params;
 } D3D_DrawStateCache;
 } 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
 // Direct3D renderer implementation
 
 
 typedef struct
 typedef struct
@@ -74,23 +94,12 @@ typedef struct
     int currentVertexBuffer;
     int currentVertexBuffer;
     bool reportedVboProblem;
     bool reportedVboProblem;
     D3D_DrawStateCache drawstate;
     D3D_DrawStateCache drawstate;
+    D3D_PaletteData *palettes;
 } D3D_RenderData;
 } D3D_RenderData;
 
 
-typedef struct
-{
-    bool dirty;
-    int w, h;
-    DWORD usage;
-    Uint32 format;
-    D3DFORMAT d3dfmt;
-    IDirect3DTexture9 *texture;
-    IDirect3DTexture9 *staging;
-} D3D_TextureRep;
-
 typedef struct
 typedef struct
 {
 {
     D3D_TextureRep texture;
     D3D_TextureRep texture;
-    D3D_TextureRep palette;
     D3D9_Shader shader;
     D3D9_Shader shader;
     const float *shader_params;
     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)
 static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
 {
     D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
     D3D_RenderData *data = (D3D_RenderData *)renderer->internal;
@@ -555,10 +657,6 @@ static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         return false;
         return false;
     }
     }
     if (texture->format == SDL_PIXELFORMAT_INDEX8) {
     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;
         texturedata->shader = SHADER_PALETTE;
     }
     }
 #ifdef SDL_HAVE_YUV
 #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)) {
     if (!D3D_RecreateTextureRep(data->device, &texturedata->texture)) {
         return false;
         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
 #ifdef SDL_HAVE_YUV
     if (texturedata->yuv) {
     if (texturedata->yuv) {
         if (!D3D_RecreateTextureRep(data->device, &texturedata->utexture)) {
         if (!D3D_RecreateTextureRep(data->device, &texturedata->utexture)) {
@@ -616,56 +708,6 @@ static bool D3D_RecreateTexture(SDL_Renderer *renderer, SDL_Texture *texture)
     return true;
     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
 #ifdef SDL_HAVE_YUV
 static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
 static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
                                 const SDL_Rect *rect,
                                 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)) {
     if (!BindTextureRep(data->device, &texturedata->texture, 0)) {
         return false;
         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;
             return false;
         }
         }
     }
     }
@@ -1046,8 +1089,8 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd)
         if (!texture) {
         if (!texture) {
             IDirect3DDevice9_SetTexture(data->device, 0, NULL);
             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);
             IDirect3DDevice9_SetTexture(data->device, 1, NULL);
         }
         }
 #ifdef SDL_HAVE_YUV
 #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;
         D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal;
         if (texturedata) {
         if (texturedata) {
             UpdateDirtyTexture(data->device, &texturedata->texture);
             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
 #ifdef SDL_HAVE_YUV
             if (texturedata->yuv) {
             if (texturedata->yuv) {
@@ -1488,7 +1532,7 @@ static void D3D_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
         renderdata->drawstate.shader_params = NULL;
         renderdata->drawstate.shader_params = NULL;
         IDirect3DDevice9_SetPixelShader(renderdata->device, NULL);
         IDirect3DDevice9_SetPixelShader(renderdata->device, NULL);
         IDirect3DDevice9_SetTexture(renderdata->device, 0, NULL);
         IDirect3DDevice9_SetTexture(renderdata->device, 0, NULL);
-        if (texture->format == SDL_PIXELFORMAT_INDEX8) {
+        if (texture->palette) {
             IDirect3DDevice9_SetTexture(renderdata->device, 1, NULL);
             IDirect3DDevice9_SetTexture(renderdata->device, 1, NULL);
         }
         }
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
@@ -1520,6 +1564,9 @@ static void D3D_DestroyRenderer(SDL_Renderer *renderer)
     if (data) {
     if (data) {
         int i;
         int i;
 
 
+        // Make sure the palettes have been freed
+        SDL_assert(!data->palettes);
+
         // Release the render target
         // Release the render target
         if (data->defaultRenderTarget) {
         if (data->defaultRenderTarget) {
             IDirect3DSurface9_Release(data->defaultRenderTarget);
             IDirect3DSurface9_Release(data->defaultRenderTarget);
@@ -1560,6 +1607,7 @@ static bool D3D_Reset(SDL_Renderer *renderer)
     const Float4X4 d3dmatrix = MatrixIdentity();
     const Float4X4 d3dmatrix = MatrixIdentity();
     HRESULT result;
     HRESULT result;
     SDL_Texture *texture;
     SDL_Texture *texture;
+    D3D_PaletteData *palette;
     int i;
     int i;
 
 
     // Cancel any scene that we've started
     // 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
     // Release all vertex buffers
     for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
     for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) {
         if (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->WindowEvent = D3D_WindowEvent;
     renderer->SupportsBlendMode = D3D_SupportsBlendMode;
     renderer->SupportsBlendMode = D3D_SupportsBlendMode;
+    renderer->CreatePalette = D3D_CreatePalette;
+    renderer->UpdatePalette = D3D_UpdatePalette;
+    renderer->DestroyPalette = D3D_DestroyPalette;
     renderer->CreateTexture = D3D_CreateTexture;
     renderer->CreateTexture = D3D_CreateTexture;
-    renderer->UpdateTexturePalette = D3D_UpdateTexturePalette;
     renderer->UpdateTexture = D3D_UpdateTexture;
     renderer->UpdateTexture = D3D_UpdateTexture;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = D3D_UpdateTextureYUV;
     renderer->UpdateTextureYUV = D3D_UpdateTextureYUV;

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

@@ -114,6 +114,13 @@ typedef struct
     SDL_FColor color;
     SDL_FColor color;
 } D3D11_VertexPositionColor;
 } D3D11_VertexPositionColor;
 
 
+// Per-palette data
+typedef struct
+{
+    ID3D11Texture2D *texture;
+    ID3D11ShaderResourceView *resourceView;
+} D3D11_PaletteData;
+
 // Per-texture data
 // Per-texture data
 typedef struct
 typedef struct
 {
 {
@@ -121,8 +128,6 @@ typedef struct
     ID3D11Texture2D *mainTexture;
     ID3D11Texture2D *mainTexture;
     ID3D11ShaderResourceView *mainTextureResourceView;
     ID3D11ShaderResourceView *mainTextureResourceView;
     ID3D11RenderTargetView *mainTextureRenderTargetView;
     ID3D11RenderTargetView *mainTextureRenderTargetView;
-    ID3D11Texture2D *paletteTexture;
-    ID3D11ShaderResourceView *paletteTextureResourceView;
     ID3D11Texture2D *stagingTexture;
     ID3D11Texture2D *stagingTexture;
     int lockedTexturePositionX;
     int lockedTexturePositionX;
     int lockedTexturePositionY;
     int lockedTexturePositionY;
@@ -232,6 +237,8 @@ static const GUID SDL_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe
 #pragma GCC diagnostic pop
 #pragma GCC diagnostic pop
 #endif
 #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)
 SDL_PixelFormat D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat)
 {
 {
     switch (dxgiFormat) {
     switch (dxgiFormat) {
@@ -1179,6 +1186,73 @@ static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D11T
     return true;
     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)
 static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
 {
     D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal;
     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);
     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
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
         texture->format == SDL_PIXELFORMAT_IYUV) {
         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);
         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
 #ifdef SDL_HAVE_YUV
     if (textureData->yuv) {
     if (textureData->yuv) {
         result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice,
         result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice,
@@ -1424,8 +1469,6 @@ static void D3D11_DestroyTexture(SDL_Renderer *renderer,
     SAFE_RELEASE(data->mainTexture);
     SAFE_RELEASE(data->mainTexture);
     SAFE_RELEASE(data->mainTextureResourceView);
     SAFE_RELEASE(data->mainTextureResourceView);
     SAFE_RELEASE(data->mainTextureRenderTargetView);
     SAFE_RELEASE(data->mainTextureRenderTargetView);
-    SAFE_RELEASE(data->paletteTexture);
-    SAFE_RELEASE(data->paletteTextureResourceView);
     SAFE_RELEASE(data->stagingTexture);
     SAFE_RELEASE(data->stagingTexture);
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     SAFE_RELEASE(data->mainTextureU);
     SAFE_RELEASE(data->mainTextureU);
@@ -1542,19 +1585,6 @@ static bool D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Te
     return true;
     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
 #ifdef SDL_HAVE_YUV
 static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
 static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
                                  const SDL_Rect *rect,
                                  const SDL_Rect *rect,
@@ -2491,8 +2521,10 @@ static bool D3D11_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *
     }
     }
     ++numShaderSamplers;
     ++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);
         shaderSamplers[numShaderSamplers] = D3D11_GetSamplerState(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
         if (!shaderSamplers[numShaderSamplers]) {
         if (!shaderSamplers[numShaderSamplers]) {
@@ -2846,8 +2878,10 @@ static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
 
 
     renderer->WindowEvent = D3D11_WindowEvent;
     renderer->WindowEvent = D3D11_WindowEvent;
     renderer->SupportsBlendMode = D3D11_SupportsBlendMode;
     renderer->SupportsBlendMode = D3D11_SupportsBlendMode;
+    renderer->CreatePalette = D3D11_CreatePalette;
+    renderer->UpdatePalette = D3D11_UpdatePalette;
+    renderer->DestroyPalette = D3D11_DestroyPalette;
     renderer->CreateTexture = D3D11_CreateTexture;
     renderer->CreateTexture = D3D11_CreateTexture;
-    renderer->UpdateTexturePalette = D3D11_UpdateTexturePalette;
     renderer->UpdateTexture = D3D11_UpdateTexture;
     renderer->UpdateTexture = D3D11_UpdateTexture;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = D3D11_UpdateTextureYUV;
     renderer->UpdateTextureYUV = D3D11_UpdateTextureYUV;

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

@@ -109,6 +109,15 @@ typedef struct
     SDL_FColor color;
     SDL_FColor color;
 } D3D12_VertexPositionColor;
 } 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
 // Per-texture data
 typedef struct
 typedef struct
 {
 {
@@ -119,10 +128,6 @@ typedef struct
     SIZE_T mainSRVIndex;
     SIZE_T mainSRVIndex;
     D3D12_CPU_DESCRIPTOR_HANDLE mainTextureRenderTargetView;
     D3D12_CPU_DESCRIPTOR_HANDLE mainTextureRenderTargetView;
     DXGI_FORMAT mainTextureFormat;
     DXGI_FORMAT mainTextureFormat;
-    ID3D12Resource *paletteTexture;
-    D3D12_CPU_DESCRIPTOR_HANDLE paletteTextureResourceView;
-    D3D12_RESOURCE_STATES paletteResourceState;
-    SIZE_T paletteSRVIndex;
     ID3D12Resource *stagingBuffer;
     ID3D12Resource *stagingBuffer;
     D3D12_RESOURCE_STATES stagingResourceState;
     D3D12_RESOURCE_STATES stagingResourceState;
     const float *YCbCr_matrix;
     const float *YCbCr_matrix;
@@ -289,6 +294,8 @@ static const GUID SDL_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9
 #pragma GCC diagnostic pop
 #pragma GCC diagnostic pop
 #endif
 #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)
 static UINT D3D12_Align(UINT location, UINT alignment)
 {
 {
     return (location + (alignment - 1)) & ~(alignment - 1);
     return (location + (alignment - 1)) & ~(alignment - 1);
@@ -1537,6 +1544,88 @@ static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D12R
     return true;
     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)
 static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
 {
     D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal;
     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;
     textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST;
     SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER, textureData->mainTexture);
     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
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
         texture->format == SDL_PIXELFORMAT_IYUV) {
         texture->format == SDL_PIXELFORMAT_IYUV) {
@@ -1723,19 +1789,6 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD
              &resourceViewDesc,
              &resourceViewDesc,
              textureData->mainTextureResourceView);
              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
 #ifdef SDL_HAVE_YUV
     if (textureData->yuv) {
     if (textureData->yuv) {
         D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewU);
         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->mainTexture);
     D3D_SAFE_RELEASE(textureData->stagingBuffer);
     D3D_SAFE_RELEASE(textureData->stagingBuffer);
     D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndex);
     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
 #ifdef SDL_HAVE_YUV
     D3D_SAFE_RELEASE(textureData->mainTextureU);
     D3D_SAFE_RELEASE(textureData->mainTextureU);
     D3D_SAFE_RELEASE(textureData->mainTextureV);
     D3D_SAFE_RELEASE(textureData->mainTextureV);
@@ -1969,19 +2018,6 @@ static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Re
     return true;
     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,
 static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                                const SDL_Rect *rect, const void *srcPixels,
                                const SDL_Rect *rect, const void *srcPixels,
                                int srcPitch)
                                int srcPitch)
@@ -2902,10 +2938,12 @@ static bool D3D12_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *
     }
     }
     shaderSamplers[numShaderSamplers++] = *textureSampler;
     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);
         textureSampler = D3D12_GetSamplerState(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
         if (!textureSampler) {
         if (!textureSampler) {
@@ -3366,8 +3404,10 @@ bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Proper
 
 
     renderer->WindowEvent = D3D12_WindowEvent;
     renderer->WindowEvent = D3D12_WindowEvent;
     renderer->SupportsBlendMode = D3D12_SupportsBlendMode;
     renderer->SupportsBlendMode = D3D12_SupportsBlendMode;
+    renderer->CreatePalette = D3D12_CreatePalette;
+    renderer->UpdatePalette = D3D12_UpdatePalette;
+    renderer->DestroyPalette = D3D12_DestroyPalette;
     renderer->CreateTexture = D3D12_CreateTexture;
     renderer->CreateTexture = D3D12_CreateTexture;
-    renderer->UpdateTexturePalette = D3D12_UpdateTexturePalette;
     renderer->UpdateTexture = D3D12_UpdateTexture;
     renderer->UpdateTexture = D3D12_UpdateTexture;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = D3D12_UpdateTextureYUV;
     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];
     SDL_GPUSampler *samplers[RENDER_SAMPLER_COUNT];
 } GPU_RenderData;
 } GPU_RenderData;
 
 
+typedef struct GPU_PaletteData
+{
+    SDL_GPUTexture *texture;
+} GPU_PaletteData;
+
 typedef struct GPU_TextureData
 typedef struct GPU_TextureData
 {
 {
     SDL_GPUTexture *texture;
     SDL_GPUTexture *texture;
-    SDL_GPUTexture *palette;
     SDL_GPUTextureFormat format;
     SDL_GPUTextureFormat format;
     GPU_FragmentShaderID shader;
     GPU_FragmentShaderID shader;
     void *pixels;
     void *pixels;
@@ -118,6 +122,88 @@ static bool GPU_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMod
     return true;
     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)
 static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
 {
     GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal;
     GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal;
@@ -184,16 +270,6 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_
         return false;
         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_PropertiesID props = SDL_GetTextureProperties(texture);
     SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_POINTER, data->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;
     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,
 static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                               const SDL_Rect *rect, const void *pixels, int pitch)
                               const SDL_Rect *rect, const void *pixels, int pitch)
 {
 {
@@ -663,9 +693,11 @@ static void Draw(
         sampler_bind.texture = tdata->texture;
         sampler_bind.texture = tdata->texture;
         SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1);
         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.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);
             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->texture);
-    SDL_ReleaseGPUTexture(renderdata->device, data->palette);
     SDL_free(data->pixels);
     SDL_free(data->pixels);
     SDL_free(data);
     SDL_free(data);
     texture->internal = NULL;
     texture->internal = NULL;
@@ -1242,8 +1273,10 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P
     }
     }
 
 
     renderer->SupportsBlendMode = GPU_SupportsBlendMode;
     renderer->SupportsBlendMode = GPU_SupportsBlendMode;
+    renderer->CreatePalette = GPU_CreatePalette;
+    renderer->UpdatePalette = GPU_UpdatePalette;
+    renderer->DestroyPalette = GPU_DestroyPalette;
     renderer->CreateTexture = GPU_CreateTexture;
     renderer->CreateTexture = GPU_CreateTexture;
-    renderer->UpdateTexturePalette = GPU_UpdateTexturePalette;
     renderer->UpdateTexture = GPU_UpdateTexture;
     renderer->UpdateTexture = GPU_UpdateTexture;
     renderer->LockTexture = GPU_LockTexture;
     renderer->LockTexture = GPU_LockTexture;
     renderer->UnlockTexture = GPU_UnlockTexture;
     renderer->UnlockTexture = GPU_UnlockTexture;

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

@@ -144,6 +144,14 @@ typedef struct METAL_ShaderPipelines
 @implementation SDL3METAL_RenderData
 @implementation SDL3METAL_RenderData
 @end
 @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
 @interface SDL3METAL_TextureData : NSObject
 @property(nonatomic, retain) id<MTLTexture> mtltexture;
 @property(nonatomic, retain) id<MTLTexture> mtltexture;
 @property(nonatomic, retain) id<MTLTexture> mtlpalette;
 @property(nonatomic, retain) id<MTLTexture> mtlpalette;
@@ -162,6 +170,10 @@ typedef struct METAL_ShaderPipelines
 @implementation SDL3METAL_TextureData
 @implementation SDL3METAL_TextureData
 @end
 @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 MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF;
 static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
 static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF;
 
 
@@ -611,13 +623,68 @@ size_t GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, in
     return 0;
     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)
 static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
 {
     @autoreleasepool {
     @autoreleasepool {
         SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal;
         SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal;
         MTLPixelFormat pixfmt;
         MTLPixelFormat pixfmt;
         MTLTextureDescriptor *mtltexdesc;
         MTLTextureDescriptor *mtltexdesc;
-        id<MTLTexture> mtltexture = nil, mtltextureUv = nil, mtlpalette = nil;
+        id<MTLTexture> mtltexture = nil, mtltextureUv = nil;
         SDL3METAL_TextureData *texturedata;
         SDL3METAL_TextureData *texturedata;
         CVPixelBufferRef pixelbuffer = nil;
         CVPixelBufferRef pixelbuffer = nil;
         IOSurfaceRef surface = 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");
             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;
         mtltextureUv = nil;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
         BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12);
         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.fragmentFunction = SDL_METAL_FRAGMENT_COPY;
         }
         }
         texturedata.mtltexture = mtltexture;
         texturedata.mtltexture = mtltexture;
-        texturedata.mtlpalette = mtlpalette;
         texturedata.mtltextureUv = mtltextureUv;
         texturedata.mtltextureUv = mtltextureUv;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
         texturedata.yuv = yuv;
         texturedata.yuv = yuv;
@@ -788,7 +835,7 @@ static MTLStorageMode METAL_GetStorageMode(id<MTLResource> resource)
     return resource.storageMode;
     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,
                                        id<MTLTexture> texture, SDL_Rect rect, int slice,
                                        const void *pixels, int pitch)
                                        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
     /* 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
      * use replaceRegion to upload to it directly. Otherwise we upload the data
      * to a staging texture and copy that over. */
      * 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);
         METAL_UploadTextureData(texture, rect, slice, pixels, pitch);
         return true;
         return true;
     }
     }
@@ -856,24 +903,13 @@ static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, SDL3METAL_Textur
     return true;
     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,
 static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                                const SDL_Rect *rect, const void *pixels, int pitch)
                                const SDL_Rect *rect, const void *pixels, int pitch)
 {
 {
     @autoreleasepool {
     @autoreleasepool {
         SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal;
         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;
             return false;
         }
         }
 #ifdef SDL_HAVE_YUV
 #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
             // Skip to the correct offset into the next texture
             pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
             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;
                 return false;
             }
             }
 
 
             // Skip to the correct offset into the next texture
             // Skip to the correct offset into the next texture
             pixels = (const void *)((const Uint8 *)pixels + UVrect.h * UVpitch);
             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;
                 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
             // Skip to the correct offset into the next texture
             pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch);
             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;
                 return false;
             }
             }
         }
         }
@@ -931,13 +967,13 @@ static bool METAL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture,
             return true;
             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;
             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;
             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;
             return false;
         }
         }
 
 
@@ -961,11 +997,11 @@ static bool METAL_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture,
             return true;
             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;
             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;
             return false;
         }
         }
 
 
@@ -1568,8 +1604,9 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c
 
 
     if (texture != statecache->texture) {
     if (texture != statecache->texture) {
         [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0];
         [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
 #ifdef SDL_HAVE_YUV
         if (texturedata.yuv || texturedata.nv12) {
         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_u = cmd->data.draw.texture_address_mode_u;
         statecache->texture_address_mode_v = cmd->data.draw.texture_address_mode_v;
         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) {
         if (!statecache->texture_palette) {
             id<MTLSamplerState> mtlsampler = GetSampler(data, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
             id<MTLSamplerState> mtlsampler = GetSampler(data, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
             if (mtlsampler == nil) {
             if (mtlsampler == nil) {
@@ -2201,8 +2238,10 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL
         renderer->WindowEvent = METAL_WindowEvent;
         renderer->WindowEvent = METAL_WindowEvent;
         renderer->GetOutputSize = METAL_GetOutputSize;
         renderer->GetOutputSize = METAL_GetOutputSize;
         renderer->SupportsBlendMode = METAL_SupportsBlendMode;
         renderer->SupportsBlendMode = METAL_SupportsBlendMode;
+        renderer->CreatePalette = METAL_CreatePalette;
+        renderer->UpdatePalette = METAL_UpdatePalette;
+        renderer->DestroyPalette = METAL_DestroyPalette;
         renderer->CreateTexture = METAL_CreateTexture;
         renderer->CreateTexture = METAL_CreateTexture;
-        renderer->UpdateTexturePalette = METAL_UpdateTexturePalette;
         renderer->UpdateTexture = METAL_UpdateTexture;
         renderer->UpdateTexture = METAL_UpdateTexture;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
         renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;
         renderer->UpdateTextureYUV = METAL_UpdateTextureYUV;

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

@@ -125,6 +125,11 @@ typedef struct
     GL_DrawStateCache drawstate;
     GL_DrawStateCache drawstate;
 } GL_RenderData;
 } GL_RenderData;
 
 
+typedef struct
+{
+    GLuint texture;
+} GL_PaletteData;
+
 typedef struct
 typedef struct
 {
 {
     GLuint texture;
     GLuint texture;
@@ -133,7 +138,6 @@ typedef struct
     GLfloat texh;
     GLfloat texh;
     GLenum format;
     GLenum format;
     GLenum formattype;
     GLenum formattype;
-    GLuint palette;
     GL_Shader shader;
     GL_Shader shader;
     float texel_size[4];
     float texel_size[4];
     const float *shader_params;
     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));
     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)
 static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
 {
     GL_RenderData *renderdata = (GL_RenderData *)renderer->internal;
     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);
     SetTextureScaleMode(renderdata, textype, data->texture_scale_mode);
     SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v);
     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
 #ifdef SDL_HAVE_YUV
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
     if (texture->format == SDL_PIXELFORMAT_YV12 ||
         texture->format == SDL_PIXELFORMAT_IYUV) {
         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);
     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,
 static bool GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                             const SDL_Rect *rect, const void *pixels, int pitch)
                             const SDL_Rect *rect, const void *pixels, int pitch)
 {
 {
@@ -1205,8 +1234,9 @@ static bool SetCopyState(GL_RenderData *data, const SDL_RenderCommand *cmd)
         }
         }
 #endif
 #endif
         if (texture->palette) {
         if (texture->palette) {
+            GL_PaletteData *palette = (GL_PaletteData *)texture->palette->internal;
             data->glActiveTextureARB(GL_TEXTURE1_ARB);
             data->glActiveTextureARB(GL_TEXTURE1_ARB);
-            data->glBindTexture(textype, texturedata->palette);
+            data->glBindTexture(textype, palette->texture);
         }
         }
         if (data->GL_ARB_multitexture_supported) {
         if (data->GL_ARB_multitexture_supported) {
             data->glActiveTextureARB(GL_TEXTURE0_ARB);
             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) {
     if (data->texture && !data->texture_external) {
         renderdata->glDeleteTextures(1, &data->texture);
         renderdata->glDeleteTextures(1, &data->texture);
     }
     }
-    if (data->palette) {
-        renderdata->glDeleteTextures(1, &data->palette);
-    }
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     if (data->yuv) {
     if (data->yuv) {
         if (!data->utexture_external) {
         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->WindowEvent = GL_WindowEvent;
     renderer->SupportsBlendMode = GL_SupportsBlendMode;
     renderer->SupportsBlendMode = GL_SupportsBlendMode;
+    renderer->CreatePalette = GL_CreatePalette;
+    renderer->UpdatePalette = GL_UpdatePalette;
+    renderer->DestroyPalette = GL_DestroyPalette;
     renderer->CreateTexture = GL_CreateTexture;
     renderer->CreateTexture = GL_CreateTexture;
-    renderer->UpdateTexturePalette = GL_UpdateTexturePalette;
     renderer->UpdateTexture = GL_UpdateTexture;
     renderer->UpdateTexture = GL_UpdateTexture;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = GL_UpdateTextureYUV;
     renderer->UpdateTextureYUV = GL_UpdateTextureYUV;

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

@@ -58,14 +58,18 @@ struct GLES2_FBOList
     GLES2_FBOList *next;
     GLES2_FBOList *next;
 };
 };
 
 
-typedef struct GLES2_TextureData
+typedef struct
+{
+    GLuint texture;
+} GLES2_PaletteData;
+
+typedef struct
 {
 {
     GLuint texture;
     GLuint texture;
     bool texture_external;
     bool texture_external;
     GLenum texture_type;
     GLenum texture_type;
     GLenum pixel_format;
     GLenum pixel_format;
     GLenum pixel_type;
     GLenum pixel_type;
-    GLuint palette;
     void *pixel_data;
     void *pixel_data;
     int pitch;
     int pitch;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
@@ -1277,8 +1281,9 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v
         }
         }
 #endif
 #endif
         if (texture->palette) {
         if (texture->palette) {
+            GLES2_PaletteData *palette = (GLES2_PaletteData *)texture->palette->internal;
             data->glActiveTexture(GL_TEXTURE1);
             data->glActiveTexture(GL_TEXTURE1);
-            data->glBindTexture(tdata->texture_type, tdata->palette);
+            data->glBindTexture(tdata->texture_type, palette->texture);
 
 
             data->glActiveTexture(GL_TEXTURE0);
             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)
 static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
 {
     GLES2_RenderData *renderdata = (GLES2_RenderData *)renderer->internal;
     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[0] = 1.0f / data->texel_size[2];
     data->texel_size[1] = 1.0f / data->texel_size[3];
     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
 #ifdef SDL_HAVE_YUV
     if (data->yuv) {
     if (data->yuv) {
         data->texture_v = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER, 0);
         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;
     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,
 static bool GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect,
                                const void *pixels, int pitch)
                                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) {
         if (tdata->texture && !tdata->texture_external) {
             data->glDeleteTextures(1, &tdata->texture);
             data->glDeleteTextures(1, &tdata->texture);
         }
         }
-        if (tdata->palette) {
-            data->glDeleteTextures(1, &tdata->palette);
-        }
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
         if (tdata->texture_v && !tdata->texture_v_external) {
         if (tdata->texture_v && !tdata->texture_v_external) {
             data->glDeleteTextures(1, &tdata->texture_v);
             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
     // Populate the function pointers for the module
     renderer->WindowEvent = GLES2_WindowEvent;
     renderer->WindowEvent = GLES2_WindowEvent;
     renderer->SupportsBlendMode = GLES2_SupportsBlendMode;
     renderer->SupportsBlendMode = GLES2_SupportsBlendMode;
+    renderer->CreatePalette = GLES2_CreatePalette;
+    renderer->UpdatePalette = GLES2_UpdatePalette;
+    renderer->DestroyPalette = GLES2_DestroyPalette;
     renderer->CreateTexture = GLES2_CreateTexture;
     renderer->CreateTexture = GLES2_CreateTexture;
-    renderer->UpdateTexturePalette = GLES2_UpdateTexturePalette;
     renderer->UpdateTexture = GLES2_UpdateTexture;
     renderer->UpdateTexture = GLES2_UpdateTexture;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = GLES2_UpdateTextureYUV;
     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");
     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)
 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);
     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;
     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,
 static bool SW_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                             const SDL_Rect *rect, const void *pixels, int pitch)
                             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->WindowEvent = SW_WindowEvent;
     renderer->GetOutputSize = SW_GetOutputSize;
     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->CreateTexture = SW_CreateTexture;
-    renderer->UpdateTexturePalette = SW_UpdateTexturePalette;
     renderer->UpdateTexture = SW_UpdateTexture;
     renderer->UpdateTexture = SW_UpdateTexture;
     renderer->LockTexture = SW_LockTexture;
     renderer->LockTexture = SW_LockTexture;
     renderer->UnlockTexture = SW_UnlockTexture;
     renderer->UnlockTexture = SW_UnlockTexture;

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

@@ -240,11 +240,16 @@ typedef struct
     VkFormat format;
     VkFormat format;
 } VULKAN_Image;
 } VULKAN_Image;
 
 
+// Per-palette data
+typedef struct
+{
+    VULKAN_Image image;
+} VULKAN_PaletteData;
+
 // Per-texture data
 // Per-texture data
 typedef struct
 typedef struct
 {
 {
     VULKAN_Image mainImage;
     VULKAN_Image mainImage;
-    VULKAN_Image paletteImage;
     VkRenderPass mainRenderpasses[VULKAN_RENDERPASS_COUNT];
     VkRenderPass mainRenderpasses[VULKAN_RENDERPASS_COUNT];
     VkFramebuffer mainFramebuffer;
     VkFramebuffer mainFramebuffer;
     VULKAN_Buffer stagingBuffer;
     VULKAN_Buffer stagingBuffer;
@@ -384,6 +389,8 @@ typedef struct
     bool issueBatch;
     bool issueBatch;
 } VULKAN_RenderData;
 } 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)
 static SDL_PixelFormat VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat)
 {
 {
     switch (vkFormat) {
     switch (vkFormat) {
@@ -2533,6 +2540,45 @@ static bool VULKAN_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blend
     return true;
     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)
 static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
 {
 {
     VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
     VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal;
@@ -2694,21 +2740,6 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S
         return false;
         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_PropertiesID props = SDL_GetTextureProperties(texture);
     SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER, (Sint64)textureData->mainImage.image);
     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);
     VULKAN_DestroyImage(rendererData, &textureData->mainImage);
 
 
-    if (texture->format == SDL_PIXELFORMAT_INDEX8) {
-        VULKAN_DestroyImage(rendererData, &textureData->paletteImage);
-    }
-
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     if (textureData->samplerYcbcrConversion != VK_NULL_HANDLE) {
     if (textureData->samplerYcbcrConversion != VK_NULL_HANDLE) {
         vkDestroySamplerYcbcrConversionKHR(rendererData->device, textureData->samplerYcbcrConversion, NULL);
         vkDestroySamplerYcbcrConversionKHR(rendererData->device, textureData->samplerYcbcrConversion, NULL);
@@ -2876,20 +2903,6 @@ static bool VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImag
     return true;
     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,
 static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
                                const SDL_Rect *rect, const void *srcPixels,
                                const SDL_Rect *rect, const void *srcPixels,
                                int srcPitch)
                                int srcPitch)
@@ -3900,8 +3913,9 @@ static bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand
     }
     }
     ++numSamplers;
     ++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;
             bool stoppedRenderPass = false;
             if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
             if (rendererData->currentRenderPass != VK_NULL_HANDLE) {
                 vkCmdEndRenderPass(rendererData->currentCommandBuffer);
                 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_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
                 VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
                 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
                 VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
-                textureData->paletteImage.image,
-                &textureData->paletteImage.imageLayout);
+                palette->image.image,
+                &palette->image.imageLayout);
 
 
             if (stoppedRenderPass) {
             if (stoppedRenderPass) {
                 VULKAN_BeginRenderPass(rendererData, VK_ATTACHMENT_LOAD_OP_LOAD, NULL);
                 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);
         samplers[numSamplers] = VULKAN_GetSampler(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP);
         if (samplers[numSamplers] == VK_NULL_HANDLE) {
         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->WindowEvent = VULKAN_WindowEvent;
     renderer->SupportsBlendMode = VULKAN_SupportsBlendMode;
     renderer->SupportsBlendMode = VULKAN_SupportsBlendMode;
+    renderer->CreatePalette = VULKAN_CreatePalette;
+    renderer->UpdatePalette = VULKAN_UpdatePalette;
+    renderer->DestroyPalette = VULKAN_DestroyPalette;
     renderer->CreateTexture = VULKAN_CreateTexture;
     renderer->CreateTexture = VULKAN_CreateTexture;
-    renderer->UpdateTexturePalette = VULKAN_UpdateTexturePalette;
     renderer->UpdateTexture = VULKAN_UpdateTexture;
     renderer->UpdateTexture = VULKAN_UpdateTexture;
 #ifdef SDL_HAVE_YUV
 #ifdef SDL_HAVE_YUV
     renderer->UpdateTextureYUV = VULKAN_UpdateTextureYUV;
     renderer->UpdateTextureYUV = VULKAN_UpdateTextureYUV;