fix(vk): a picture larger than the window is filtered over all its pixels, not sampled #307

Merged
HumanBean merged 1 commit from Amateurwizrd/Longshot:fix/downscale-filter into main 2026-11-14 01:41:32 +00:00
Contributor

Why

An Output resolution larger than the window (3840x2160 or 5120x2880 in a 2560x1440 window, 5K on a 4K screen) is meant to be the sharp, smooth choice: drawn bigger, then scaled down. The window pass sampled that picture instead of averaging it, so fine detail (grass, foliage, fences, text edges) came out as shimmer and moiré, worse with Catmull-Rom or Lanczos than with the filter Off.

What changed

  • shader: filtered() in gpu/vk/shaders/fg_scale.comp stretches the filter by the picture's pixels per window pixel when the picture is the larger (up to 4x, 16 x 16 taps). Catmull-Rom and Lanczos read 4 x 4 of the picture whatever the shrink and skipped most of it; at 2x a window pixel now reads the 8 x 8 its kernel covers. Off becomes a stretched linear (tent) filter when shrinking. A window as large as the picture or larger is filtered exactly as before (the same 4 x 4 taps, the same weights; Off the same hardware bilinear).
  • vk: framegen_scale() in gpu/vk/vk_framegen.cpp takes the pass whenever the picture is larger than the window. With the filter Off and Sharpness 0 the picture took blit_crop_to_rect's vkCmdBlitImage(VK_FILTER_LINEAR), which reads 2 x 2 texels whatever the shrink and aliases too.
  • SPIR-V: fg_scale_comp_spv.h and fg_scale_pq_comp_spv.h regenerated (tests/test_fg_scale_sharpen.py --write, glslangValidator from Vulkan SDK 1.4.341.1; main's headers came from a newer glslang, so the test compares their hash line, not their bytes).
  • test: tests/test_fg_scale_sharpen.py compiles the shader's own kernel() and reach() as C++ and resamples sines finer than the window at 1.33x, 1.5x, 2x and 2.67x shrinks: at most 0.3 of their swing may come through (0.02 to 0.27 now; the same resampling with main's fixed 4 x 4 taps or a 2 x 2 bilinear lets 0.27 to 0.89 through, from a numpy model of the old code).

Flags and settings

None. No switch: the old behaviour is a bug, and a picture no larger than the window is untouched.

Measurements

main: 514591a3 (2026-11-13)
this PR: fix/downscale-filter @ f057a4e4
Hardware: NVIDIA GeForce RTX 5070 Ti, driver 621.01, AMD Ryzen 7 7800X3D, 32 GB, Windows 11
Scene: Kokiri Forest, standing at a fixed save; Output resolution 5120x2880 (HOS_OUTPUT_RES) in a 2560x1440 window, Render resolution 75 %, the game's own upscaler, no frame generation, no frame cap
Method: PresentMon, 60 s a leg after 15 s settle; 3 rounds, the two builds alternating in each round; the same game folder with only longshot_vk.dll swapped (both built with RX_VK_ONLY=1, same toolchain). Medians of the three rounds.
How to reproduce: Output resolution larger than the window (5120x2880 in a 2560x1440 window), stand still in Kokiri Forest, read GPU busy from PresentMon or the FPS counter's Breakdown.

main @ 514591a3 this PR @ f057a4e4
Lanczos, Sharpness 50: frame time (median ms) 10.20 10.20
Lanczos, Sharpness 50: GPU busy (median ms) 8.98 9.29
Lanczos, Sharpness 50: 1 % low fps 80.0 80.9
Off, Sharpness 0: frame time (median ms) 10.23 10.19
Off, Sharpness 0: GPU busy (median ms) 8.87 8.84
Off, Sharpness 0: 1 % low fps 79.0 80.9

GPU busy with Lanczos rose by 0.30, 0.34 and 0.27 ms in the three rounds: the cost of reading the 8 x 8 the 2x shrink needs. Off: 0.03 ms less in every round. Frame time is not GPU-bound here and did not move.

