apps/doom: refactor rendering to integer scale 24bpp for better performance on big window sizes
Build system / Check kernel codestyle (pull_request) Successful in 27s
Build system / Build (pull_request) Successful in 11m0s

This commit is contained in:
2026-06-08 16:15:14 +03:00
parent c4078273b8
commit 6c8b6b3b76
+113 -50
View File
@@ -44,6 +44,11 @@ SDL_Surface *screen;
// it to the (arbitrary) window size each frame.
SDL_Surface *render;
// 24bpp truecolor twin of render: the 8->24 palette conversion is done once
// here at 320x200 (cheap), so the window-sized surface is truecolor - no
// per-pixel palette pass, and the display needs no 8->24 shadow conversion.
SDL_Surface *render24;
// Fake mouse handling.
boolean grabMouse;
@@ -220,6 +225,68 @@ void I_UpdateNoBlit (void)
extern int screenblocks; // view size; >=11 means the bar is an overlay
extern boolean menuactive; // a menu is up: present the whole frame, not the split
//
// I_BlitViewCoverInt
//
// Integer "cover" magnification of the 24bpp source into the dest: scaled by the
// smallest whole factor that fills the dest in both axes, centered, overflow
// cropped. Each source row is built once and memcpy'd to the rows it repeats -
// much cheaper than a per-pixel stretch, and pixel-crisp. 24bpp (3 bytes) only.
//
static void I_BlitViewCoverInt(SDL_Surface *src, int sw, int sh,
SDL_Surface *dst, int dw, int dh)
{
Uint8 *dstpix = (Uint8 *)dst->pixels;
Uint8 *prev_row = NULL;
int N, ny, offX, offY, oy, prev_sy = -1;
if (dw <= 0 || dh <= 0)
return;
N = (dw + sw - 1) / sw; // ceil: smallest factor that covers
ny = (dh + sh - 1) / sh;
if (ny > N) N = ny;
offX = (N*sw - dw) / 2; // centered, overflow cropped
offY = (N*sh - dh) / 2;
for (oy = 0; oy < dh; oy++)
{
Uint8 *orow = dstpix + oy*dst->pitch;
int sy = (oy + offY) / N;
if (sy == prev_sy) // repeats the row above: copy it
{
memcpy(orow, prev_row, dw*3);
continue;
}
// build one source row, run by run (each source pixel -> N pixels)
{
Uint8 *srow = (Uint8 *)src->pixels + sy*src->pitch;
Uint8 *dp = orow;
int ox = 0, sx = offX / N;
while (ox < dw)
{
Uint8 *sp = srow + sx*3;
Uint8 r = sp[0], g = sp[1], b = sp[2];
int run_end = (sx+1)*N - offX;
if (run_end > dw)
run_end = dw;
for (; ox < run_end; ox++)
{
dp[0] = r; dp[1] = g; dp[2] = b;
dp += 3;
}
sx++;
}
}
prev_sy = sy;
prev_row = orow;
}
}
void I_FinishUpdate (void)
{
@@ -242,68 +309,54 @@ void I_FinishUpdate (void)
screens[0][ (SCREENHEIGHT-1)*SCREENWIDTH + i] = 0x0;
}
// scales the screen size before blitting it
// The 8bpp frame is converted to truecolor once here at 320x200; every blit
// below is 24bpp, so the window-sized surface gets no per-pixel palette pass
// and the display needs no 8->24 shadow conversion on update.
SDL_BlitSurface(render, NULL, render24, NULL);
if ( SDL_MUSTLOCK(screen) ) {
if ( SDL_LockSurface(screen) < 0 ) {
return;
}
if ( SDL_LockSurface(screen) < 0 ) {
return;
}
}
//
// In-game the 320x200 frame is presented in two pieces so the picture
// never distorts with the window shape:
// * the status bar gets the full window width with a proportional
// height (uniform scale, no horizontal stretch);
// * the 3D view "covers" the remaining space - scaled uniformly to
// fill it, cropping whatever overflows, centered.
// * the 3D view "covers" the space above the bar - magnified by a whole
// number (centered, overflow cropped): crisp pixels and a cheap
// row-replicating blit;
// * the status bar gets the full window width with a proportional height.
// Menus and full-screen images are stretched whole (no crop, no seam).
//
if (gamestate == GS_LEVEL && !menuactive)
{
int W = screen->w;
int H = screen->h;
int sbar_src_h = (screenblocks >= 11) ? 0 : I_SBARHEIGHT;
int hud_h = sbar_src_h * W / SCREENWIDTH;
int view_h;
int sh;
SDL_Rect view_src, view_dst;
int W = screen->w;
int H = screen->h;
int sbar_src_h = (screenblocks >= 11) ? 0 : I_SBARHEIGHT;
int hud_h = sbar_src_h * W / SCREENWIDTH;
int view_h;
int sh;
if (hud_h > H) hud_h = H;
view_h = H - hud_h;
sh = SCREENHEIGHT - sbar_src_h;
if (hud_h > H) hud_h = H;
view_h = H - hud_h;
sh = SCREENHEIGHT - sbar_src_h;
// 3D view: cover the area above the bar (uniform scale, crop, center)
view_dst.x = 0; view_dst.y = 0; view_dst.w = W; view_dst.h = view_h;
if ((long)W*sh >= (long)view_h*SCREENWIDTH)
{
// window relatively wider: keep full src width, crop src height
int vis_h = (int)((long)view_h*SCREENWIDTH / W);
if (vis_h < 1) vis_h = 1;
view_src.x = 0; view_src.w = SCREENWIDTH;
view_src.y = (sh-vis_h)/2; view_src.h = vis_h;
}
else
{
// window relatively taller: keep full src height, crop src width
int vis_w = (int)((long)W*sh / view_h);
if (vis_w < 1) vis_w = 1;
view_src.y = 0; view_src.h = sh;
view_src.x = (SCREENWIDTH-vis_w)/2; view_src.w = vis_w;
}
if (view_h > 0)
SDL_SoftStretch(render, &view_src, screen, &view_dst);
// 3D view: integer cover-center magnification (8->24 already done above)
if (view_h > 0)
I_BlitViewCoverInt(render24, SCREENWIDTH, sh, screen, W, view_h);
// status bar: full window width, proportional height
if (hud_h > 0)
{
SDL_Rect hud_src = { 0, SCREENHEIGHT-I_SBARHEIGHT, SCREENWIDTH, I_SBARHEIGHT };
SDL_Rect hud_dst = { 0, view_h, W, hud_h };
SDL_SoftStretch(render, &hud_src, screen, &hud_dst);
}
// status bar: full window width, proportional height
if (hud_h > 0)
{
SDL_Rect hud_src = { 0, SCREENHEIGHT-I_SBARHEIGHT, SCREENWIDTH, I_SBARHEIGHT };
SDL_Rect hud_dst = { 0, view_h, W, hud_h };
SDL_SoftStretch(render24, &hud_src, screen, &hud_dst);
}
}
else
SDL_SoftStretch(render, NULL, screen, NULL);
SDL_SoftStretch(render24, NULL, screen, NULL);
if ( SDL_MUSTLOCK(screen) ) {
SDL_UnlockSurface(screen);
SDL_UnlockSurface(screen);
}
SDL_UpdateRect(screen, 0, 0, 0, 0);
}
@@ -332,7 +385,7 @@ void I_SetPalette (byte* palette)
colors[i].b = gammatable[usegamma][*palette++];
colors[i].unused = 0;
}
SDL_SetColors(screen, colors, 0, 256);
SDL_SetColors(render, colors, 0, 256);
}
@@ -348,7 +401,7 @@ void I_InitGraphics(void)
return;
firsttime = 0;
video_flags = (SDL_SWSURFACE|SDL_HWPALETTE);
video_flags = SDL_SWSURFACE;
if (!!M_CheckParm("-fullscreen"))
video_flags |= SDL_FULLSCREEN;
@@ -364,7 +417,10 @@ void I_InitGraphics(void)
video_h = atoi(myargv[i+2]);
}
screen = SDL_SetVideoMode(video_w, video_h, 8, video_flags);
// 24bpp truecolor window: the kolibri framebuffer is 24bpp, so a truecolor
// surface avoids SDL's per-frame 8->24 shadow conversion across the whole
// window (the 8->24 step is done once at 320x200 in I_FinishUpdate).
screen = SDL_SetVideoMode(video_w, video_h, 24, video_flags);
if ( screen == NULL ) {
I_Error("Could not set %dx%d video mode: %s", video_w, video_h,
SDL_GetError());
@@ -378,5 +434,12 @@ void I_InitGraphics(void)
8, 0, 0, 0, 0);
if ( render == NULL )
I_Error("Cannot create render surface: %s", SDL_GetError());
// 24bpp truecolor copy of render, scaled to the window (see I_FinishUpdate)
render24 = SDL_CreateRGBSurface(SDL_SWSURFACE, SCREENWIDTH, SCREENHEIGHT,
screen->format->BitsPerPixel,
screen->format->Rmask, screen->format->Gmask,
screen->format->Bmask, screen->format->Amask);
if ( render24 == NULL )
I_Error("Cannot create render24 surface: %s", SDL_GetError());
screens[0] = (unsigned char *) render->pixels;
}