diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c index 4b1cecef44..0bfa5a9097 100644 --- a/src/render/SDL_render.c +++ b/src/render/SDL_render.c @@ -347,6 +347,15 @@ static bool FlushRenderCommandsIfTextureNeeded(SDL_Texture *texture) return true; } +static bool FlushRenderCommandsIfPaletteNeeded(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + if (palette->last_command_generation == renderer->render_command_generation) { + // the current command queue depends on this palette, flush the queue now before it changes + return FlushRenderCommands(renderer); + } + return true; +} + static bool FlushRenderCommandsIfGPURenderStateNeeded(SDL_GPURenderState *state) { SDL_Renderer *renderer = state->renderer; @@ -700,20 +709,22 @@ static bool QueueCmdFillRects(SDL_Renderer *renderer, const SDL_FRect *rects, co static bool UpdateTexturePalette(SDL_Texture *texture) { SDL_Renderer *renderer = texture->renderer; + SDL_Palette *public = texture->public_palette; if (!SDL_ISPIXELFORMAT_INDEXED(texture->format)) { return true; } - if (!texture->palette) { + if (!public) { return SDL_SetError("Texture doesn't have a palette"); } - if (texture->palette_version == texture->palette->version) { - return true; - } - if (texture->native) { + // Keep the native texture in sync with palette updates + if (texture->palette_version == public->version) { + return true; + } + if (!FlushRenderCommandsIfTextureNeeded(texture->native)) { return false; } @@ -727,17 +738,25 @@ static bool UpdateTexturePalette(SDL_Texture *texture) if (!result) { return false; } - } else { - if (!FlushRenderCommandsIfTextureNeeded(texture)) { - return false; - } - - if (!renderer->UpdateTexturePalette(renderer, texture)) { - return false; - } + texture->palette_version = public->version; + return true; } - texture->palette_version = texture->palette->version; + SDL_TexturePalette *palette = texture->palette; + if (palette->version != public->version) { + // Keep the native palette in sync with palette updates + if (!FlushRenderCommandsIfPaletteNeeded(renderer, palette)) { + return false; + } + + if (!renderer->UpdatePalette(renderer, palette, public->ncolors, public->colors)) { + return false; + } + + palette->version = public->version; + } + + palette->last_command_generation = renderer->render_command_generation; return true; } @@ -1143,6 +1162,11 @@ SDL_Renderer *SDL_CreateRendererWithProperties(SDL_PropertiesID props) UpdatePixelClipRect(renderer, &renderer->main_view); UpdateMainViewDimensions(renderer); + renderer->palettes = SDL_CreateHashTable(0, false, SDL_HashPointer, SDL_KeyMatchPointer, SDL_DestroyHashValue, NULL); + if (!renderer->palettes) { + goto error; + } + // new textures start at zero, so we start at 1 so first render doesn't flush by accident. renderer->render_command_generation = 1; @@ -1656,7 +1680,7 @@ static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, S bool direct_update; if (surface->format == texture->format && - surface->palette == texture->palette && + surface->palette == texture->public_palette && SDL_GetSurfaceColorspace(surface) == texture->colorspace) { if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) { /* Surface and Renderer formats are identical. @@ -1682,7 +1706,7 @@ static bool SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, S } } else { // Set up a destination surface for the texture update - SDL_Surface *temp = SDL_ConvertSurfaceAndColorspace(surface, texture->format, texture->palette, texture->colorspace, SDL_GetSurfaceProperties(surface)); + SDL_Surface *temp = SDL_ConvertSurfaceAndColorspace(surface, texture->format, texture->public_palette, texture->colorspace, SDL_GetSurfaceProperties(surface)); if (temp) { SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch); SDL_DestroySurface(temp); @@ -1901,16 +1925,56 @@ bool SDL_SetTexturePalette(SDL_Texture *texture, SDL_Palette *palette) return SDL_SetError("SDL_SetSurfacePalette() passed a palette that doesn't match the surface format"); } - if (palette != texture->palette) { - if (texture->palette) { - SDL_DestroyPalette(texture->palette); + if (palette != texture->public_palette) { + SDL_Renderer *renderer = texture->renderer; + + if (texture->public_palette) { + SDL_DestroyPalette(texture->public_palette); + + if (!texture->native) { + // Clean up the texture palette + --texture->palette->refcount; + if (texture->palette->refcount == 0) { + renderer->DestroyPalette(renderer, texture->palette); + SDL_RemoveFromHashTable(renderer->palettes, texture->public_palette); + } + texture->palette = NULL; + } } - texture->palette = palette; + texture->public_palette = palette; texture->palette_version = 0; - if (texture->palette) { - ++texture->palette->refcount; + if (texture->public_palette) { + ++texture->public_palette->refcount; + + if (!texture->native) { + if (SDL_FindInHashTable(renderer->palettes, palette, (const void **)&texture->palette)) { + ++texture->palette->refcount; + } else { + SDL_TexturePalette *texture_palette = (SDL_TexturePalette *)SDL_calloc(1, sizeof(*texture_palette)); + if (!texture_palette) { + SDL_SetTexturePalette(texture, NULL); + return false; + } + if (!renderer->CreatePalette(renderer, texture_palette)) { + renderer->DestroyPalette(renderer, texture_palette); + SDL_SetTexturePalette(texture, NULL); + return false; + } + texture->palette = texture_palette; + texture->palette->refcount = 1; + + if (!SDL_InsertIntoHashTable(renderer->palettes, palette, texture->palette, false)) { + SDL_SetTexturePalette(texture, NULL); + return false; + } + } + } + + if (!texture->native && renderer->ChangeTexturePalette) { + renderer->ChangeTexturePalette(renderer, texture); + } } if (texture->palette_surface) { @@ -1924,7 +1988,7 @@ SDL_Palette *SDL_GetTexturePalette(SDL_Texture *texture) { CHECK_TEXTURE_MAGIC(texture, NULL); - return texture->palette; + return texture->public_palette; } bool SDL_SetTextureColorMod(SDL_Texture *texture, Uint8 r, Uint8 g, Uint8 b) @@ -2602,8 +2666,8 @@ bool SDL_LockTextureToSurface(SDL_Texture *texture, const SDL_Rect *rect, SDL_Su SDL_UnlockTexture(texture); return false; } - if (texture->palette) { - SDL_SetSurfacePalette(texture->locked_surface, texture->palette); + if (texture->public_palette) { + SDL_SetSurfacePalette(texture->locked_surface, texture->public_palette); } *surface = texture->locked_surface; @@ -5513,7 +5577,7 @@ static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying) { SDL_Renderer *renderer; - if (texture->palette) { + if (texture->public_palette) { SDL_SetTexturePalette(texture, NULL); } @@ -5634,6 +5698,13 @@ void SDL_DestroyRendererWithoutFreeing(SDL_Renderer *renderer) SDL_assert(tex != renderer->textures); // satisfy static analysis. } + // Free palette cache, which should be empty now + if (renderer->palettes) { + SDL_assert(SDL_HashTableEmpty(renderer->palettes)); + SDL_DestroyHashTable(renderer->palettes); + renderer->palettes = NULL; + } + // Clean up renderer-specific resources if (renderer->DestroyRenderer) { renderer->DestroyRenderer(renderer); diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h index 660d022d53..b4bb15ac88 100644 --- a/src/render/SDL_sysrender.h +++ b/src/render/SDL_sysrender.h @@ -70,6 +70,15 @@ typedef struct SDL_RenderViewState SDL_FPoint current_scale; // this is just `scale * logical_scale`, precalculated, since we use it a lot. } SDL_RenderViewState; +// Define the SDL texture palette structure +typedef struct SDL_TexturePalette +{ + int refcount; + Uint32 version; + Uint32 last_command_generation; // last command queue generation this palette was in. + void *internal; // Driver specific palette representation +} SDL_TexturePalette; + // Define the SDL texture structure struct SDL_Texture { @@ -90,7 +99,8 @@ struct SDL_Texture SDL_ScaleMode scaleMode; // The texture scale mode SDL_FColor color; // Texture modulation values SDL_RenderViewState view; // Target texture view state - SDL_Palette *palette; + SDL_Palette *public_palette; + SDL_TexturePalette *palette; Uint32 palette_version; SDL_Surface *palette_surface; @@ -235,7 +245,10 @@ struct SDL_Renderer void (*InvalidateCachedState)(SDL_Renderer *renderer); bool (*RunCommandQueue)(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize); - bool (*UpdateTexturePalette)(SDL_Renderer *renderer, SDL_Texture *texture); + bool (*CreatePalette)(SDL_Renderer *renderer, SDL_TexturePalette *palette); + bool (*UpdatePalette)(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors); + void (*DestroyPalette)(SDL_Renderer *renderer, SDL_TexturePalette *palette); + bool (*ChangeTexturePalette)(SDL_Renderer *renderer, SDL_Texture *texture); bool (*UpdateTexture)(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch); @@ -301,6 +314,9 @@ struct SDL_Renderer SDL_Texture *target; SDL_Mutex *target_mutex; + // The list of palettes + SDL_HashTable *palettes; + SDL_Colorspace output_colorspace; float SDR_white_point; float HDR_headroom; diff --git a/src/render/direct3d/SDL_render_d3d.c b/src/render/direct3d/SDL_render_d3d.c index 9131a59df0..e72c49e34e 100644 --- a/src/render/direct3d/SDL_render_d3d.c +++ b/src/render/direct3d/SDL_render_d3d.c @@ -48,6 +48,26 @@ typedef struct const float *shader_params; } D3D_DrawStateCache; +typedef struct +{ + bool dirty; + int w, h; + DWORD usage; + Uint32 format; + D3DFORMAT d3dfmt; + IDirect3DTexture9 *texture; + IDirect3DTexture9 *staging; +} D3D_TextureRep; + +struct D3D_PaletteData +{ + D3D_TextureRep texture; + + struct D3D_PaletteData *prev; + struct D3D_PaletteData *next; +}; +typedef struct D3D_PaletteData D3D_PaletteData; + // Direct3D renderer implementation typedef struct @@ -74,23 +94,12 @@ typedef struct int currentVertexBuffer; bool reportedVboProblem; D3D_DrawStateCache drawstate; + D3D_PaletteData *palettes; } D3D_RenderData; -typedef struct -{ - bool dirty; - int w, h; - DWORD usage; - Uint32 format; - D3DFORMAT d3dfmt; - IDirect3DTexture9 *texture; - IDirect3DTexture9 *staging; -} D3D_TextureRep; - typedef struct { D3D_TextureRep texture; - D3D_TextureRep palette; D3D9_Shader shader; const float *shader_params; @@ -532,6 +541,99 @@ static void D3D_DestroyTextureRep(D3D_TextureRep *texture) } } +static bool D3D_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + D3D_RenderData *data = (D3D_RenderData *)renderer->internal; + D3D_PaletteData *palettedata = (D3D_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); + if (!palettedata) { + return false; + } + palette->internal = palettedata; + + if (!D3D_CreateTextureRep(data->device, &palettedata->texture, 0, SDL_PIXELFORMAT_ARGB8888, D3DFMT_A8R8G8B8, 256, 1)) { + SDL_free(palettedata); + return false; + } + + // Keep a reference to the palette so we can restore the texture if we lose the D3D device + if (data->palettes) { + palettedata->next = data->palettes; + data->palettes->prev = palettedata; + } + data->palettes = palettedata; + return true; +} + +static bool D3D_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + D3D_RenderData *data = (D3D_RenderData *)renderer->internal; + D3D_PaletteData *palettedata = (D3D_PaletteData *)palette->internal; + bool retval; + + Uint32 *entries = SDL_stack_alloc(Uint32, ncolors); + if (!entries) { + return false; + } + for (int i = 0; i < ncolors; ++i) { + entries[i] = (colors[i].a << 24) | (colors[i].r << 16) | (colors[i].g << 8) | colors[i].b; + } + retval = D3D_UpdateTextureRep(data->device, &palettedata->texture, 0, 0, ncolors, 1, entries, ncolors * sizeof(*entries)); + SDL_stack_free(entries); + return retval; +} + +static void D3D_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + D3D_RenderData *data = (D3D_RenderData *)renderer->internal; + D3D_PaletteData *palettedata = (D3D_PaletteData *)palette->internal; + + if (palettedata) { + D3D_DestroyTextureRep(&palettedata->texture); + + if (data->palettes == palettedata) { + data->palettes = palettedata->next; + } else if (palettedata->prev) { + palettedata->prev->next = palettedata->next; + } + if (palettedata->next) { + palettedata->next->prev = palettedata->prev; + } + SDL_free(palettedata); + } +} + +static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, const void *pixels, int pitch) +{ + D3D_RenderData *data = (D3D_RenderData *)renderer->internal; + D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; + + if (!texturedata) { + return SDL_SetError("Texture is not currently available"); + } + + if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) { + return false; + } +#ifdef SDL_HAVE_YUV + if (texturedata->yuv) { + // Skip to the correct offset into the next texture + pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); + + if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { + return false; + } + + // Skip to the correct offset into the next texture + pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); + if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { + return false; + } + } +#endif + return true; +} + static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { D3D_RenderData *data = (D3D_RenderData *)renderer->internal; @@ -555,10 +657,6 @@ static bool D3D_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ return false; } if (texture->format == SDL_PIXELFORMAT_INDEX8) { - if (!D3D_CreateTextureRep(data->device, &texturedata->palette, usage, SDL_PIXELFORMAT_ARGB8888, D3DFMT_A8R8G8B8, 256, 1)) { - return false; - } - texturedata->shader = SHADER_PALETTE; } #ifdef SDL_HAVE_YUV @@ -596,12 +694,6 @@ static bool D3D_RecreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (!D3D_RecreateTextureRep(data->device, &texturedata->texture)) { return false; } - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - if (!D3D_RecreateTextureRep(data->device, &texturedata->palette)) { - return false; - } - texture->palette_version = 0; - } #ifdef SDL_HAVE_YUV if (texturedata->yuv) { if (!D3D_RecreateTextureRep(data->device, &texturedata->utexture)) { @@ -616,56 +708,6 @@ static bool D3D_RecreateTexture(SDL_Renderer *renderer, SDL_Texture *texture) return true; } -static bool D3D_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - D3D_RenderData *data = (D3D_RenderData *)renderer->internal; - D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; - const int ncolors = texture->palette->ncolors; - const SDL_Color *colors = texture->palette->colors; - Uint32 palette[256]; - - if (!texturedata) { - return SDL_SetError("Texture is not currently available"); - } - - for (int i = 0; i < ncolors; ++i) { - palette[i] = (colors[i].a << 24) | (colors[i].r << 16) | (colors[i].g << 8) | colors[i].b; - } - return D3D_UpdateTextureRep(data->device, &texturedata->palette, 0, 0, 256, 1, palette, sizeof(palette)); -} - -static bool D3D_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, - const SDL_Rect *rect, const void *pixels, int pitch) -{ - D3D_RenderData *data = (D3D_RenderData *)renderer->internal; - D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; - - if (!texturedata) { - return SDL_SetError("Texture is not currently available"); - } - - if (!D3D_UpdateTextureRep(data->device, &texturedata->texture, rect->x, rect->y, rect->w, rect->h, pixels, pitch)) { - return false; - } -#ifdef SDL_HAVE_YUV - if (texturedata->yuv) { - // Skip to the correct offset into the next texture - pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); - - if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->vtexture : &texturedata->utexture, rect->x / 2, rect->y / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { - return false; - } - - // Skip to the correct offset into the next texture - pixels = (const void *)((const Uint8 *)pixels + ((rect->h + 1) / 2) * ((pitch + 1) / 2)); - if (!D3D_UpdateTextureRep(data->device, texture->format == SDL_PIXELFORMAT_YV12 ? &texturedata->utexture : &texturedata->vtexture, rect->x / 2, (rect->y + 1) / 2, (rect->w + 1) / 2, (rect->h + 1) / 2, pixels, (pitch + 1) / 2)) { - return false; - } - } -#endif - return true; -} - #ifdef SDL_HAVE_YUV static bool D3D_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, @@ -1013,8 +1055,9 @@ static bool SetupTextureState(D3D_RenderData *data, SDL_Texture *texture, D3D9_S if (!BindTextureRep(data->device, &texturedata->texture, 0)) { return false; } - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - if (!BindTextureRep(data->device, &texturedata->palette, 1)) { + if (texture->palette) { + D3D_PaletteData *palette = (D3D_PaletteData *)texture->palette->internal; + if (!BindTextureRep(data->device, &palette->texture, 1)) { return false; } } @@ -1046,8 +1089,8 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) if (!texture) { IDirect3DDevice9_SetTexture(data->device, 0, NULL); } - if ((!newtexturedata || (texture->format != SDL_PIXELFORMAT_INDEX8)) && - (oldtexturedata && (data->drawstate.texture->format == SDL_PIXELFORMAT_INDEX8))) { + if ((!newtexturedata || !texture->palette) && + (oldtexturedata && (data->drawstate.texture->palette))) { IDirect3DDevice9_SetTexture(data->device, 1, NULL); } #ifdef SDL_HAVE_YUV @@ -1086,8 +1129,9 @@ static bool SetDrawState(D3D_RenderData *data, const SDL_RenderCommand *cmd) D3D_TextureData *texturedata = (D3D_TextureData *)texture->internal; if (texturedata) { UpdateDirtyTexture(data->device, &texturedata->texture); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - UpdateDirtyTexture(data->device, &texturedata->palette); + if (texture->palette) { + D3D_PaletteData *palettedata = (D3D_PaletteData *)texture->palette->internal; + UpdateDirtyTexture(data->device, &palettedata->texture); } #ifdef SDL_HAVE_YUV if (texturedata->yuv) { @@ -1488,7 +1532,7 @@ static void D3D_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) renderdata->drawstate.shader_params = NULL; IDirect3DDevice9_SetPixelShader(renderdata->device, NULL); IDirect3DDevice9_SetTexture(renderdata->device, 0, NULL); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { + if (texture->palette) { IDirect3DDevice9_SetTexture(renderdata->device, 1, NULL); } #ifdef SDL_HAVE_YUV @@ -1520,6 +1564,9 @@ static void D3D_DestroyRenderer(SDL_Renderer *renderer) if (data) { int i; + // Make sure the palettes have been freed + SDL_assert(!data->palettes); + // Release the render target if (data->defaultRenderTarget) { IDirect3DSurface9_Release(data->defaultRenderTarget); @@ -1560,6 +1607,7 @@ static bool D3D_Reset(SDL_Renderer *renderer) const Float4X4 d3dmatrix = MatrixIdentity(); HRESULT result; SDL_Texture *texture; + D3D_PaletteData *palette; int i; // Cancel any scene that we've started @@ -1587,6 +1635,11 @@ static bool D3D_Reset(SDL_Renderer *renderer) } } + // Release all palettes + for (palette = data->palettes; palette; palette = palette->next) { + D3D_RecreateTextureRep(data->device, &palette->texture); + } + // Release all vertex buffers for (i = 0; i < SDL_arraysize(data->vertexBuffers); ++i) { if (data->vertexBuffers[i]) { @@ -1711,8 +1764,10 @@ static bool D3D_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P renderer->WindowEvent = D3D_WindowEvent; renderer->SupportsBlendMode = D3D_SupportsBlendMode; + renderer->CreatePalette = D3D_CreatePalette; + renderer->UpdatePalette = D3D_UpdatePalette; + renderer->DestroyPalette = D3D_DestroyPalette; renderer->CreateTexture = D3D_CreateTexture; - renderer->UpdateTexturePalette = D3D_UpdateTexturePalette; renderer->UpdateTexture = D3D_UpdateTexture; #ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = D3D_UpdateTextureYUV; diff --git a/src/render/direct3d11/SDL_render_d3d11.c b/src/render/direct3d11/SDL_render_d3d11.c index 94c7c0fdeb..dec879e0d8 100644 --- a/src/render/direct3d11/SDL_render_d3d11.c +++ b/src/render/direct3d11/SDL_render_d3d11.c @@ -114,6 +114,13 @@ typedef struct SDL_FColor color; } D3D11_VertexPositionColor; +// Per-palette data +typedef struct +{ + ID3D11Texture2D *texture; + ID3D11ShaderResourceView *resourceView; +} D3D11_PaletteData; + // Per-texture data typedef struct { @@ -121,8 +128,6 @@ typedef struct ID3D11Texture2D *mainTexture; ID3D11ShaderResourceView *mainTextureResourceView; ID3D11RenderTargetView *mainTextureRenderTargetView; - ID3D11Texture2D *paletteTexture; - ID3D11ShaderResourceView *paletteTextureResourceView; ID3D11Texture2D *stagingTexture; int lockedTexturePositionX; int lockedTexturePositionY; @@ -232,6 +237,8 @@ static const GUID SDL_DXGI_DEBUG_ALL = { 0xe48ae283, 0xda80, 0x490b, { 0x87, 0xe #pragma GCC diagnostic pop #endif +static bool D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Texture2D *texture, int bpp, int x, int y, int w, int h, const void *pixels, int pitch); + SDL_PixelFormat D3D11_DXGIFormatToSDLPixelFormat(DXGI_FORMAT dxgiFormat) { switch (dxgiFormat) { @@ -1179,6 +1186,73 @@ static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D11T return true; } +static bool D3D11_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal; + D3D11_PaletteData *palettedata = (D3D11_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); + if (!palettedata) { + return false; + } + palette->internal = palettedata; + + if (!data->d3dDevice) { + return SDL_SetError("Device lost and couldn't be recovered"); + } + + D3D11_TEXTURE2D_DESC textureDesc; + SDL_zero(textureDesc); + textureDesc.Width = 256; + textureDesc.Height = 1; + textureDesc.MipLevels = 1; + textureDesc.ArraySize = 1; + textureDesc.Format = SDLPixelFormatToDXGITextureFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace); + textureDesc.SampleDesc.Count = 1; + textureDesc.SampleDesc.Quality = 0; + textureDesc.MiscFlags = 0; + textureDesc.Usage = D3D11_USAGE_DEFAULT; + textureDesc.CPUAccessFlags = 0; + textureDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + + HRESULT result = ID3D11Device_CreateTexture2D(data->d3dDevice, &textureDesc, NULL, &palettedata->texture); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result); + } + + D3D11_SHADER_RESOURCE_VIEW_DESC resourceViewDesc; + SDL_zero(resourceViewDesc); + resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace); + resourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + resourceViewDesc.Texture2D.MostDetailedMip = 0; + resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels; + result = ID3D11Device_CreateShaderResourceView(data->d3dDevice, + (ID3D11Resource *)palettedata->texture, + &resourceViewDesc, + &palettedata->resourceView); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result); + } + return true; +} + +static bool D3D11_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + D3D11_RenderData *data = (D3D11_RenderData *)renderer->internal; + D3D11_PaletteData *palettedata = (D3D11_PaletteData *)palette->internal; + + return D3D11_UpdateTextureInternal(data, palettedata->texture, 4, 0, 0, ncolors, 1, colors, ncolors * sizeof(*colors)); +} + +static void D3D11_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + D3D11_PaletteData *palettedata = (D3D11_PaletteData *)palette->internal; + + if (palettedata) { + SAFE_RELEASE(palettedata->texture); + SAFE_RELEASE(palettedata->resourceView); + SDL_free(palettedata); + } +} + static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal; @@ -1252,24 +1326,6 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD } SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D11_TEXTURE_POINTER, textureData->mainTexture); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - textureDesc.Width = 256; - textureDesc.Height = 1; - if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { - textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - } else { - textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - } - - result = ID3D11Device_CreateTexture2D(rendererData->d3dDevice, - &textureDesc, - NULL, - &textureData->paletteTexture); - if (FAILED(result)) { - return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateTexture2D"), result); - } - } - #ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV) { @@ -1345,17 +1401,6 @@ static bool D3D11_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result); } - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - resourceViewDesc.Format = textureDesc.Format; - result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice, - (ID3D11Resource *)textureData->paletteTexture, - &resourceViewDesc, - &textureData->paletteTextureResourceView); - if (FAILED(result)) { - return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D11Device1::CreateShaderResourceView"), result); - } - } - #ifdef SDL_HAVE_YUV if (textureData->yuv) { result = ID3D11Device_CreateShaderResourceView(rendererData->d3dDevice, @@ -1424,8 +1469,6 @@ static void D3D11_DestroyTexture(SDL_Renderer *renderer, SAFE_RELEASE(data->mainTexture); SAFE_RELEASE(data->mainTextureResourceView); SAFE_RELEASE(data->mainTextureRenderTargetView); - SAFE_RELEASE(data->paletteTexture); - SAFE_RELEASE(data->paletteTextureResourceView); SAFE_RELEASE(data->stagingTexture); #ifdef SDL_HAVE_YUV SAFE_RELEASE(data->mainTextureU); @@ -1542,19 +1585,6 @@ static bool D3D11_UpdateTextureInternal(D3D11_RenderData *rendererData, ID3D11Te return true; } -static bool D3D11_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - D3D11_RenderData *rendererData = (D3D11_RenderData *)renderer->internal; - D3D11_TextureData *textureData = (D3D11_TextureData *)texture->internal; - SDL_Palette *palette = texture->palette; - - if (!textureData) { - return SDL_SetError("Texture is not currently available"); - } - - return D3D11_UpdateTextureInternal(rendererData, textureData->paletteTexture, 4, 0, 0, palette->ncolors, 1, palette->colors, palette->ncolors * sizeof(*palette->colors)); -} - #ifdef SDL_HAVE_YUV static bool D3D11_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, @@ -2491,8 +2521,10 @@ static bool D3D11_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand * } ++numShaderSamplers; - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - shaderResources[numShaderResources++] = textureData->paletteTextureResourceView; + if (texture->palette) { + D3D11_PaletteData *palette = (D3D11_PaletteData *)texture->palette->internal; + + shaderResources[numShaderResources++] = palette->resourceView; shaderSamplers[numShaderSamplers] = D3D11_GetSamplerState(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); if (!shaderSamplers[numShaderSamplers]) { @@ -2846,8 +2878,10 @@ static bool D3D11_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL renderer->WindowEvent = D3D11_WindowEvent; renderer->SupportsBlendMode = D3D11_SupportsBlendMode; + renderer->CreatePalette = D3D11_CreatePalette; + renderer->UpdatePalette = D3D11_UpdatePalette; + renderer->DestroyPalette = D3D11_DestroyPalette; renderer->CreateTexture = D3D11_CreateTexture; - renderer->UpdateTexturePalette = D3D11_UpdateTexturePalette; renderer->UpdateTexture = D3D11_UpdateTexture; #ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = D3D11_UpdateTextureYUV; diff --git a/src/render/direct3d12/SDL_render_d3d12.c b/src/render/direct3d12/SDL_render_d3d12.c index 1a9bd5a305..64ba7a5b87 100644 --- a/src/render/direct3d12/SDL_render_d3d12.c +++ b/src/render/direct3d12/SDL_render_d3d12.c @@ -109,6 +109,15 @@ typedef struct SDL_FColor color; } D3D12_VertexPositionColor; +// Per-palette data +typedef struct +{ + ID3D12Resource *texture; + D3D12_CPU_DESCRIPTOR_HANDLE resourceView; + D3D12_RESOURCE_STATES resourceState; + SIZE_T SRVIndex; +} D3D12_PaletteData; + // Per-texture data typedef struct { @@ -119,10 +128,6 @@ typedef struct SIZE_T mainSRVIndex; D3D12_CPU_DESCRIPTOR_HANDLE mainTextureRenderTargetView; DXGI_FORMAT mainTextureFormat; - ID3D12Resource *paletteTexture; - D3D12_CPU_DESCRIPTOR_HANDLE paletteTextureResourceView; - D3D12_RESOURCE_STATES paletteResourceState; - SIZE_T paletteSRVIndex; ID3D12Resource *stagingBuffer; D3D12_RESOURCE_STATES stagingResourceState; const float *YCbCr_matrix; @@ -289,6 +294,8 @@ static const GUID SDL_IID_ID3D12InfoQueue = { 0x0742a90b, 0xc387, 0x483f, { 0xb9 #pragma GCC diagnostic pop #endif +static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Resource *texture, int plane, int x, int y, int w, int h, const void *pixels, int pitch, D3D12_RESOURCE_STATES *resourceState); + static UINT D3D12_Align(UINT location, UINT alignment) { return (location + (alignment - 1)) & ~(alignment - 1); @@ -1537,6 +1544,88 @@ static bool GetTextureProperty(SDL_PropertiesID props, const char *name, ID3D12R return true; } +static bool D3D12_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; + D3D12_PaletteData *palettedata = (D3D12_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); + if (!palettedata) { + return false; + } + palette->internal = palettedata; + + if (!data->d3dDevice) { + return SDL_SetError("Device lost and couldn't be recovered"); + } + + D3D12_RESOURCE_DESC textureDesc; + SDL_zero(textureDesc); + textureDesc.Width = 256; + textureDesc.Height = 1; + textureDesc.MipLevels = 1; + textureDesc.DepthOrArraySize = 1; + textureDesc.Format = SDLPixelFormatToDXGITextureFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace); + textureDesc.SampleDesc.Count = 1; + textureDesc.SampleDesc.Quality = 0; + textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE; + + D3D12_HEAP_PROPERTIES heapProps; + SDL_zero(heapProps); + heapProps.Type = D3D12_HEAP_TYPE_DEFAULT; + heapProps.CreationNodeMask = 1; + heapProps.VisibleNodeMask = 1; + + HRESULT result = ID3D12Device1_CreateCommittedResource(data->d3dDevice, + &heapProps, + D3D12_HEAP_FLAG_NONE, + &textureDesc, + D3D12_RESOURCE_STATE_COPY_DEST, + NULL, + D3D_GUID(SDL_IID_ID3D12Resource), + (void **)&palettedata->texture); + if (FAILED(result)) { + return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [texture]"), result); + } + palettedata->resourceState = D3D12_RESOURCE_STATE_COPY_DEST; + + D3D12_SHADER_RESOURCE_VIEW_DESC resourceViewDesc; + SDL_zero(resourceViewDesc); + resourceViewDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + resourceViewDesc.Format = SDLPixelFormatToDXGIMainResourceViewFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace); + resourceViewDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + resourceViewDesc.Texture2D.MipLevels = textureDesc.MipLevels; + + D3D_CALL_RET(data->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &palettedata->resourceView); + palettedata->SRVIndex = D3D12_GetAvailableSRVIndex(renderer); + palettedata->resourceView.ptr += palettedata->SRVIndex * data->srvDescriptorSize; + + ID3D12Device1_CreateShaderResourceView(data->d3dDevice, + palettedata->texture, + &resourceViewDesc, + palettedata->resourceView); + + return true; +} + +static bool D3D12_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + D3D12_RenderData *data = (D3D12_RenderData *)renderer->internal; + D3D12_PaletteData *palettedata = (D3D12_PaletteData *)palette->internal; + + return D3D12_UpdateTextureInternal(data, palettedata->texture, 0, 0, 0, ncolors, 1, colors, ncolors * sizeof(*colors), &palettedata->resourceState); +} + +static void D3D12_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + D3D12_PaletteData *palettedata = (D3D12_PaletteData *)palette->internal; + + if (palettedata) { + D3D_SAFE_RELEASE(palettedata->texture); + D3D12_FreeSRVIndex(renderer, palettedata->SRVIndex); + SDL_free(palettedata); + } +} + static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; @@ -1612,29 +1701,6 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD textureData->mainResourceState = D3D12_RESOURCE_STATE_COPY_DEST; SDL_SetPointerProperty(SDL_GetTextureProperties(texture), SDL_PROP_TEXTURE_D3D12_TEXTURE_POINTER, textureData->mainTexture); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - textureDesc.Width = 256; - textureDesc.Height = 1; - if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { - textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - } else { - textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - } - - result = ID3D12Device1_CreateCommittedResource(rendererData->d3dDevice, - &heapProps, - D3D12_HEAP_FLAG_NONE, - &textureDesc, - D3D12_RESOURCE_STATE_COPY_DEST, - NULL, - D3D_GUID(SDL_IID_ID3D12Resource), - (void **)&textureData->paletteTexture); - if (FAILED(result)) { - return WIN_SetErrorFromHRESULT(SDL_COMPOSE_ERROR("ID3D12Device::CreateCommittedResource [texture]"), result); - } - textureData->paletteResourceState = D3D12_RESOURCE_STATE_COPY_DEST; - } - #ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV) { @@ -1723,19 +1789,6 @@ static bool D3D12_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD &resourceViewDesc, textureData->mainTextureResourceView); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - resourceViewDesc.Format = textureDesc.Format; - - D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->paletteTextureResourceView); - textureData->paletteSRVIndex = D3D12_GetAvailableSRVIndex(renderer); - textureData->paletteTextureResourceView.ptr += textureData->paletteSRVIndex * rendererData->srvDescriptorSize; - - ID3D12Device1_CreateShaderResourceView(rendererData->d3dDevice, - textureData->paletteTexture, - &resourceViewDesc, - textureData->paletteTextureResourceView); - } - #ifdef SDL_HAVE_YUV if (textureData->yuv) { D3D_CALL_RET(rendererData->srvDescriptorHeap, GetCPUDescriptorHandleForHeapStart, &textureData->mainTextureResourceViewU); @@ -1811,10 +1864,6 @@ static void D3D12_DestroyTexture(SDL_Renderer *renderer, D3D_SAFE_RELEASE(textureData->mainTexture); D3D_SAFE_RELEASE(textureData->stagingBuffer); D3D12_FreeSRVIndex(renderer, textureData->mainSRVIndex); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - D3D_SAFE_RELEASE(textureData->paletteTexture); - D3D12_FreeSRVIndex(renderer, textureData->paletteSRVIndex); - } #ifdef SDL_HAVE_YUV D3D_SAFE_RELEASE(textureData->mainTextureU); D3D_SAFE_RELEASE(textureData->mainTextureV); @@ -1969,19 +2018,6 @@ static bool D3D12_UpdateTextureInternal(D3D12_RenderData *rendererData, ID3D12Re return true; } -static bool D3D12_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - D3D12_RenderData *rendererData = (D3D12_RenderData *)renderer->internal; - D3D12_TextureData *textureData = (D3D12_TextureData *)texture->internal; - SDL_Palette *palette = texture->palette; - - if (!textureData) { - return SDL_SetError("Texture is not currently available"); - } - - return D3D12_UpdateTextureInternal(rendererData, textureData->paletteTexture, 0, 0, 0, palette->ncolors, 1, palette->colors, palette->ncolors * sizeof(*palette->colors), &textureData->paletteResourceState); -} - static bool D3D12_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *srcPixels, int srcPitch) @@ -2902,10 +2938,12 @@ static bool D3D12_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand * } shaderSamplers[numShaderSamplers++] = *textureSampler; - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - D3D12_TransitionResource(rendererData, textureData->paletteTexture, textureData->paletteResourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); - textureData->paletteResourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; - shaderResources[numShaderResources++] = textureData->paletteTextureResourceView; + if (texture->palette) { + D3D12_PaletteData *palette = (D3D12_PaletteData *)texture->palette->internal; + + D3D12_TransitionResource(rendererData, palette->texture, palette->resourceState, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + palette->resourceState = D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE; + shaderResources[numShaderResources++] = palette->resourceView; textureSampler = D3D12_GetSamplerState(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); if (!textureSampler) { @@ -3366,8 +3404,10 @@ bool D3D12_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Proper renderer->WindowEvent = D3D12_WindowEvent; renderer->SupportsBlendMode = D3D12_SupportsBlendMode; + renderer->CreatePalette = D3D12_CreatePalette; + renderer->UpdatePalette = D3D12_UpdatePalette; + renderer->DestroyPalette = D3D12_DestroyPalette; renderer->CreateTexture = D3D12_CreateTexture; - renderer->UpdateTexturePalette = D3D12_UpdateTexturePalette; renderer->UpdateTexture = D3D12_UpdateTexture; #ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = D3D12_UpdateTextureYUV; diff --git a/src/render/gpu/SDL_render_gpu.c b/src/render/gpu/SDL_render_gpu.c index 73ac14fbe1..116ef258aa 100644 --- a/src/render/gpu/SDL_render_gpu.c +++ b/src/render/gpu/SDL_render_gpu.c @@ -86,10 +86,14 @@ typedef struct GPU_RenderData SDL_GPUSampler *samplers[RENDER_SAMPLER_COUNT]; } GPU_RenderData; +typedef struct GPU_PaletteData +{ + SDL_GPUTexture *texture; +} GPU_PaletteData; + typedef struct GPU_TextureData { SDL_GPUTexture *texture; - SDL_GPUTexture *palette; SDL_GPUTextureFormat format; GPU_FragmentShaderID shader; void *pixels; @@ -118,6 +122,88 @@ static bool GPU_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blendMod return true; } +static bool GPU_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + GPU_RenderData *data = (GPU_RenderData *)renderer->internal; + GPU_PaletteData *palettedata = (GPU_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); + if (!palettedata) { + return false; + } + palette->internal = palettedata; + + SDL_GPUTextureCreateInfo tci; + SDL_zero(tci); + tci.format = SDL_GetGPUTextureFormatFromPixelFormat(SDL_PIXELFORMAT_RGBA32); + tci.layer_count_or_depth = 1; + tci.num_levels = 1; + tci.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; + tci.width = 256; + tci.height = 1; + tci.sample_count = SDL_GPU_SAMPLECOUNT_1; + + palettedata->texture = SDL_CreateGPUTexture(data->device, &tci); + if (!palettedata->texture) { + return false; + } + return true; +} + +static bool GPU_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + GPU_RenderData *data = (GPU_RenderData *)renderer->internal; + GPU_PaletteData *palettedata = (GPU_PaletteData *)palette->internal; + const Uint32 data_size = ncolors * sizeof(*colors); + + SDL_GPUTransferBufferCreateInfo tbci; + SDL_zero(tbci); + tbci.size = data_size; + tbci.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; + + SDL_GPUTransferBuffer *tbuf = SDL_CreateGPUTransferBuffer(data->device, &tbci); + if (tbuf == NULL) { + return false; + } + + Uint8 *output = SDL_MapGPUTransferBuffer(data->device, tbuf, false); + SDL_memcpy(output, colors, data_size); + SDL_UnmapGPUTransferBuffer(data->device, tbuf); + + SDL_GPUCommandBuffer *cbuf = data->state.command_buffer; + SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf); + + SDL_GPUTextureTransferInfo tex_src; + SDL_zero(tex_src); + tex_src.transfer_buffer = tbuf; + tex_src.rows_per_layer = 1; + tex_src.pixels_per_row = ncolors; + + SDL_GPUTextureRegion tex_dst; + SDL_zero(tex_dst); + tex_dst.texture = palettedata->texture; + tex_dst.x = 0; + tex_dst.y = 0; + tex_dst.w = ncolors; + tex_dst.h = 1; + tex_dst.d = 1; + + SDL_UploadToGPUTexture(cpass, &tex_src, &tex_dst, false); + SDL_EndGPUCopyPass(cpass); + SDL_ReleaseGPUTransferBuffer(data->device, tbuf); + + return true; +} + +static void GPU_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + GPU_RenderData *data = (GPU_RenderData *)renderer->internal; + GPU_PaletteData *palettedata = (GPU_PaletteData *)palette->internal; + + if (palettedata) { + SDL_ReleaseGPUTexture(data->device, palettedata->texture); + SDL_free(palettedata); + } +} + static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal; @@ -184,16 +270,6 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ return false; } - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - tci.format = SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM; - tci.width = 256; - tci.height = 1; - data->palette = SDL_CreateGPUTexture(renderdata->device, &tci); - if (!data->palette) { - return false; - } - } - SDL_PropertiesID props = SDL_GetTextureProperties(texture); SDL_SetPointerProperty(props, SDL_PROP_TEXTURE_GPU_TEXTURE_POINTER, data->texture); @@ -206,52 +282,6 @@ static bool GPU_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_ return true; } -static bool GPU_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - GPU_RenderData *renderdata = (GPU_RenderData *)renderer->internal; - GPU_TextureData *data = (GPU_TextureData *)texture->internal; - SDL_Palette *palette = texture->palette; - const Uint32 data_size = palette->ncolors * sizeof(*palette->colors); - - SDL_GPUTransferBufferCreateInfo tbci; - SDL_zero(tbci); - tbci.size = data_size; - tbci.usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD; - - SDL_GPUTransferBuffer *tbuf = SDL_CreateGPUTransferBuffer(renderdata->device, &tbci); - if (tbuf == NULL) { - return false; - } - - Uint8 *output = SDL_MapGPUTransferBuffer(renderdata->device, tbuf, false); - SDL_memcpy(output, palette->colors, data_size); - SDL_UnmapGPUTransferBuffer(renderdata->device, tbuf); - - SDL_GPUCommandBuffer *cbuf = renderdata->state.command_buffer; - SDL_GPUCopyPass *cpass = SDL_BeginGPUCopyPass(cbuf); - - SDL_GPUTextureTransferInfo tex_src; - SDL_zero(tex_src); - tex_src.transfer_buffer = tbuf; - tex_src.rows_per_layer = 1; - tex_src.pixels_per_row = palette->ncolors; - - SDL_GPUTextureRegion tex_dst; - SDL_zero(tex_dst); - tex_dst.texture = data->palette; - tex_dst.x = 0; - tex_dst.y = 0; - tex_dst.w = palette->ncolors; - tex_dst.h = 1; - tex_dst.d = 1; - - SDL_UploadToGPUTexture(cpass, &tex_src, &tex_dst, false); - SDL_EndGPUCopyPass(cpass); - SDL_ReleaseGPUTransferBuffer(renderdata->device, tbuf); - - return true; -} - static bool GPU_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { @@ -663,9 +693,11 @@ static void Draw( sampler_bind.texture = tdata->texture; SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { + if (texture->palette) { + GPU_PaletteData *palette = (GPU_PaletteData *)texture->palette->internal; + sampler_bind.sampler = GetSampler(data, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); - sampler_bind.texture = tdata->palette; + sampler_bind.texture = palette->texture; SDL_BindGPUFragmentSamplers(pass, sampler_slot++, &sampler_bind, 1); } } @@ -1128,7 +1160,6 @@ static void GPU_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) } SDL_ReleaseGPUTexture(renderdata->device, data->texture); - SDL_ReleaseGPUTexture(renderdata->device, data->palette); SDL_free(data->pixels); SDL_free(data); texture->internal = NULL; @@ -1242,8 +1273,10 @@ static bool GPU_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_P } renderer->SupportsBlendMode = GPU_SupportsBlendMode; + renderer->CreatePalette = GPU_CreatePalette; + renderer->UpdatePalette = GPU_UpdatePalette; + renderer->DestroyPalette = GPU_DestroyPalette; renderer->CreateTexture = GPU_CreateTexture; - renderer->UpdateTexturePalette = GPU_UpdateTexturePalette; renderer->UpdateTexture = GPU_UpdateTexture; renderer->LockTexture = GPU_LockTexture; renderer->UnlockTexture = GPU_UnlockTexture; diff --git a/src/render/metal/SDL_render_metal.m b/src/render/metal/SDL_render_metal.m index 4f828d9fe7..b917e5ef64 100644 --- a/src/render/metal/SDL_render_metal.m +++ b/src/render/metal/SDL_render_metal.m @@ -144,6 +144,14 @@ typedef struct METAL_ShaderPipelines @implementation SDL3METAL_RenderData @end +@interface SDL3METAL_PaletteData : NSObject +@property(nonatomic, retain) id mtltexture; +@property(nonatomic, assign) BOOL hasdata; +@end + +@implementation SDL3METAL_PaletteData +@end + @interface SDL3METAL_TextureData : NSObject @property(nonatomic, retain) id mtltexture; @property(nonatomic, retain) id mtlpalette; @@ -162,6 +170,10 @@ typedef struct METAL_ShaderPipelines @implementation SDL3METAL_TextureData @end +static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, BOOL hasdata, + id texture, SDL_Rect rect, int slice, + const void *pixels, int pitch); + static const MTLBlendOperation invalidBlendOperation = (MTLBlendOperation)0xFFFFFFFF; static const MTLBlendFactor invalidBlendFactor = (MTLBlendFactor)0xFFFFFFFF; @@ -611,13 +623,68 @@ size_t GetYCbCRtoRGBConversionMatrix(SDL_Colorspace colorspace, int w, int h, in return 0; } +static bool METAL_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + @autoreleasepool { + SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal; + MTLPixelFormat pixfmt; + MTLTextureDescriptor *mtltexdesc; + id mtltexture = nil; + SDL3METAL_PaletteData *palettedata; + + if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { + pixfmt = MTLPixelFormatRGBA8Unorm_sRGB; + } else { + pixfmt = MTLPixelFormatRGBA8Unorm; + } + mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt + width:256 + height:1 + mipmapped:NO]; + mtltexdesc.usage = MTLTextureUsageShaderRead; + + mtltexture = [data.mtldevice newTextureWithDescriptor:mtltexdesc]; + if (mtltexture == nil) { + return SDL_SetError("Palette allocation failed"); + } + + palettedata = [[SDL3METAL_PaletteData alloc] init]; + palettedata.mtltexture = mtltexture; + palette->internal = (void *)CFBridgingRetain(palettedata); + + return true; + } +} + +static bool METAL_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + @autoreleasepool { + SDL3METAL_PaletteData *palettedata = (__bridge SDL3METAL_PaletteData *)palette->internal; + SDL_Rect rect = { 0, 0, ncolors, 1 }; + + if (!METAL_UpdateTextureInternal(renderer, palettedata.hasdata, palettedata.mtltexture, rect, 0, colors, ncolors * sizeof(*colors))) { + return false; + } + palettedata.hasdata = true; + return true; + } +} + +static void METAL_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + @autoreleasepool { + CFBridgingRelease(palette->internal); + palette->internal = NULL; + } +} + static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { @autoreleasepool { SDL3METAL_RenderData *data = (__bridge SDL3METAL_RenderData *)renderer->internal; MTLPixelFormat pixfmt; MTLTextureDescriptor *mtltexdesc; - id mtltexture = nil, mtltextureUv = nil, mtlpalette = nil; + id mtltexture = nil, mtltextureUv = nil; SDL3METAL_TextureData *texturedata; CVPixelBufferRef pixelbuffer = nil; IOSurfaceRef surface = nil; @@ -688,25 +755,6 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD return SDL_SetError("Texture allocation failed"); } - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { - pixfmt = MTLPixelFormatBGRA8Unorm_sRGB; - } else { - pixfmt = MTLPixelFormatBGRA8Unorm; - } - mtltexdesc = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:pixfmt - width:256 - height:1 - mipmapped:NO]; - - mtltexdesc.usage = MTLTextureUsageShaderRead; - - mtlpalette = [data.mtldevice newTextureWithDescriptor:mtltexdesc]; - if (mtlpalette == nil) { - return SDL_SetError("Palette allocation failed"); - } - } - mtltextureUv = nil; #ifdef SDL_HAVE_YUV BOOL yuv = (texture->format == SDL_PIXELFORMAT_IYUV || texture->format == SDL_PIXELFORMAT_YV12); @@ -753,7 +801,6 @@ static bool METAL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD texturedata.fragmentFunction = SDL_METAL_FRAGMENT_COPY; } texturedata.mtltexture = mtltexture; - texturedata.mtlpalette = mtlpalette; texturedata.mtltextureUv = mtltextureUv; #ifdef SDL_HAVE_YUV texturedata.yuv = yuv; @@ -788,7 +835,7 @@ static MTLStorageMode METAL_GetStorageMode(id resource) return resource.storageMode; } -static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, SDL3METAL_TextureData *texturedata, +static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, BOOL hasdata, id texture, SDL_Rect rect, int slice, const void *pixels, int pitch) { @@ -801,7 +848,7 @@ static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, SDL3METAL_Textur /* If the texture is managed or shared and this is the first upload, we can * use replaceRegion to upload to it directly. Otherwise we upload the data * to a staging texture and copy that over. */ - if (!texturedata.hasdata && METAL_GetStorageMode(texture) != MTLStorageModePrivate) { + if (!hasdata && METAL_GetStorageMode(texture) != MTLStorageModePrivate) { METAL_UploadTextureData(texture, rect, slice, pixels, pitch); return true; } @@ -856,24 +903,13 @@ static bool METAL_UpdateTextureInternal(SDL_Renderer *renderer, SDL3METAL_Textur return true; } -static bool METAL_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - @autoreleasepool { - SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal; - SDL_Palette *palette = texture->palette; - SDL_Rect rect = { 0, 0, palette->ncolors, 1 }; - - return METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtlpalette, rect, 0, palette->colors, palette->ncolors * sizeof(*palette->colors)); - } -} - static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { @autoreleasepool { SDL3METAL_TextureData *texturedata = (__bridge SDL3METAL_TextureData *)texture->internal; - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, pixels, pitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltexture, *rect, 0, pixels, pitch)) { return false; } #ifdef SDL_HAVE_YUV @@ -885,13 +921,13 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, // Skip to the correct offset into the next texture pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, Uslice, pixels, UVpitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Uslice, pixels, UVpitch)) { return false; } // Skip to the correct offset into the next texture pixels = (const void *)((const Uint8 *)pixels + UVrect.h * UVpitch); - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, Vslice, pixels, UVpitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Vslice, pixels, UVpitch)) { return false; } } @@ -902,7 +938,7 @@ static bool METAL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, // Skip to the correct offset into the next texture pixels = (const void *)((const Uint8 *)pixels + rect->h * pitch); - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, 0, pixels, UVpitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, 0, pixels, UVpitch)) { return false; } } @@ -931,13 +967,13 @@ static bool METAL_UpdateTextureYUV(SDL_Renderer *renderer, SDL_Texture *texture, return true; } - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) { return false; } - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, Uslice, Uplane, Upitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Uslice, Uplane, Upitch)) { return false; } - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, Vslice, Vplane, Vpitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, Vslice, Vplane, Vpitch)) { return false; } @@ -961,11 +997,11 @@ static bool METAL_UpdateTextureNV(SDL_Renderer *renderer, SDL_Texture *texture, return true; } - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltexture, *rect, 0, Yplane, Ypitch)) { return false; } - if (!METAL_UpdateTextureInternal(renderer, texturedata, texturedata.mtltextureUv, UVrect, 0, UVplane, UVpitch)) { + if (!METAL_UpdateTextureInternal(renderer, texturedata.hasdata, texturedata.mtltextureUv, UVrect, 0, UVplane, UVpitch)) { return false; } @@ -1568,8 +1604,9 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c if (texture != statecache->texture) { [data.mtlcmdencoder setFragmentTexture:texturedata.mtltexture atIndex:0]; - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - [data.mtlcmdencoder setFragmentTexture:texturedata.mtlpalette atIndex:1]; + if (texture->palette) { + SDL3METAL_PaletteData *palette = (__bridge SDL3METAL_PaletteData *)texture->palette->internal; + [data.mtlcmdencoder setFragmentTexture:palette.mtltexture atIndex:1]; } #ifdef SDL_HAVE_YUV if (texturedata.yuv || texturedata.nv12) { @@ -1593,7 +1630,7 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, c statecache->texture_address_mode_u = cmd->data.draw.texture_address_mode_u; statecache->texture_address_mode_v = cmd->data.draw.texture_address_mode_v; } - if (texture->format == SDL_PIXELFORMAT_INDEX8) { + if (texture->palette) { if (!statecache->texture_palette) { id mtlsampler = GetSampler(data, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); if (mtlsampler == nil) { @@ -2201,8 +2238,10 @@ static bool METAL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL renderer->WindowEvent = METAL_WindowEvent; renderer->GetOutputSize = METAL_GetOutputSize; renderer->SupportsBlendMode = METAL_SupportsBlendMode; + renderer->CreatePalette = METAL_CreatePalette; + renderer->UpdatePalette = METAL_UpdatePalette; + renderer->DestroyPalette = METAL_DestroyPalette; renderer->CreateTexture = METAL_CreateTexture; - renderer->UpdateTexturePalette = METAL_UpdateTexturePalette; renderer->UpdateTexture = METAL_UpdateTexture; #ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = METAL_UpdateTextureYUV; diff --git a/src/render/opengl/SDL_render_gl.c b/src/render/opengl/SDL_render_gl.c index cf288ab806..dc14b2e969 100644 --- a/src/render/opengl/SDL_render_gl.c +++ b/src/render/opengl/SDL_render_gl.c @@ -125,6 +125,11 @@ typedef struct GL_DrawStateCache drawstate; } GL_RenderData; +typedef struct +{ + GLuint texture; +} GL_PaletteData; + typedef struct { GLuint texture; @@ -133,7 +138,6 @@ typedef struct GLfloat texh; GLenum format; GLenum formattype; - GLuint palette; GL_Shader shader; float texel_size[4]; const float *shader_params; @@ -481,6 +485,58 @@ static void SetTextureAddressMode(GL_RenderData *data, GLenum textype, SDL_Textu data->glTexParameteri(textype, GL_TEXTURE_WRAP_T, TranslateAddressMode(addressModeV)); } +static bool GL_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + GL_RenderData *data = (GL_RenderData *)renderer->internal; + GL_PaletteData *palettedata = (GL_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); + if (!palettedata) { + return false; + } + palette->internal = palettedata; + + data->drawstate.texture = NULL; // we trash this state. + + const GLenum textype = data->textype; + data->glGenTextures(1, &palettedata->texture); + data->glBindTexture(textype, palettedata->texture); + data->glTexImage2D(textype, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (!GL_CheckError("glTexImage2D()", renderer)) { + return false; + } + SetTextureScaleMode(data, textype, SDL_SCALEMODE_NEAREST); + SetTextureAddressMode(data, textype, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); + return true; +} + +static bool GL_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + GL_RenderData *data = (GL_RenderData *)renderer->internal; + GL_PaletteData *palettedata = (GL_PaletteData *)palette->internal; + + GL_ActivateRenderer(renderer); + + data->drawstate.texture = NULL; // we trash this state. + + const GLenum textype = data->textype; + data->glBindTexture(textype, palettedata->texture); + data->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + data->glPixelStorei(GL_UNPACK_ROW_LENGTH, ncolors); + data->glTexSubImage2D(textype, 0, 0, 0, ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, colors); + + return GL_CheckError("glTexSubImage2D()", renderer); +} + +static void GL_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + GL_RenderData *data = (GL_RenderData *)renderer->internal; + GL_PaletteData *palettedata = (GL_PaletteData *)palette->internal; + + if (palettedata) { + data->glDeleteTextures(1, &palettedata->texture); + SDL_free(palettedata); + } +} + static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { GL_RenderData *renderdata = (GL_RenderData *)renderer->internal; @@ -621,14 +677,6 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P SetTextureScaleMode(renderdata, textype, data->texture_scale_mode); SetTextureAddressMode(renderdata, textype, data->texture_address_mode_u, data->texture_address_mode_v); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - renderdata->glGenTextures(1, &data->palette); - renderdata->glBindTexture(textype, data->palette); - renderdata->glTexImage2D(textype, 0, GL_RGBA8, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - SetTextureScaleMode(renderdata, textype, SDL_SCALEMODE_NEAREST); - SetTextureAddressMode(renderdata, textype, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); - } - #ifdef SDL_HAVE_YUV if (texture->format == SDL_PIXELFORMAT_YV12 || texture->format == SDL_PIXELFORMAT_IYUV) { @@ -723,25 +771,6 @@ static bool GL_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P return GL_CheckError("", renderer); } -static bool GL_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - GL_RenderData *renderdata = (GL_RenderData *)renderer->internal; - const GLenum textype = renderdata->textype; - GL_TextureData *data = (GL_TextureData *)texture->internal; - SDL_Palette *palette = texture->palette; - - GL_ActivateRenderer(renderer); - - renderdata->drawstate.texture = NULL; // we trash this state. - - renderdata->glBindTexture(textype, data->palette); - renderdata->glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - renderdata->glPixelStorei(GL_UNPACK_ROW_LENGTH, palette->ncolors); - renderdata->glTexSubImage2D(textype, 0, 0, 0, palette->ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, palette->colors); - - return GL_CheckError("glTexSubImage2D()", renderer); -} - static bool GL_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { @@ -1205,8 +1234,9 @@ static bool SetCopyState(GL_RenderData *data, const SDL_RenderCommand *cmd) } #endif if (texture->palette) { + GL_PaletteData *palette = (GL_PaletteData *)texture->palette->internal; data->glActiveTextureARB(GL_TEXTURE1_ARB); - data->glBindTexture(textype, texturedata->palette); + data->glBindTexture(textype, palette->texture); } if (data->GL_ARB_multitexture_supported) { data->glActiveTextureARB(GL_TEXTURE0_ARB); @@ -1631,9 +1661,6 @@ static void GL_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (data->texture && !data->texture_external) { renderdata->glDeleteTextures(1, &data->texture); } - if (data->palette) { - renderdata->glDeleteTextures(1, &data->palette); - } #ifdef SDL_HAVE_YUV if (data->yuv) { if (!data->utexture_external) { @@ -1753,8 +1780,10 @@ static bool GL_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_Pr renderer->WindowEvent = GL_WindowEvent; renderer->SupportsBlendMode = GL_SupportsBlendMode; + renderer->CreatePalette = GL_CreatePalette; + renderer->UpdatePalette = GL_UpdatePalette; + renderer->DestroyPalette = GL_DestroyPalette; renderer->CreateTexture = GL_CreateTexture; - renderer->UpdateTexturePalette = GL_UpdateTexturePalette; renderer->UpdateTexture = GL_UpdateTexture; #ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = GL_UpdateTextureYUV; diff --git a/src/render/opengles2/SDL_render_gles2.c b/src/render/opengles2/SDL_render_gles2.c index a947aeb2b5..d7845a347a 100644 --- a/src/render/opengles2/SDL_render_gles2.c +++ b/src/render/opengles2/SDL_render_gles2.c @@ -58,14 +58,18 @@ struct GLES2_FBOList GLES2_FBOList *next; }; -typedef struct GLES2_TextureData +typedef struct +{ + GLuint texture; +} GLES2_PaletteData; + +typedef struct { GLuint texture; bool texture_external; GLenum texture_type; GLenum pixel_format; GLenum pixel_type; - GLuint palette; void *pixel_data; int pitch; #ifdef SDL_HAVE_YUV @@ -1277,8 +1281,9 @@ static bool SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand *cmd, v } #endif if (texture->palette) { + GLES2_PaletteData *palette = (GLES2_PaletteData *)texture->palette->internal; data->glActiveTexture(GL_TEXTURE1); - data->glBindTexture(tdata->texture_type, tdata->palette); + data->glBindTexture(tdata->texture_type, palette->texture); data->glActiveTexture(GL_TEXTURE0); } @@ -1633,6 +1638,58 @@ static void GLES2_DestroyRenderer(SDL_Renderer *renderer) } } +static bool GLES2_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal; + GLES2_PaletteData *palettedata = (GLES2_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); + if (!palettedata) { + return false; + } + palette->internal = palettedata; + + data->drawstate.texture = NULL; // we trash this state. + + data->glGenTextures(1, &palettedata->texture); + if (!GL_CheckError("glGenTexures()", renderer)) { + return false; + } + data->glActiveTexture(GL_TEXTURE1); + data->glBindTexture(GL_TEXTURE_2D, palettedata->texture); + data->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + if (!GL_CheckError("glTexImage2D()", renderer)) { + return false; + } + SetTextureScaleMode(data, GL_TEXTURE_2D, SDL_SCALEMODE_NEAREST); + SetTextureAddressMode(data, GL_TEXTURE_2D, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); + return true; +} + +static bool GLES2_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal; + GLES2_PaletteData *palettedata = (GLES2_PaletteData *)palette->internal; + + GLES2_ActivateRenderer(renderer); + + data->drawstate.texture = NULL; // we trash this state. + + data->glBindTexture(GL_TEXTURE_2D, palettedata->texture); + data->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, colors); + + return GL_CheckError("glTexSubImage2D()", renderer); +} + +static void GLES2_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal; + GLES2_PaletteData *palettedata = (GLES2_PaletteData *)palette->internal; + + if (palettedata) { + data->glDeleteTextures(1, &palettedata->texture); + SDL_free(palettedata); + } +} + static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { GLES2_RenderData *renderdata = (GLES2_RenderData *)renderer->internal; @@ -1731,25 +1788,6 @@ static bool GLES2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SD data->texel_size[0] = 1.0f / data->texel_size[2]; data->texel_size[1] = 1.0f / data->texel_size[3]; - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - renderdata->glGenTextures(1, &data->palette); - if (!GL_CheckError("glGenTexures()", renderer)) { - SDL_free(data->pixel_data); - SDL_free(data); - return false; - } - renderdata->glActiveTexture(GL_TEXTURE1); - renderdata->glBindTexture(data->texture_type, data->palette); - renderdata->glTexImage2D(data->texture_type, 0, GL_RGBA, 256, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - if (!GL_CheckError("glTexImage2D()", renderer)) { - SDL_free(data->pixel_data); - SDL_free(data); - return false; - } - SetTextureScaleMode(renderdata, data->texture_type, SDL_SCALEMODE_NEAREST); - SetTextureAddressMode(renderdata, data->texture_type, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); - } - #ifdef SDL_HAVE_YUV if (data->yuv) { data->texture_v = (GLuint)SDL_GetNumberProperty(create_props, SDL_PROP_TEXTURE_CREATE_OPENGLES2_TEXTURE_V_NUMBER, 0); @@ -1904,22 +1942,6 @@ static bool GLES2_TexSubImage2D(GLES2_RenderData *data, GLenum target, GLint xof return true; } -static bool GLES2_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - GLES2_RenderData *data = (GLES2_RenderData *)renderer->internal; - GLES2_TextureData *tdata = (GLES2_TextureData *)texture->internal; - SDL_Palette *palette = texture->palette; - - GLES2_ActivateRenderer(renderer); - - data->drawstate.texture = NULL; /* we trash this state. */ - - data->glBindTexture(tdata->texture_type, tdata->palette); - data->glTexSubImage2D(tdata->texture_type, 0, 0, 0, palette->ncolors, 1, GL_RGBA, GL_UNSIGNED_BYTE, palette->colors); - - return GL_CheckError("glTexSubImage2D()", renderer); -} - static bool GLES2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { @@ -2160,9 +2182,6 @@ static void GLES2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture) if (tdata->texture && !tdata->texture_external) { data->glDeleteTextures(1, &tdata->texture); } - if (tdata->palette) { - data->glDeleteTextures(1, &tdata->palette); - } #ifdef SDL_HAVE_YUV if (tdata->texture_v && !tdata->texture_v_external) { data->glDeleteTextures(1, &tdata->texture_v); @@ -2290,8 +2309,10 @@ static bool GLES2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL // Populate the function pointers for the module renderer->WindowEvent = GLES2_WindowEvent; renderer->SupportsBlendMode = GLES2_SupportsBlendMode; + renderer->CreatePalette = GLES2_CreatePalette; + renderer->UpdatePalette = GLES2_UpdatePalette; + renderer->DestroyPalette = GLES2_DestroyPalette; renderer->CreateTexture = GLES2_CreateTexture; - renderer->UpdateTexturePalette = GLES2_UpdateTexturePalette; renderer->UpdateTexture = GLES2_UpdateTexture; #ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = GLES2_UpdateTextureYUV; diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c index b7c765413b..a4f628ffc9 100644 --- a/src/render/software/SDL_render_sw.c +++ b/src/render/software/SDL_render_sw.c @@ -99,6 +99,38 @@ static bool SW_GetOutputSize(SDL_Renderer *renderer, int *w, int *h) return SDL_SetError("Software renderer doesn't have an output surface"); } +static bool SW_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + SDL_Palette *surface_palette = SDL_CreatePalette(256); + if (!surface_palette) { + return false; + } + palette->internal = surface_palette; + return true; +} + +static bool SW_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + SDL_Palette *surface_palette = (SDL_Palette *)palette->internal; + return SDL_SetPaletteColors(surface_palette, colors, 0, ncolors); +} + +static void SW_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + SDL_Palette *surface_palette = (SDL_Palette *)palette->internal; + SDL_DestroyPalette(surface_palette); +} + +static bool SW_ChangeTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) +{ + SDL_Surface *surface = (SDL_Surface *)texture->internal; + SDL_Palette *surface_palette = NULL; + if (texture->palette) { + surface_palette = (SDL_Palette *)texture->palette->internal; + } + return SDL_SetSurfacePalette(surface, surface_palette); +} + static bool SW_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { SDL_Surface *surface = SDL_CreateSurface(texture->w, texture->h, texture->format); @@ -132,14 +164,6 @@ static bool SW_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_P return true; } -static bool SW_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - SDL_Surface *surface = (SDL_Surface *)texture->internal; - SDL_Palette *palette = texture->palette; - - return SDL_SetPaletteColors(surface->palette, palette->colors, 0, palette->ncolors); -} - static bool SW_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch) { @@ -1158,8 +1182,11 @@ bool SW_CreateRendererForSurface(SDL_Renderer *renderer, SDL_Surface *surface, S renderer->WindowEvent = SW_WindowEvent; renderer->GetOutputSize = SW_GetOutputSize; + renderer->CreatePalette = SW_CreatePalette; + renderer->UpdatePalette = SW_UpdatePalette; + renderer->DestroyPalette = SW_DestroyPalette; + renderer->ChangeTexturePalette = SW_ChangeTexturePalette; renderer->CreateTexture = SW_CreateTexture; - renderer->UpdateTexturePalette = SW_UpdateTexturePalette; renderer->UpdateTexture = SW_UpdateTexture; renderer->LockTexture = SW_LockTexture; renderer->UnlockTexture = SW_UnlockTexture; diff --git a/src/render/vulkan/SDL_render_vulkan.c b/src/render/vulkan/SDL_render_vulkan.c index 2f68f4792e..5d5dea2de8 100644 --- a/src/render/vulkan/SDL_render_vulkan.c +++ b/src/render/vulkan/SDL_render_vulkan.c @@ -240,11 +240,16 @@ typedef struct VkFormat format; } VULKAN_Image; +// Per-palette data +typedef struct +{ + VULKAN_Image image; +} VULKAN_PaletteData; + // Per-texture data typedef struct { VULKAN_Image mainImage; - VULKAN_Image paletteImage; VkRenderPass mainRenderpasses[VULKAN_RENDERPASS_COUNT]; VkFramebuffer mainFramebuffer; VULKAN_Buffer stagingBuffer; @@ -384,6 +389,8 @@ typedef struct bool issueBatch; } VULKAN_RenderData; +static bool VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImage image, VkFormat format, int plane, int x, int y, int w, int h, const void *pixels, int pitch, VkImageLayout *imageLayout); + static SDL_PixelFormat VULKAN_VkFormatToSDLPixelFormat(VkFormat vkFormat) { switch (vkFormat) { @@ -2533,6 +2540,45 @@ static bool VULKAN_SupportsBlendMode(SDL_Renderer *renderer, SDL_BlendMode blend return true; } +static bool VULKAN_CreatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal; + VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)SDL_calloc(1, sizeof(*palettedata)); + if (!palettedata) { + return false; + } + palette->internal = palettedata; + + VkFormat format = SDLPixelFormatToVkTextureFormat(SDL_PIXELFORMAT_RGBA32, renderer->output_colorspace); + VkImageUsageFlags usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + VkComponentMapping imageViewSwizzle = data->identitySwizzle; + VkResult result = VULKAN_AllocateImage(data, 0, 256, 1, format, usage, imageViewSwizzle, VK_NULL_HANDLE, &palettedata->image); + if (result != VK_SUCCESS) { + SET_ERROR_CODE("VULKAN_AllocateImage()", result); + return false; + } + return true; +} + +static bool VULKAN_UpdatePalette(SDL_Renderer *renderer, SDL_TexturePalette *palette, int ncolors, SDL_Color *colors) +{ + VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal; + VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)palette->internal; + + return VULKAN_UpdateTextureInternal(data, palettedata->image.image, palettedata->image.format, 0, 0, 0, ncolors, 1, colors, ncolors * sizeof(*colors), &palettedata->image.imageLayout); +} + +static void VULKAN_DestroyPalette(SDL_Renderer *renderer, SDL_TexturePalette *palette) +{ + VULKAN_RenderData *data = (VULKAN_RenderData *)renderer->internal; + VULKAN_PaletteData *palettedata = (VULKAN_PaletteData *)palette->internal; + + if (palettedata) { + VULKAN_DestroyImage(data, &palettedata->image); + SDL_free(palettedata); + } +} + static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props) { VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal; @@ -2694,21 +2740,6 @@ static bool VULKAN_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, S return false; } - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - VkFormat paletteFormat; - - if (renderer->output_colorspace == SDL_COLORSPACE_SRGB_LINEAR) { - paletteFormat = VK_FORMAT_R8G8B8A8_SRGB; - } else { - paletteFormat = VK_FORMAT_R8G8B8A8_UNORM; - } - result = VULKAN_AllocateImage(rendererData, 0, 256, 1, paletteFormat, usage, imageViewSwizzle, textureData->samplerYcbcrConversion, &textureData->paletteImage); - if (result != VK_SUCCESS) { - SET_ERROR_CODE("VULKAN_AllocateImage()", result); - return false; - } - } - SDL_PropertiesID props = SDL_GetTextureProperties(texture); SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_VULKAN_TEXTURE_NUMBER, (Sint64)textureData->mainImage.image); @@ -2746,10 +2777,6 @@ static void VULKAN_DestroyTexture(SDL_Renderer *renderer, VULKAN_DestroyImage(rendererData, &textureData->mainImage); - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - VULKAN_DestroyImage(rendererData, &textureData->paletteImage); - } - #ifdef SDL_HAVE_YUV if (textureData->samplerYcbcrConversion != VK_NULL_HANDLE) { vkDestroySamplerYcbcrConversionKHR(rendererData->device, textureData->samplerYcbcrConversion, NULL); @@ -2876,20 +2903,6 @@ static bool VULKAN_UpdateTextureInternal(VULKAN_RenderData *rendererData, VkImag return true; } - -static bool VULKAN_UpdateTexturePalette(SDL_Renderer *renderer, SDL_Texture *texture) -{ - VULKAN_RenderData *rendererData = (VULKAN_RenderData *)renderer->internal; - VULKAN_TextureData *textureData = (VULKAN_TextureData *)texture->internal; - SDL_Palette *palette = texture->palette; - - if (!textureData) { - return SDL_SetError("Texture is not currently available"); - } - - return VULKAN_UpdateTextureInternal(rendererData, textureData->paletteImage.image, textureData->paletteImage.format, 0, 0, 0, palette->ncolors, 1, palette->colors, palette->ncolors * sizeof(*palette->colors), &textureData->paletteImage.imageLayout); -} - static bool VULKAN_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *rect, const void *srcPixels, int srcPitch) @@ -3900,8 +3913,9 @@ static bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand } ++numSamplers; - if (texture->format == SDL_PIXELFORMAT_INDEX8) { - if (textureData->paletteImage.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + if (texture->palette) { + VULKAN_PaletteData *palette = (VULKAN_PaletteData *)texture->palette->internal; + if (palette->image.imageLayout != VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { bool stoppedRenderPass = false; if (rendererData->currentRenderPass != VK_NULL_HANDLE) { vkCmdEndRenderPass(rendererData->currentCommandBuffer); @@ -3915,14 +3929,14 @@ static bool VULKAN_SetCopyState(SDL_Renderer *renderer, const SDL_RenderCommand VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, - textureData->paletteImage.image, - &textureData->paletteImage.imageLayout); + palette->image.image, + &palette->image.imageLayout); if (stoppedRenderPass) { VULKAN_BeginRenderPass(rendererData, VK_ATTACHMENT_LOAD_OP_LOAD, NULL); } } - imageViews[numImageViews++] = textureData->paletteImage.imageView; + imageViews[numImageViews++] = palette->image.imageView; samplers[numSamplers] = VULKAN_GetSampler(rendererData, SDL_SCALEMODE_NEAREST, SDL_TEXTURE_ADDRESS_CLAMP, SDL_TEXTURE_ADDRESS_CLAMP); if (samplers[numSamplers] == VK_NULL_HANDLE) { @@ -4391,8 +4405,10 @@ static bool VULKAN_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SD renderer->WindowEvent = VULKAN_WindowEvent; renderer->SupportsBlendMode = VULKAN_SupportsBlendMode; + renderer->CreatePalette = VULKAN_CreatePalette; + renderer->UpdatePalette = VULKAN_UpdatePalette; + renderer->DestroyPalette = VULKAN_DestroyPalette; renderer->CreateTexture = VULKAN_CreateTexture; - renderer->UpdateTexturePalette = VULKAN_UpdateTexturePalette; renderer->UpdateTexture = VULKAN_UpdateTexture; #ifdef SDL_HAVE_YUV renderer->UpdateTextureYUV = VULKAN_UpdateTextureYUV;