round  filter   main frame/GPU (ms)   this PR frame/GPU (ms)
r1     Lanczos  10.188 / 8.984        10.238 / 9.285
r2     Lanczos  10.200 / 8.982        10.180 / 9.319
r3     Lanczos  10.287 / 9.011        10.200 / 9.278
r1     Off      10.208 / 8.869        10.185 / 8.842
r2     Off      10.227 / 8.857        10.167 / 8.831
r3     Off      10.516 / 8.917        10.212 / 8.874

Testing

Tested: python -I -B tests/run_all.py on Windows 11 with llvm-mingw: 490 passed, 10 failed, 33 skipped. Seven of the failures fail the same way on main 514591a3 (test_nup_paths, test_check_third_party, test_gpu_hang_watch, test_menu_i18n, test_portable_builder_startup, test_portable_builder_home, test_release). The other three were this PC's setup: test_fpx_sticky and test_spawn_emit pass with LONGSHOT_MINGW set, and test_setup_tar_diagnostics passed when run again. In the game on the PC above: 5120x2880 in a 2560x1440 window with Lanczos/Sharpness 50 and Off/Sharpness 0, 12 measured legs and 4 screenshot legs, no device loss, the picture drawn whole in both filters.

Not tested: Native HDR's 10-bit pass (fg_scale_pq) with a picture larger than the window; the ultrawide menu sides while shrinking; frame generation on; AMD and Intel GPUs; Linux. A side-by-side picture of the shimmer: Navi and the particles move between launches, so still shots of the two builds don't line up; the game-free check measures the aliasing instead.

Needs other testers:

  • AMD GPU: a picture larger than the window with Lanczos, expecting no shimmer on grass in motion and GPU time within about 0.3 ms of main (T-Z9VV)

Screenshots

Nothing to compare as a still: the change shows as less shimmer in motion. The game-free check's numbers are under What changed.

Risk and rollback

GPU cost when shrinking with Catmull-Rom or Lanczos: about 0.3 ms at 2x on an RTX 5070 Ti, up to 16 x 16 taps at the 4x cap (a 5K picture in a 1280x720 window). Shrinking more than 4x aliases again, as before. A picture no larger than the window is filtered as on main. Reverting the commit restores the old filter.

Reviewer notes

filtered() is the change; at stretch 1 its tap range is the old b - 1 .. b + 2. A mip chain would make shrinks past 4x clean; left out as nobody plays a 5K picture in a quarter-size window.

Issues

None.

Checklist

For everyone

  • One topic.
  • The title is a Conventional Commit (type(scope): description).
  • Rebased on current main.
  • python -I -B tests/run_all.py run and passing (any failure that also fails on main is named above).
  • Tested in the game, or what wasn't is named under Testing.
  • Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests).
  • Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported.
  • No game files, dumps, build output or generated files.
  • third_party/ untouched (a new folder with a PROVENANCE.md instead).
  • The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled.

If applicable

  • AI assistance recorded with Assisted-by: / Co-Authored-By: trailers.
  • The SPIR-V headers regenerated with glslang (a shader changed).
  • A game-free test added for the fix, failing on main and passing here.
  • A check under tests/ that reads changed source updated in this PR, not weakened.
