Search code examples
2ddirect3dpalette

Fast paletted screen blit with Direct3D 9


A game uses software rendering to draw a full-screen paletted (8-bit) image in memory.

What's the fastest way to put that image on the screen, using Direct3D?

Currently I convert the paletted image to RGB in software, then put it on a D3DUSAGE_DYNAMIC texture (which is locked with D3DLOCK_DISCARD).

Is there a faster way? E.g. using shaders to perform palettization?

Related questions:


Solution

  • Create a D3DFMT_L8 texture containing the paletted image, and an 256x1 D3DFMT_X8R8G8B8 image containing the palette.

    HLSL shader code:

    uniform sampler2D image;
    uniform sampler1D palette;
    
    float4 main(in float2 coord:TEXCOORD) : COLOR
    {
        return tex1D(palette, tex2D(image, coord).r * (255./256) + (0.5/256));
    }
    

    Note that the luminance (palette index) is adjusted with a multiply-add operation. This is necessary, as palette index 255 is considered as white (maximum luminance), which becomes 1.0f when represented as a float. Reading the palette texture at that coordinate causes it to wrap around (as only the fractionary part is used) and read the first palette entry instead.

    Compile it with:

    fxc /Tps_2_0 PaletteShader.hlsl /FhPaletteShader.h
    

    Use it like this:

    // ... create and populate texture and paletteTexture objects ...
    d3dDevice->CreatePixelShader((DWORD*)g_ps20_main, &shader)
    // ...
    d3dDevice->SetTexture(1, paletteTexture);
    d3dDevice->SetPixelShader(shader);
    // ... draw texture to screen as textured quad as usual ...