SDL
SDL copied to clipboard
render: Preliminary support for paletted textures
This extends the limited support for paletted textures in the DirectFB render driver to allow support to be added to other platforms, and to allow the palette to be changed after the texture has been created, which is useful in older games.
This is currently implemented in the Direct3D 9, OpenGL and OpenGL ES 2 drivers using shaders, but these currently don't work correctly when bilinear filtering is enabled. Fixing it would require using texture targets, but I'm not sure on the best way to implement this. A software-based fallback is also available, which doesn't have this problem.
testoverlay2 has been modified to make use of this feature, and another test program has been added to test palette rotation effects that may occur without updating the image data. I've also opened https://github.com/sergiou87/open-supaplex/pull/41 to demonstrate this in a real-world application.
I really don't want to get into paletted textures in the 2D API, fwiw; if there isn't a trivial way to upload them to a non-paletted format, we should add that as a generic thing, and let people that need this use a shader directly in the upcoming GPU API.
I really don't want to get into paletted textures in the 2D API, fwiw; if there isn't a trivial way to upload them to a non-paletted format, we should add that as a generic thing, and let people that need this use a shader directly in the upcoming GPU API.
Hi @icculus , SDL 1 supports paletted textures and for some projects like fheroes2 it is important to have such ability. Right now we do a hack to convert from 8-bit images to 32-bit images which leads to 20% performance drop. This is a very significant reduction which affects not only the gameplay but eats more resources and battery on portable devices.
If you think about an easier way to add support for such feature then please explain it.
Also it would be important to know the target version of this change, whether this PR going to be a part of the source code or there are concerns about integrating it. If there are concerns what are they besides the fact of possible complexity of the code?
The main blocker for this PR is the fact that bilinear filtering still doesn't work with backends that implement this using shaders. Once that's done it should be ready, but I'm not sure that I'm capable of doing this myself. If anyone else is interested, then feel free to take over this PR.
If this is ever finished, I would quite like to get this into SDL2 as well as SDL3, but I don't know if new API calls are still being accepted there.
Hi @ccawley2011 , bilinear interpolation cannot be done on indexed images. The only way to do it is to get values in RGB space and then find the nearest value in the palette to the result. If we think that RGB is a 3D space then it's a task of finding the smallest distance between 2 dots: the result value and existing palette value. The complexity of this algorithm is huge and bigger than to O(M*N). It would definitely be very slow.
I've implemented bilinear downscaling for indexed images in the past here. That file has 2 functions, one does 32-bit scaling to arbitrary size, the other one downscales 8-bit indexed images by 50%.
Downscaling of 8-bit indexed images is achieved by passing a Uint8 paletteBlendingTable[256][256]
, created on palette changes, which is very slow (but we do it infrequently in DevilutionX). The table is created here.
You can handle the filtering in the shader method with some number of weighted lookups on the texture then blend. (I'm not sure this works for scaling down?).
#define SHADER_FRAGMENT_CUBIC \
"varying vec4 v_color;\n" \
"varying vec2 v_texCoord;\n" \
"uniform sampler2D tex0;\n" \
"\n" \
"const float pal[768] = float[768](\n" \
PAL_0 \
");\n" \
"float cubic(float x) {\n" \
" x = abs(x);\n" \
" float x2 = x * x;\n" \
" float x3 = x2 * x;\n" \
" if (x <= 1.0) {\n" \
" return 1.0 - 2.0 * x2 + x3;\n" \
" } else if (x < 2.0) {\n" \
" return 4.0 - 8.0 * x + 5.0 * x2 - x3;\n" \
" } else {\n" \
" return 0.0;\n" \
" }\n" \
"}\n" \
"vec4 textureBicubic(sampler2D sampler, vec2 uv) {\n" \
" vec2 texSize = textureSize(sampler, 0);\n" \
" vec2 invTexSize = 1.0 / texSize;\n" \
" vec2 f = fract(uv * texSize - 0.5);\n" \
" vec2 p = floor(uv * texSize - 0.5);\n" \
" vec4 sum = vec4(0.0);\n" \
" for (int i = -1; i <= 2; ++i) {\n" \
" for (int j = -1; j <= 2; ++j) {\n" \
" vec2 index = p + vec2(i, j);\n" \
" float weight = cubic(f.x - float(i)) * cubic(f.y - float(j));\n" \
" float idx = texture2D(sampler, (index + 0.5) * invTexSize).r * 255.0;\n" \
" int colorIdx = int(idx) * 3;\n" \
" float alpha = colorIdx <= 2.0 ? 0.0 : 1.0;\n" \
" vec4 color = vec4(pal[colorIdx], pal[colorIdx+1], pal[colorIdx+2], alpha);\n" \
" sum += color * weight;\n" \
" }\n" \
" }\n" \
" return sum;\n" \
"}\n" \
"void main() {\n" \
" vec4 color = textureBicubic(tex0, v_texCoord);\n" \
" gl_FragColor = color * v_color;\n" \
"}\n"
For sdl2 I think this is maybe too much to add since it adds a lot to each rendering backend implementation, new indexed texture formats and specific shaders. Any use of this would likely need some shader customization anyway ontop - which SDL2 is not built for.
I would rather see a generic data/byte type of image that can bind a generic/custom/utility shader to , something that could also be useful for tonemapping, luts etc. (thinking sdl3)