## Why An Output resolution larger than the window (3840x2160 or 5120x2880 in a 2560x1440 window, 5K on a 4K screen) is meant to be the sharp, smooth choice: drawn bigger, then scaled down. The window pass sampled that picture instead of averaging it, so fine detail (grass, foliage, fences, text edges) came out as shimmer and moiré, worse with Catmull-Rom or Lanczos than with the filter Off. ## What changed - shader: `filtered()` in `gpu/vk/shaders/fg_scale.comp` stretches the filter by the picture's pixels per window pixel when the picture is the larger (up to 4x, 16 x 16 taps). Catmull-Rom and Lanczos read 4 x 4 of the picture whatever the shrink and skipped most of it; at 2x a window pixel now reads the 8 x 8 its kernel covers. Off becomes a stretched linear (tent) filter when shrinking. A window as large as the picture or larger is filtered exactly as before (the same 4 x 4 taps, the same weights; Off the same hardware bilinear). - vk: `framegen_scale()` in `gpu/vk/vk_framegen.cpp` takes the pass whenever the picture is larger than the window. With the filter Off and Sharpness 0 the picture took `blit_crop_to_rect`'s `vkCmdBlitImage(VK_FILTER_LINEAR)`, which reads 2 x 2 texels whatever the shrink and aliases too. - SPIR-V: `fg_scale_comp_spv.h` and `fg_scale_pq_comp_spv.h` regenerated (`tests/test_fg_scale_sharpen.py --write`, glslangValidator from Vulkan SDK 1.4.341.1; `main`'s headers came from a newer glslang, so the test compares their hash line, not their bytes). - test: `tests/test_fg_scale_sharpen.py` compiles the shader's own `kernel()` and `reach()` as C++ and resamples sines finer than the window at 1.33x, 1.5x, 2x and 2.67x shrinks: at most 0.3 of their swing may come through (0.02 to 0.27 now; the same resampling with `main`'s fixed 4 x 4 taps or a 2 x 2 bilinear lets 0.27 to 0.89 through, from a numpy model of the old code). ## Flags and settings None. No switch: the old behaviour is a bug, and a picture no larger than the window is untouched. ## Measurements main: 514591a3 (2026-11-13) this PR: fix/downscale-filter @ f057a4e4 Hardware: NVIDIA GeForce RTX 5070 Ti, driver 621.01, AMD Ryzen 7 7800X3D, 32 GB, Windows 11 Scene: Kokiri Forest, standing at a fixed save; Output resolution 5120x2880 (`HOS_OUTPUT_RES`) in a 2560x1440 window, Render resolution 75 %, the game's own upscaler, no frame generation, no frame cap Method: PresentMon, 60 s a leg after 15 s settle; 3 rounds, the two builds alternating in each round; the same game folder with only `longshot_vk.dll` swapped (both built with `RX_VK_ONLY=1`, same toolchain). Medians of the three rounds. How to reproduce: Output resolution larger than the window (5120x2880 in a 2560x1440 window), stand still in Kokiri Forest, read GPU busy from PresentMon or the FPS counter's Breakdown. | | main @ 514591a3 | this PR @ f057a4e4 | |---|---|---| | Lanczos, Sharpness 50: frame time (median ms) | 10.20 | 10.20 | | Lanczos, Sharpness 50: GPU busy (median ms) | 8.98 | 9.29 | | Lanczos, Sharpness 50: 1 % low fps | 80.0 | 80.9 | | Off, Sharpness 0: frame time (median ms) | 10.23 | 10.19 | | Off, Sharpness 0: GPU busy (median ms) | 8.87 | 8.84 | | Off, Sharpness 0: 1 % low fps | 79.0 | 80.9 | GPU busy with Lanczos rose by 0.30, 0.34 and 0.27 ms in the three rounds: the cost of reading the 8 x 8 the 2x shrink needs. Off: 0.03 ms less in every round. Frame time is not GPU-bound here and did not move. ``` round filter main frame/GPU (ms) this PR frame/GPU (ms) r1 Lanczos 10.188 / 8.984 10.238 / 9.285 r2 Lanczos 10.200 / 8.982 10.180 / 9.319 r3 Lanczos 10.287 / 9.011 10.200 / 9.278 r1 Off 10.208 / 8.869 10.185 / 8.842 r2 Off 10.227 / 8.857 10.167 / 8.831 r3 Off 10.516 / 8.917 10.212 / 8.874 ``` ## Testing **Tested:** `python -I -B tests/run_all.py` on Windows 11 with llvm-mingw: 490 passed, 10 failed, 33 skipped. Seven of the failures fail the same way on `main` 514591a3 (`test_nup_paths`, `test_check_third_party`, `test_gpu_hang_watch`, `test_menu_i18n`, `test_portable_builder_startup`, `test_portable_builder_home`, `test_release`). The other three were this PC's setup: `test_fpx_sticky` and `test_spawn_emit` pass with `LONGSHOT_MINGW` set, and `test_setup_tar_diagnostics` passed when run again. In the game on the PC above: 5120x2880 in a 2560x1440 window with Lanczos/Sharpness 50 and Off/Sharpness 0, 12 measured legs and 4 screenshot legs, no device loss, the picture drawn whole in both filters. **Not tested:** Native HDR's 10-bit pass (`fg_scale_pq`) with a picture larger than the window; the ultrawide menu sides while shrinking; frame generation on; AMD and Intel GPUs; Linux. A side-by-side picture of the shimmer: Navi and the particles move between launches, so still shots of the two builds don't line up; the game-free check measures the aliasing instead. **Needs other testers:** - AMD GPU: a picture larger than the window with Lanczos, expecting no shimmer on grass in motion and GPU time within about 0.3 ms of `main` (T-Z9VV) ## Screenshots Nothing to compare as a still: the change shows as less shimmer in motion. The game-free check's numbers are under What changed. ## Risk and rollback GPU cost when shrinking with Catmull-Rom or Lanczos: about 0.3 ms at 2x on an RTX 5070 Ti, up to 16 x 16 taps at the 4x cap (a 5K picture in a 1280x720 window). Shrinking more than 4x aliases again, as before. A picture no larger than the window is filtered as on `main`. Reverting the commit restores the old filter. ## Reviewer notes `filtered()` is the change; at stretch 1 its tap range is the old `b - 1 .. b + 2`. A mip chain would make shrinks past 4x clean; left out as nobody plays a 5K picture in a quarter-size window. ## Issues None. ## Checklist ### For everyone - [x] One topic. - [x] The title is a Conventional Commit (`type(scope): description`). - [x] Rebased on current `main`. - [x] `python -I -B tests/run_all.py` run and passing (any failure that also fails on `main` is named above). - [x] Tested in the game, or what wasn't is named under Testing. - [x] Testing filled in: what was tested, what wasn't, and what needs other testers (with test requests). - [x] Measured: main and this PR at named commits, on the same PC, scene and settings, at least 2 interleaved runs each, the median reported. - [x] No game files, dumps, build output or generated files. - [x] `third_party/` untouched (a new folder with a `PROVENANCE.md` instead). - [x] The license is OK: GPL-3.0-or-later, nothing incompatible and nothing decompiled. ### If applicable - [x] AI assistance recorded with `Assisted-by:` / `Co-Authored-By:` trailers. - [x] The SPIR-V headers regenerated with glslang (a shader changed). - [x] A game-free test added for the fix, failing on `main` and passing here. - [x] A check under `tests/` that reads changed source updated in this PR, not weakened.
fix(vk): a picture larger than the window is filtered over all its pixels, not sampled
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request_target) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 3s
f057a4e428
An Output resolution larger than the window (3840x2160 in a 2560x1440 window, 5120x2880 on a 4K screen) is drawn
bigger, then scaled down to the window. The window pass read its Catmull-Rom or Lanczos 2 over 4 x 4 of the picture's
pixels whatever the shrink, so it skipped most of them, and detail finer than the window can show came out as shimmer
and moire: worse than the plain linear copy. With the filter Off and Sharpness 0 the picture took that copy, a blit
whose linear filter reads 2 x 2 pixels, which aliases too.

