mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-25 18:05:12 -04:00
widescreen: [video] aspect_ratio — GTE X-squash + stretched present
Adds the DuckStation-style widescreen hack as a config option, implemented in the runtime (no regen needed): - gte.cpp: gte_set_display_aspect(num, den). RTPS/RTPT scale the IR1*h/sz screen-X term by (4*den)/(3*num) around the game's own OFX projection centre (identity at 4:3). Games cull against the SXY values they read back from us, so post-projection screen- bounds culls stay aligned with the visible frame. - present: GL letterbox_rect generalised to the configured aspect (gl_renderer_set_display_aspect); SDL_Renderer path gets an aspect-derived logical size + dst rect. Window opens at the configured aspect (clamp_window_4x3 -> clamp_window_aspect). - config: [video] aspect_ratio = W:H (4:3 default, 16:9, up to 32:9) in game.toml, settings.toml (load+save), launcher seed. World geometry keeps correct proportions on the stretched present; screen-space 2D (HUD, FMV, sprite quad widths) stretches — the known limitation of the squash tier. EXPERIMENTAL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,6 +28,32 @@ static uint32_t parse_hex(const std::string& s, const std::string& field) {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a display aspect "W:H" string (e.g. "4:3", "16:9", "21:9"). Accepts
|
||||
// only aspects between native 4:3 and 32:9 — narrower than 4:3 has no
|
||||
// widescreen meaning and ultra-extreme values would squash the GTE X axis
|
||||
// into unusability. Returns false on malformed input or out-of-range aspect.
|
||||
static bool parse_aspect_ratio(const std::string& s, int* num, int* den) {
|
||||
const size_t colon = s.find(':');
|
||||
if (colon == std::string::npos || colon == 0 || colon + 1 >= s.size())
|
||||
return false;
|
||||
int n = 0, d = 0;
|
||||
try {
|
||||
size_t used = 0;
|
||||
n = std::stoi(s.substr(0, colon), &used);
|
||||
if (used != colon) return false;
|
||||
const std::string rest = s.substr(colon + 1);
|
||||
d = std::stoi(rest, &used);
|
||||
if (used != rest.size()) return false;
|
||||
} catch (const std::exception&) {
|
||||
return false;
|
||||
}
|
||||
if (n <= 0 || d <= 0 || n > 99 || d > 99) return false;
|
||||
if (3 * n < 4 * d) return false; // narrower than 4:3
|
||||
if (9 * n > 32 * d) return false; // wider than 32:9
|
||||
*num = n; *den = d;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Parse the optional [runtime] block. All fields optional; absent fields
|
||||
// leave has_* = false on the returned struct. Paths are resolved relative
|
||||
// to `root` (project root).
|
||||
@@ -124,6 +150,16 @@ static RuntimeConfig parse_runtime_block(const toml::value& cfg, const fs::path&
|
||||
if (video.contains("auto_skip_fmv")) {
|
||||
rt.video_auto_skip_fmv = toml::find<bool>(video, "auto_skip_fmv");
|
||||
}
|
||||
if (video.contains("aspect_ratio")) {
|
||||
const auto mode = toml::find<std::string>(video, "aspect_ratio");
|
||||
int n = 0, d = 0;
|
||||
if (!parse_aspect_ratio(mode, &n, &d))
|
||||
throw std::runtime_error(fmt::format(
|
||||
"[video] aspect_ratio must be \"W:H\", no narrower than 4:3 "
|
||||
"and no wider than 32:9 (e.g. \"4:3\", \"16:9\"): {}", mode));
|
||||
rt.video_aspect_num = n;
|
||||
rt.video_aspect_den = d;
|
||||
}
|
||||
}
|
||||
|
||||
// Optional [audio] block.
|
||||
@@ -510,6 +546,13 @@ UserSettings load_user_settings(const fs::path& path) {
|
||||
if (v.contains("auto_skip_fmv")) try_get([&]{
|
||||
s.auto_skip_fmv = toml::find<bool>(v, "auto_skip_fmv"); s.has_auto_skip_fmv = true;
|
||||
});
|
||||
if (v.contains("aspect_ratio")) try_get([&]{
|
||||
const auto m = toml::find<std::string>(v, "aspect_ratio");
|
||||
int n = 0, d = 0;
|
||||
if (parse_aspect_ratio(m, &n, &d)) {
|
||||
s.aspect_num = n; s.aspect_den = d; s.has_aspect_ratio = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
if (doc.contains("audio")) {
|
||||
const toml::value& a = toml::find(doc, "audio");
|
||||
@@ -614,6 +657,8 @@ bool save_user_settings(const fs::path& path, const UserSettings& s) {
|
||||
}
|
||||
if (s.has_auto_skip_fmv)
|
||||
f << "auto_skip_fmv = " << (s.auto_skip_fmv ? "true" : "false") << "\n";
|
||||
if (s.has_aspect_ratio)
|
||||
f << "aspect_ratio = \"" << s.aspect_num << ":" << s.aspect_den << "\"\n";
|
||||
f << "\n[audio]\n";
|
||||
if (s.has_spu_hq)
|
||||
f << "spu_hq = " << (s.spu_hq ? "true" : "false") << "\n";
|
||||
|
||||
@@ -113,6 +113,15 @@ struct RuntimeConfig {
|
||||
// all side effects (scene transition, save flags) happen exactly as normal.
|
||||
bool video_auto_skip_fmv = false;
|
||||
|
||||
// aspect_ratio: display aspect "W:H" (default "4:3" = native). A wider
|
||||
// aspect (e.g. "16:9") enables the widescreen hack: the GTE squashes
|
||||
// screen-X by (4*H)/(3*W) around the game's projection centre and the
|
||||
// present path stretches the 4:3 frame to W:H — net effect is a wider
|
||||
// field of view for GTE-projected geometry. Screen-space 2D (HUD, FMV,
|
||||
// sprite widths) stretches; world geometry keeps correct proportions.
|
||||
int video_aspect_num = 4;
|
||||
int video_aspect_den = 3;
|
||||
|
||||
// ---- [audio] block ----
|
||||
// spu_hq: enable the SPU float-shadow re-render (Catmull-Rom resample, float
|
||||
// headroom). Verified-enhancement, default OFF — spu_render output is
|
||||
@@ -230,6 +239,8 @@ struct UserSettings {
|
||||
// launcher window (mirrors snesrecomp's SkipLauncher). Overridable per-run:
|
||||
// `--launcher` forces the GUI back on; `PSX_NO_LAUNCHER=1` forces it off.
|
||||
bool has_skip_launcher = false; bool skip_launcher = false;
|
||||
bool has_aspect_ratio = false; int aspect_num = 4; // display aspect W:H
|
||||
int aspect_den = 3; // (4:3 = native)
|
||||
// [audio]
|
||||
bool has_spu_hq = false; bool spu_hq = false;
|
||||
// [bios] / [disc] / [memcard]
|
||||
|
||||
@@ -50,6 +50,10 @@ extern void psx_unknown_dispatch(CPUState* cpu, uint32_t addr, uint32_t phys);
|
||||
|
||||
/* GTE (COP2) — defined in runtime/src/gte.cpp */
|
||||
extern void gte_execute(CPUState* cpu, uint32_t cmd);
|
||||
/* Widescreen X-squash: display aspect num:den (4,3 = identity/off). Scales
|
||||
* RTPS/RTPT screen-X around the game's OFX so a 4:3 frame stretched to the
|
||||
* wide aspect shows a wider field of view. */
|
||||
extern void gte_set_display_aspect(int num, int den);
|
||||
extern uint32_t gte_read_data(CPUState* cpu, uint8_t reg);
|
||||
extern uint32_t gte_read_ctrl(CPUState* cpu, uint8_t reg);
|
||||
extern void gte_write_data(CPUState* cpu, uint8_t reg, uint32_t val);
|
||||
|
||||
@@ -32,10 +32,15 @@ void gl_renderer_present_blank(void);
|
||||
void gl_renderer_sync_cpu(void);
|
||||
|
||||
/* THE present path for 15-bit frames: blit the display region straight from
|
||||
* the authoritative VRAM FBO into a 4:3 letterboxed rect (no readback).
|
||||
* the authoritative VRAM FBO into a letterboxed rect (no readback).
|
||||
* Deterministic — used for every 15-bit frame. linear = filter on scale. */
|
||||
void gl_renderer_present_vram(int disp_x, int disp_y, int w, int h, int linear);
|
||||
|
||||
/* Display aspect for the present letterbox (default 4:3). A wide aspect
|
||||
* stretches the 4:3 frame; pair with gte_set_display_aspect (cpu_state.h)
|
||||
* for the widescreen field-of-view hack. */
|
||||
void gl_renderer_set_display_aspect(int num, int den);
|
||||
|
||||
void gl_renderer_shutdown(void);
|
||||
|
||||
/* Diagnostics (debug server): read GPU-side VRAM without touching the CPU
|
||||
|
||||
@@ -1110,10 +1110,20 @@ static void upload_present_tex(const uint32_t *pixels, int w, int h, int linear)
|
||||
}
|
||||
}
|
||||
|
||||
/* 4:3 letterbox: largest 4:3 rect centered in the drawable. */
|
||||
/* Display aspect for the present letterbox. Default 4:3 (native). When a wide
|
||||
* aspect is configured the 4:3 frame is stretched into it — paired with the
|
||||
* GTE X-squash (gte_set_display_aspect) this nets a wider field of view. */
|
||||
static int s_aspect_num = 4, s_aspect_den = 3;
|
||||
|
||||
void gl_renderer_set_display_aspect(int num, int den) {
|
||||
if (num <= 0 || den <= 0) { num = 4; den = 3; }
|
||||
s_aspect_num = num; s_aspect_den = den;
|
||||
}
|
||||
|
||||
/* Letterbox: largest s_aspect rect centered in the drawable. */
|
||||
static void letterbox_rect(int ww, int wh, int *x, int *y, int *w, int *h) {
|
||||
int dw = ww, dh = (ww * 3) / 4;
|
||||
if (dh > wh) { dh = wh; dw = (wh * 4) / 3; }
|
||||
int dw = ww, dh = (ww * s_aspect_den) / s_aspect_num;
|
||||
if (dh > wh) { dh = wh; dw = (wh * s_aspect_num) / s_aspect_den; }
|
||||
*x = (ww - dw) / 2;
|
||||
*y = (wh - dh) / 2;
|
||||
*w = dw; *h = dh;
|
||||
|
||||
+28
-1
@@ -105,6 +105,31 @@ static void depth_cue_from_ir(GTEState* gte) {
|
||||
gte->IR3 = gte->saturate_ir(gte->MAC3, 3, true);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Widescreen X-squash (verified-enhancement, default off = identity).
|
||||
//
|
||||
// For a display aspect wider than the native 4:3, screen-space X is scaled by
|
||||
// (4*den)/(3*num) around the projection centre OFX — e.g. 3/4 for 16:9 — and
|
||||
// the present path stretches the 4:3 frame to the wide aspect, netting a wider
|
||||
// horizontal field of view (the DuckStation/Beetle "widescreen hack", but
|
||||
// applied in our GTE library so every RTPS/RTPT caller — generated code,
|
||||
// interpreter, overlay DLLs — sees it). Only the IR1*h/sz term is scaled, NOT
|
||||
// OFX, so the squash is centred on the game's own projection centre and
|
||||
// games' post-projection screen-bounds culls (which read SXY back from us)
|
||||
// stay aligned with the visible frame.
|
||||
// ---------------------------------------------------------------------------
|
||||
static int32_t s_ws_xnum = 1, s_ws_xden = 1;
|
||||
|
||||
extern "C" void gte_set_display_aspect(int num, int den) {
|
||||
if (num <= 0 || den <= 0) { s_ws_xnum = s_ws_xden = 1; return; }
|
||||
// squash = (4/3) / (num/den) = (4*den) / (3*num); identity for 4:3.
|
||||
int32_t n = 4 * den, d = 3 * num;
|
||||
int32_t a = n, b = d;
|
||||
while (b) { int32_t t = a % b; a = b; b = t; } // gcd
|
||||
s_ws_xnum = n / a;
|
||||
s_ws_xden = d / a;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// RTPS — Perspective Transformation (internal, operates on given vertex V)
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -141,7 +166,9 @@ void gte_rtps_internal(GTEState* gte, int16_t* V, bool setMac0) {
|
||||
int32_t h_div_sz = gte_divide(gte->H, gte->SZ[3], gte->FLAG);
|
||||
|
||||
// Step 4: Project to screen coordinates
|
||||
int64_t sx = (gte->OFX + (int64_t)gte->IR1 * h_div_sz) >> 16;
|
||||
int64_t xterm = (int64_t)gte->IR1 * h_div_sz;
|
||||
if (s_ws_xnum != s_ws_xden) xterm = xterm * s_ws_xnum / s_ws_xden;
|
||||
int64_t sx = (gte->OFX + xterm) >> 16;
|
||||
int64_t sy = (gte->OFY + (int64_t)gte->IR2 * h_div_sz) >> 16;
|
||||
gte->push_sxy(sx, sy);
|
||||
|
||||
|
||||
+55
-24
@@ -118,7 +118,7 @@ static bool g_video_aa = true; /* linear present filtering */
|
||||
static int g_video_texfilter = 0; /* 0=nearest, 1=bilinear */
|
||||
static int g_video_renderer = 0; /* 0=software, 1=opengl (requested) */
|
||||
static int g_video_screen = 0; /* 0=raw,1=crt,2=composite,3=trinitron */
|
||||
static int g_video_win_w = 1280; /* window width (height = w*3/4, 4:3) */
|
||||
static int g_video_win_w = 1280; /* window width (height follows aspect) */
|
||||
static bool g_audio_spu_hq = false; /* SPU float-shadow (env overrides) */
|
||||
static int g_auto_skip_fmv = 0; /* fast-forward FMVs invisibly to EOF */
|
||||
|
||||
@@ -126,19 +126,29 @@ static int g_auto_skip_fmv = 0; /* fast-forward FMVs invisibly to E
|
||||
extern "C" int cdrom_xa_stream_active(void);
|
||||
extern "C" uint32_t mdec_get_decode_count(void);
|
||||
|
||||
/* Clamp a requested 4:3 window width to the primary display's usable area so an
|
||||
* oversized choice (e.g. 1920 on a 1080p panel) still fits on screen. Keeps the
|
||||
* 4:3 aspect: height = width*3/4. */
|
||||
static void clamp_window_4x3(int* w, int* h) {
|
||||
/* Display aspect W:H (default 4:3 = native). Wider aspects enable the
|
||||
* widescreen hack: GTE X-squash + stretched present (see [video] aspect_ratio
|
||||
* in config_loader.h). */
|
||||
static int g_video_aspect_num = 4;
|
||||
static int g_video_aspect_den = 3;
|
||||
/* Logical present width for the SDL_Renderer (software) path; 640*scale at
|
||||
* 4:3, wider for wide aspects. Height is always 480*scale. Set at window
|
||||
* creation alongside SDL_RenderSetLogicalSize. */
|
||||
static int g_logical_w = 640;
|
||||
|
||||
/* Clamp a requested window width to the primary display's usable area so an
|
||||
* oversized choice (e.g. 1920 on a 1080p panel) still fits on screen. Keeps
|
||||
* the given aspect: height = width*den/num. */
|
||||
static void clamp_window_aspect(int* w, int* h, int num, int den) {
|
||||
int width = *w;
|
||||
if (width < 640) width = 640;
|
||||
SDL_Rect bounds;
|
||||
if (SDL_GetDisplayUsableBounds(0, &bounds) == 0 && bounds.w > 0 && bounds.h > 0) {
|
||||
if (width > bounds.w) width = bounds.w;
|
||||
if (width * 3 / 4 > bounds.h) width = bounds.h * 4 / 3;
|
||||
if (width > bounds.w) width = bounds.w;
|
||||
if (width * den / num > bounds.h) width = bounds.h * num / den;
|
||||
}
|
||||
*w = width;
|
||||
*h = width * 3 / 4;
|
||||
*h = width * den / num;
|
||||
}
|
||||
static bool g_gl_active = false; /* GL context live -> GL present path */
|
||||
/* Present straight from the FBO (fast, no readback). Set PSX_GL_FORCE_CPU_PRESENT=1
|
||||
@@ -1295,7 +1305,7 @@ static void sdl_vblank_present(void) {
|
||||
SDL_UpdateTexture(sdl_texture, &src, sdl_pixel_buf,
|
||||
(int)(src_w * sizeof(uint32_t)));
|
||||
|
||||
SDL_Rect dst = { 0, 0, 640 * g_video_scale, 480 * g_video_scale };
|
||||
SDL_Rect dst = { 0, 0, g_logical_w, 480 * g_video_scale };
|
||||
SDL_RenderClear(sdl_renderer);
|
||||
SDL_RenderCopy(sdl_renderer, sdl_texture, &src, &dst);
|
||||
|
||||
@@ -1423,13 +1433,15 @@ int main(int argc, char** argv) {
|
||||
g_turbo_loads_enabled = 1;
|
||||
std::fprintf(stdout, "psxrecomp: turbo_loads enabled (opt-in)\n");
|
||||
}
|
||||
g_video_scale = gc.runtime.video_supersampling;
|
||||
g_video_aa = gc.runtime.video_antialiasing;
|
||||
g_video_texfilter = gc.runtime.video_texture_filter;
|
||||
g_video_renderer = gc.runtime.video_renderer;
|
||||
g_video_screen = gc.runtime.video_screen_kind;
|
||||
g_audio_spu_hq = gc.runtime.audio_spu_hq;
|
||||
g_auto_skip_fmv = gc.runtime.video_auto_skip_fmv ? 1 : 0;
|
||||
g_video_scale = gc.runtime.video_supersampling;
|
||||
g_video_aa = gc.runtime.video_antialiasing;
|
||||
g_video_texfilter = gc.runtime.video_texture_filter;
|
||||
g_video_renderer = gc.runtime.video_renderer;
|
||||
g_video_screen = gc.runtime.video_screen_kind;
|
||||
g_video_aspect_num = gc.runtime.video_aspect_num;
|
||||
g_video_aspect_den = gc.runtime.video_aspect_den;
|
||||
g_audio_spu_hq = gc.runtime.audio_spu_hq;
|
||||
g_auto_skip_fmv = gc.runtime.video_auto_skip_fmv ? 1 : 0;
|
||||
{ const char *e = std::getenv("PSX_GL_FORCE_CPU_PRESENT");
|
||||
if (e && e[0] && e[0] != '0') g_gl_fbo_present = 0; }
|
||||
game_entry_pc = gc.entry_pc;
|
||||
@@ -1488,6 +1500,10 @@ int main(int argc, char** argv) {
|
||||
if (us.has_texture_filter) g_video_texfilter = us.texture_filter;
|
||||
if (us.has_screen_kind) g_video_screen = us.screen_kind;
|
||||
if (us.has_auto_skip_fmv) g_auto_skip_fmv = us.auto_skip_fmv ? 1 : 0;
|
||||
if (us.has_aspect_ratio) {
|
||||
g_video_aspect_num = us.aspect_num;
|
||||
g_video_aspect_den = us.aspect_den;
|
||||
}
|
||||
if (us.has_spu_hq) g_audio_spu_hq = us.spu_hq;
|
||||
if (us.has_bios_path && !bios_from_cli) {
|
||||
settings_bios_storage = us.bios_path.string();
|
||||
@@ -1533,6 +1549,8 @@ int main(int argc, char** argv) {
|
||||
seed.texture_filter = g_video_texfilter; seed.has_texture_filter = true;
|
||||
seed.screen_kind = g_video_screen; seed.has_screen_kind = true;
|
||||
seed.auto_skip_fmv = (g_auto_skip_fmv != 0); seed.has_auto_skip_fmv = true;
|
||||
seed.aspect_num = g_video_aspect_num;
|
||||
seed.aspect_den = g_video_aspect_den; seed.has_aspect_ratio = true;
|
||||
seed.spu_hq = g_audio_spu_hq; seed.has_spu_hq = true;
|
||||
seed.skip_launcher = skip_launcher_setting; seed.has_skip_launcher = true;
|
||||
if (bios_path && bios_path[0]) { seed.bios_path = bios_path; seed.has_bios_path = true; }
|
||||
@@ -1559,7 +1577,7 @@ int main(int argc, char** argv) {
|
||||
* window is set smaller. */
|
||||
int lwin_w = g_video_win_w < 1280 ? 1280 : g_video_win_w;
|
||||
int lwin_h = 0;
|
||||
clamp_window_4x3(&lwin_w, &lwin_h);
|
||||
clamp_window_aspect(&lwin_w, &lwin_h, 4, 3);
|
||||
std::string lwin_title = (game_name.empty() ? std::string("PSX") : game_name)
|
||||
+ " \xE2\x80\x94 Launcher";
|
||||
SDL_Window* lwin = SDL_CreateWindow(
|
||||
@@ -1596,6 +1614,8 @@ int main(int argc, char** argv) {
|
||||
g_video_texfilter = seed.texture_filter;
|
||||
g_video_screen = seed.screen_kind;
|
||||
g_auto_skip_fmv = seed.auto_skip_fmv ? 1 : 0;
|
||||
g_video_aspect_num = seed.aspect_num;
|
||||
g_video_aspect_den = seed.aspect_den;
|
||||
g_audio_spu_hq = seed.spu_hq;
|
||||
skip_launcher_setting = seed.skip_launcher;
|
||||
if (seed.has_bios_path) {
|
||||
@@ -1654,6 +1674,14 @@ int main(int argc, char** argv) {
|
||||
gr_set_scale(g_video_scale);
|
||||
g_video_scale = gr_scale(); /* reflect any clamp / alloc fallback */
|
||||
gr_set_texture_filter(g_video_texfilter);
|
||||
/* Display aspect (widescreen hack). Identity at the default 4:3. The GTE
|
||||
* squashes screen-X around the game's projection centre; both present
|
||||
* paths stretch the 4:3 frame to the configured aspect. */
|
||||
gte_set_display_aspect(g_video_aspect_num, g_video_aspect_den);
|
||||
gl_renderer_set_display_aspect(g_video_aspect_num, g_video_aspect_den);
|
||||
if (g_video_aspect_num * 3 != g_video_aspect_den * 4)
|
||||
std::fprintf(stdout, "psxrecomp: widescreen %d:%d (GTE X-squash + stretched present)\n",
|
||||
g_video_aspect_num, g_video_aspect_den);
|
||||
/* Present-time screen-colour model (verified-enhancement LUT). Default raw
|
||||
* is byte-identical; PSX_SCREEN env overrides this at scanout. */
|
||||
gpu_set_screen_kind(g_video_screen);
|
||||
@@ -1759,12 +1787,13 @@ int main(int argc, char** argv) {
|
||||
|
||||
Uint32 win_flags = SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE;
|
||||
if (g_video_renderer == 1) win_flags |= SDL_WINDOW_OPENGL;
|
||||
/* Open at the user-chosen 4:3 window size (default 1280x960) instead of the
|
||||
/* Open at the user-chosen window size (default 1280 wide) instead of the
|
||||
* old hardcoded 640x480, so the game doesn't boot into a tiny window. The
|
||||
* present path keeps a 640x480 logical space, so the image scales to fill
|
||||
* the larger window with no aspect distortion. */
|
||||
* height follows the configured display aspect (4:3 native, wider for the
|
||||
* widescreen hack); the present path letterboxes to the same aspect, so
|
||||
* the image scales to fill the larger window with no further distortion. */
|
||||
int game_w = g_video_win_w, game_h = 0;
|
||||
clamp_window_4x3(&game_w, &game_h);
|
||||
clamp_window_aspect(&game_w, &game_h, g_video_aspect_num, g_video_aspect_den);
|
||||
sdl_window = SDL_CreateWindow(
|
||||
window_title.c_str(),
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
@@ -1826,12 +1855,14 @@ int main(int argc, char** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Present in a 640x480 logical space (scaled by the supersampling factor
|
||||
* so the full internal resolution reaches a large/fullscreen window; SDL
|
||||
/* Present in a logical space of the configured aspect (640x480 at native
|
||||
* 4:3, wider at widescreen aspects; scaled by the supersampling factor so
|
||||
* the full internal resolution reaches a large/fullscreen window; SDL
|
||||
* still scales and letterboxes to the real output). Identity in the
|
||||
* default window when supersampling is off, so native rendering is
|
||||
* unchanged. */
|
||||
SDL_RenderSetLogicalSize(sdl_renderer, 640 * g_video_scale, 480 * g_video_scale);
|
||||
g_logical_w = 480 * g_video_aspect_num * g_video_scale / g_video_aspect_den;
|
||||
SDL_RenderSetLogicalSize(sdl_renderer, g_logical_w, 480 * g_video_scale);
|
||||
}
|
||||
|
||||
/* Staging buffer + backing texture are sized for the internal resolution
|
||||
|
||||
Reference in New Issue
Block a user