Are INDEX8 textures not being rendered properly to target texture?

I’m not sure what cause this problem, I’m sorry to pass more code then probably needed.

So I thought what I try to do is fairly simple, what I want is fixed resolution renderer with aspect ratio scaling.

1 Switch render target to target texture and draw objects.
2. Switch render target to window and draw target texture.

And my renderer pipeline has been working just fine before I changed the rendering target. I mean the visual effect is no different when you remove the second step completely,

  1. objects are being drawn to the window,
  2. target is empty.
const SDL_AppResult
PT_Render(PT_Renderer *restrict self, PT_Scene scene)
{
  SDL_Renderer *r = self->renderer;
  PT_Camera *c = scene.camera;
  SDL_Color bg = self->bg;

  // -- 1. Draw camera objects to target
  SDL_SetRenderTarget(r, self->target);

  SDL_SetRenderDrawColor(r, bg.r, bg.g, bg.b, bg.a);
  SDL_RenderClear(r);
  const Uint16 n = scene.sprites_n;
  if (n == 0) {
    SDL_RenderPresent(r);
    return SDL_APP_CONTINUE;
  }

  RenderItem *items = SDL_calloc(n, sizeof(*items));
  if (!items)
    return dlog(ERROR_MEMORY_ALLOCATION, SDL_GetError());

  Uint16 count = 0;
  // --- Projection
  for (Uint16 i = 0; i < n; ++i) {
    const PT_Sprite *s = &scene.sprites[i];
    if (s->id == PT_TEXTURE_INVALID) continue;

    PT_CamPoint p = c->project(c, s->x, s->y, s->z, s->r);
    if (!p.visible) continue;

    items[count++] = (RenderItem){
      .rect = {
	p.sx - p.size * 0.5f,
	p.sy - p.size,
	p.size,
	p.size
      },
      .depth = p.depth,
      .texture = self->reg.textures[s->id]
    };
  }

  // --- Sort z buffer (painter's algorithm)
  if (c->type == PTR_CAMERA_P9E && count > 1)
    SDL_qsort(items, count, sizeof *items, cmp_depth);

  // --- Draw
  for (Uint16 i = 0; i < count; ++i) {
    SDL_RenderTexture(r, items[i].texture, NULL, &items[i].rect);
  }

  // --- Draw camera debug border
  SDL_SetRenderDrawColor(r, 255, 0, 0, 255);
  SDL_FRect cam_rect = {
    0.0f,
    0.0f,
    c->width,
    c->height
  };
  SDL_RenderRect(r, &cam_rect);

  // --- 2. Switch back to window
  SDL_SetRenderTarget(r, NULL);

  // If you would clean it here the window is black
  //SDL_SetRenderDrawColor(r, 0, 0, 0, 255);
  //SDL_RenderClear(r);

  SDL_FRect dst = {0, 0, 1920, 1080};
  SDL_RenderTexture(r, self->target, NULL, &dst);

  SDL_RenderPresent(r);
  SDL_free(items);
  return SDL_APP_CONTINUE;
}

Please ignore the camera perojection details, you can see on what’s being draw to the window it’s correct. With camera set to 640 x 320:

The scene appears in the upper right corner and not scaled to the window (hardcoded here to 1920 x 1080). My suspect is the custom image format I use index to INDEX8 pixel format:

#define PIXELFORMAT SDL_PIXELFORMAT_INDEX8
#define TEXTUREACCESS SDL_TEXTUREACCESS_STATIC

Here is how I create the textures in my renderer:

typedef struct {
  SDL_FRect rect;
  float depth;
  SDL_Texture *texture;
} RenderItem;

/* CPU to GPU texture data unit */
typedef struct {
  PT_ImageData *in;
  SDL_Texture *out;
} PT_TextureEntry;

struct PT_TextureRegistry {
  SDL_Texture *textures[TEXTURES_MAX];
  Uint16 textures_n;
};

struct PT_Renderer {
  SDL_Renderer *renderer;
  PT_TextureRegistry reg;
  SDL_Palette *palette;
  SDL_Color bg;

  SDL_Texture *target;
};

const PT_TextureID
PT_RegisterTexture(PT_Renderer *restrict self, const char *restrict const path)
{
  if (self->reg.textures_n >= TEXTURES_MAX) {
    plog("Reached textures limit (%i)", self->reg.textures_n);
    return PT_TEXTURE_INVALID;
  }

  SDL_AppResult e = SDL_APP_CONTINUE;
  PT_ImageData *img = PT_LoadImageData(path, &e);
  if (!img) {
    plog("Failed to load image data");
    return PT_TEXTURE_INVALID;
  }

  SDL_Texture *t = image2texture(self, img);
  PT_FreeImageData(img);
  if (!t) {
    plog("Failed to convert image to SDL surface");
    return PT_TEXTURE_INVALID;
  }

  const PT_TextureID id = self->reg.textures_n++;
  self->reg.textures[id] = t;
  return id;
}

PT_Renderer *restrict
PT_CreateRenderer(SDL_Window *restrict window, SDL_AppResult *restrict e, const Uint16 cam_w, const Uint16 cam_h) {
  PT_Renderer *self = SDL_malloc(sizeof *self);
  if (!self) {
    *e = elog(ERROR_SDL_CREATE_RENDERER);
    return NULL;
  }
  self->renderer = SDL_CreateRenderer(window, NULL);
  SDL_SetRenderDrawBlendMode(self->renderer, SDL_BLENDMODE_BLEND);
  self->reg.textures_n = 0;

  self->palette = PT_CreatePalette(e);
  self->bg = PT_GetColor(0x8D);

  self->target = SDL_CreateTexture(
    self->renderer,
    PIXELFORMAT,
    SDL_TEXTUREACCESS_TARGET,
    cam_w,
    cam_h
  );

  SDL_SetTextureScaleMode(self->target, SDL_SCALEMODE_NEAREST);

  return self;
}

Is this a known problem with indexed textures or am I completely in the weeds with my suspicions?

Okay, I should spend one moment thinking. With SDL_PIXELFORMAT_RGBA8888 textures I get proper target switch:

I just need to convert 1 byte pixel format to 4 bytes. Is this the right solution?

Make sure that you set the palette on the surface before you turn it into a texture using SDL_CreateTextureFromSurface(), or if you’re creating the 8-bit texture yourself, that you call SDL_SetTexturePalette() to give it a palette.

If you’re still seeing a problem after doing that, please report a bug on GitHub and include code for a minimal repro case that we can run here.

1 Like