Shrinking, each filter is now stretched by the picture's pixels per window pixel (up to 4), and the pass runs whenever
the picture is larger than the window, Off as a stretched linear (tent) filter. A window as large as the picture, or
larger, is filtered as before.

Assisted-by: Claude Opus 5.5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Amateurwizrd changed title from WIP: fix(vk): a picture larger than the window is filtered over all its pixels, not sampled to fix(vk): a picture larger than the window is filtered over all its pixels, not sampled 2026-11-13 21:09:28 +00:00
HumanBean left a comment

Code review of f057a4e4 (read only, nothing built or run here).

The change is sound.

  • The tap range at stretch 1 is the old b-1 .. b+2, and filter Off with no shrink still takes the hardware bilinear, so a picture no larger than the window is filtered as on main.
  • The side-3 small copy doesn't go through filtered(), so the menu sides are unaffected.
  • s is picture over dst, and uv spans all of dst, so the stretch is right with ultrawide sides too.
  • The SPIR-V headers: both name sha256 29ada605…, and that matches fg_scale.comp at f057a4e4 (LF, as the test hashes it). Their bytes come from glslang 1116.2.0, older than main's 1116.6.0, so the test doesn't compare bytes against a different glslang. That's expected, noted only.

What has to change before merge (checklist, CONTRIBUTING.md "delete what stopped applying", AGENTS.md):

  1. No docs/tested-configurations.md rows. The 12 measured legs (3 rounds x 2 filters x main/PR, RTX 5070 Ti) need rows, and the If applicable box for them is missing from the checklist.
  2. The Linux box was deleted, but it applies. vk_framegen.cpp and the two *_spv.h are host code (pr status hints the same). Keep the box: tick it if it built on Linux, or leave it unticked with the reason.
  3. CI's runs for f057a4e4 are still pending (run 2629). The CI checks and the Description check need to pass first.

A suggestion (inline, not blocking): with Sharpness above 0, RCAS's neighbours are still single bilinear reads of the raw picture, so part of the aliasing comes back through the sharpen when shrinking.

v3.0.0: yes, once 1 and 2 are fixed (description and docs only, no code change).

  • It only changes output when the picture is larger than the window. Players hit that windowed, since a new install starts at the screen's size.
  • The cost is measured (+0.3 ms GPU with Lanczos at 2x, frame time unchanged).
  • Risk: Native HDR's 10-bit pass and frame generation while shrinking are untested. T-Z9VV (AMD) is open with no result yet. Neither blocks the release, given the small reach.
Code review of f057a4e4 (read only, nothing built or run here). **The change is sound.** - The tap range at stretch 1 is the old `b-1 .. b+2`, and filter Off with no shrink still takes the hardware bilinear, so a picture no larger than the window is filtered as on main. - The side-3 small copy doesn't go through `filtered()`, so the menu sides are unaffected. - `s` is picture over `dst`, and `uv` spans all of `dst`, so the stretch is right with ultrawide sides too. - The SPIR-V headers: both name sha256 `29ada605…`, and that matches `fg_scale.comp` at f057a4e4 (LF, as the test hashes it). Their bytes come from glslang 1116.2.0, older than main's 1116.6.0, so the test doesn't compare bytes against a different glslang. That's expected, noted only. What has to change before merge (checklist, CONTRIBUTING.md "delete what stopped applying", AGENTS.md): 1. **No `docs/tested-configurations.md` rows.** The 12 measured legs (3 rounds x 2 filters x main/PR, RTX 5070 Ti) need rows, and the *If applicable* box for them is missing from the checklist. 2. **The Linux box was deleted, but it applies.** `vk_framegen.cpp` and the two `*_spv.h` are host code (`pr status` hints the same). Keep the box: tick it if it built on Linux, or leave it unticked with the reason. 3. CI's runs for f057a4e4 are still **pending** (run 2629). The CI checks and the Description check need to pass first. A suggestion (inline, not blocking): with Sharpness above 0, RCAS's neighbours are still single bilinear reads of the raw picture, so part of the aliasing comes back through the sharpen when shrinking. **v3.0.0: yes, once 1 and 2 are fixed** (description and docs only, no code change). - It only changes output when the picture is larger than the window. Players hit that windowed, since a new install starts at the screen's size. - The cost is measured (+0.3 ms GPU with Lanczos at 2x, frame time unchanged). - Risk: Native HDR's 10-bit pass and frame generation while shrinking are untested. T-Z9VV (AMD) is open with no result yet. Neither blocks the release, given the small reach.
@ -35,3 +45,3 @@
vec3 filtered(vec2 uv) {
if (pc.mode == 0) return textureLod(src, uv, 0.0).rgb;
const vec2 size = vec2(textureSize(src, 0));
const vec2 s = clamp(size / vec2(imageSize(dst)), vec2(1.0), vec2(kMaxStretch));
Owner

Suggestion, not blocking. Shrinking, c is now properly filtered, but main()'s sharpen still reads its four neighbours with single textureLod bilinear taps of the raw picture, one window pixel apart (e = max(1/size, 1/d)). At 2x those are 2 x 2 reads of a 4 x 4 footprint, so with Sharpness > 0 (every preset) the RCAS lobe adds back part of the aliasing this PR removes. The Lanczos/Sharpness 50 legs you measured are that case. Two options: take the neighbours through filtered() too (four more filter evaluations, so cost is a concern at 4x), or compare against a game-free check like shrink_alias with the sharpen applied.

Suggestion, not blocking. Shrinking, `c` is now properly filtered, but `main()`'s sharpen still reads its four neighbours with single `textureLod` bilinear taps of the raw picture, one window pixel apart (`e = max(1/size, 1/d)`). At 2x those are 2 x 2 reads of a 4 x 4 footprint, so with Sharpness > 0 (every preset) the RCAS lobe adds back part of the aliasing this PR removes. The Lanczos/Sharpness 50 legs you measured are that case. Two options: take the neighbours through `filtered()` too (four more filter evaluations, so cost is a concern at 4x), or compare against a game-free check like `shrink_alias` with the sharpen applied.
@ -599,1 +598,4 @@
if (!source.w || !source.h) return VK_NULL_HANDLE;
// (a picture larger than the window takes the pass even with the filter Off: the blit's linear filter reads 2 x 2 of the
// picture whatever the shrink, and fine detail shimmered)
const bool shrink = source.w > dw || source.h > dh;
Owner

This brings Native HDR (pq) with the filter Off into fg_scale_pq whenever the picture is larger than the window. Before, that path went to framegen_hdr with the unscaled view, or the blit. That's a real change of route for HDR players in a window, and Testing lists it as untested. Not blocking, but worth one look in Native HDR before the release, or a line in Risk.

This brings Native HDR (`pq`) with the filter Off into `fg_scale_pq` whenever the picture is larger than the window. Before, that path went to `framegen_hdr` with the unscaled view, or the blit. That's a real change of route for HDR players in a window, and Testing lists it as untested. Not blocking, but worth one look in Native HDR before the release, or a line in Risk.
HumanBean deleted branch fix/downscale-filter 2026-11-14 01:41:32 +00:00
Member

A follow-up found while checking another change against this one: with every window effect off, an oversized picture still takes the old path, so the case this pull request names ("With the filter Off and Sharpness 0 the picture took blit_crop_to_rect's vkCmdBlitImage(VK_FILTER_LINEAR)") is not reached by the new downscale in a plain window.

Where: on main cf605c85, gpu/vk/vk_replay.cpp line 3969 asks for the sampled view only when opts.upscale || opts.sharpness || dev.hdr || wide_sides_possible(). Shrinking alone is not in that condition. Without a view, line 4616 does not call framegen_scale, and the frame goes to blit_crop_to_rect at line 4630 as before.

Checked in a real window on Windows 11 with Intel Arc (driver 32.0.101.9175), native build of main cf605c85, SDR, frame generation off, sharpness 0, the still title menu, with RX_VK_PRESENT_LOG=1. The only difference between the two runs of each pair is the Window scaling filter (Off against Catmull-Rom):

Picture into window Filter Off Catmull-Rom
2560x1440 into 1280x720 route=direct view=0 route=scaled view=1
3840x2160 into 1280x720 route=direct view=0 route=scaled view=1

At exactly 2:1 the two pictures look almost alike in 1:1 crops, which fits: a 2 x 2 linear read covers every source pixel at that ratio. The pictures in this pull request are also 2:1 (5120x2880 into 2560x1440), so they could not show it. At 3:1 the all-off small text is thinner and harsher and the logo lettering coarser than through the scaled route:

3840x2160 into 1280x720, 1:1 crops of the real window: left with every effect off (direct route), right with Catmull-Rom (scaled route).

What this does and does not show: the route lines settle that the all-off case skips the new pass. The crops compare Off against Catmull-Rom, not Off through the scaled route, so they show the direct path's coarser sampling at 3:1 rather than how the new tent filter would look there. Still pictures only; nothing is claimed about shimmer in motion. Adding "the picture is larger than the destination" to the condition at line 3969 would send this case through framegen_scale as the description intends. Not checked: other GPUs, Linux in a real window, HDR, frame generation on.

A follow-up found while checking another change against this one: with every window effect off, an oversized picture still takes the old path, so the case this pull request names ("With the filter Off and Sharpness 0 the picture took `blit_crop_to_rect`'s `vkCmdBlitImage(VK_FILTER_LINEAR)`") is not reached by the new downscale in a plain window. **Where:** on main `cf605c85`, `gpu/vk/vk_replay.cpp` line 3969 asks for the sampled view only when `opts.upscale || opts.sharpness || dev.hdr || wide_sides_possible()`. Shrinking alone is not in that condition. Without a view, line 4616 does not call `framegen_scale`, and the frame goes to `blit_crop_to_rect` at line 4630 as before. **Checked in a real window** on Windows 11 with Intel Arc (driver 32.0.101.9175), native build of main `cf605c85`, SDR, frame generation off, sharpness 0, the still title menu, with `RX_VK_PRESENT_LOG=1`. The only difference between the two runs of each pair is the Window scaling filter (Off against Catmull-Rom): | Picture into window | Filter Off | Catmull-Rom | |---|---|---| | 2560x1440 into 1280x720 | `route=direct view=0` | `route=scaled view=1` | | 3840x2160 into 1280x720 | `route=direct view=0` | `route=scaled view=1` | At exactly 2:1 the two pictures look almost alike in 1:1 crops, which fits: a 2 x 2 linear read covers every source pixel at that ratio. The pictures in this pull request are also 2:1 (5120x2880 into 2560x1440), so they could not show it. At 3:1 the all-off small text is thinner and harsher and the logo lettering coarser than through the scaled route: ![3840x2160 into 1280x720, 1:1 crops of the real window: left with every effect off (direct route), right with Catmull-Rom (scaled route).](https://git.magicbean.dev/attachments/e43359bb-4718-4c57-a40d-18fd53b89346) **What this does and does not show:** the route lines settle that the all-off case skips the new pass. The crops compare Off against Catmull-Rom, not Off through the scaled route, so they show the direct path's coarser sampling at 3:1 rather than how the new tent filter would look there. Still pictures only; nothing is claimed about shimmer in motion. Adding "the picture is larger than the destination" to the condition at line 3969 would send this case through `framegen_scale` as the description intends. Not checked: other GPUs, Linux in a real window, HDR, frame generation on.
Some checks are pending
CI / Conventional Commit title (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 1/2) (pull_request) Blocked by required conditions
CI / Python source checks (ubuntu-24.04, part 2/2) (pull_request) Blocked by required conditions
CI / SPIR-V fixtures (global narrow load/store, direct surface atomics, F2I immediate) (pull_request) Blocked by required conditions
CI / Workflow lint (actionlint) (pull_request) Blocked by required conditions
Third-party parts / As their notes say (pull_request) Blocked by required conditions
Third-party parts / The same as upstream (pull_request) Blocked by required conditions
Website / Website on Cloudflare (pull_request_target) Has been skipped
PR description / Description follows the template (pull_request_target) Successful in 3s
Some workflows are waiting to be reviewed.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
MagicBeanDev/Longshot!307
No description provided.