Repository navigation
Conversation
|
Tested on RX 6700 XT (RDNA2) at 4K/50% scale: 28-33 fps with the EASU+RCAS active (also running RX_VK_ASYNC=1 + RX_VK_BATCH=1 + RX_VK_PRESENT_QUEUE=1). The upscaler costs ~0.5ms GPU (measured 16.1ms/frame with EASU vs 15.5ms with bilinear). Visually: foliage edges and distant detail are noticeably sharper than bilinear. No artifacts observed. A/B revert via |
hckrman101
left a comment
There was a problem hiding this comment.
Thanks, this is a nice upgrade over the plain linear stand-in. I checked the EASU and RCAS math against ffx_fsr1.h and it matches: the tap offsets, the easu_set weights, lob/clp, the zero-direction case, the min4/max4 de-ring clamp, and RCAS's 0.1875 limit with exp2(-stops) sharpness. The pipeline wiring looks right to me. All 4 bindings are pushed before both dispatches, the 24-byte push range covers both shaders and still fits the old 12-byte block, the EASU dispatch bumps the work epoch so the full_barrier between the passes doesn't get deduped, and mid gets resized and retired the same way hist does.
I'm leaving this as a comment rather than an approval because of the rebase. A couple of things need deciding there:
- Conflict with #1115 (merged today). Main's
taau_upscale.compnow runs FXAA in the gameplay stand-in, on by default.pc.validis now a bitfield: 1 = history bound, 2 = FXAA, 4 = debug colours, andtyped_upscaleORs FXAA's bits intosize[2]. Once you rebase, the RCAS checkpc.valid != 0would turn the history clamp on every frame, including the first frame after a resize whenhistis still undefined (see inline). You also need to decide how EASU and FXAA relate. EASU+RCAS keeps edges sharp but doesn't remove stair-steps, which is the shimmer #1115 was fixing. Options: make EASU the default with a note thatRX_VK_TAAU_MODE=bilinearbrings back the FXAA path, run FXAA before EASU, or keep FXAA as the default and make EASU opt-in. Whichever you pick,RX_VK_TAAU_FXAAshould still mean something, andtests/test_taau_fxaa.pyhas to keep passing. tests/test_taau_env_lookup.pywill break. It's registered in run_all.py. It pullstaau_stand_inout of the real vk_draw.cpp and compiles it against spies: an 8-argtyped_upscale, and atexture_view_ofthat only acceptsViewUse::Storage. The fixture has noview_type,ViewUse::Sample,VK_FORMAT_UNDEFINED,Desc::typeorTexture::width, so with this PR it won't compile. And even with those stubs added, the input lookup would return null and every accepted case would fail. Please update the fixture as part of this PR.
How this fits with #1008 and the other FSR PRs: I don't see a runtime clash. #1008's temporal_stand_in (and #1386's fsr3_stand_in) run before taau_stand_in in the dispatch path and take the network whenever their engine is selected and can run. This PR only changes what runs when the Upscaler row is on Game, or when the engine can't run on cards that leave the network out. So FSR 1 is the fallback underneath FSR 4/3.1, not a rival to them. Where they will collide is in vk_draw.cpp text: #1008 edits the lines right around typed_upscale/taau_stand_in, so whichever lands second has a small rebase. Naming-wise, it's good that this stays env-only and doesn't add an "FSR" choice to the Upscaler row, because #1008's "FSR" means FSR 4. #1386 also vendors ffx_fsr1.h. If that lands first, it might be worth including from there instead of keeping a hand port, but that's not a blocker.
No settings option ids are added here, so nothing collides. CI reports no checks on this branch.
| const vec3 lobeRGB = max(-hitMin, hitMax); | ||
| const float lobe = max(-0.1875, min(max(lobeRGB.r, max(lobeRGB.g, lobeRGB.b)), 0.0)) * pc.sharp; | ||
| vec3 c = (lobe * (b + d + h + f) + e) / (4.0 * lobe + 1.0); | ||
| if (pc.valid != 0) { // taau_upscale.comp's bound, unchanged |
There was a problem hiding this comment.
After rebasing onto #1115, size[2] carries FXAA's bits (2, or 6 with RX_VK_TAAU_FXAA=2) on the temporal call, so pc.valid != 0 would be true every frame. That turns the clamp on even though RX_VK_TAAU_CLAMP is off by default, and on the first frame after a resize it clamps against a hist that was never written. This should be (pc.valid & 1) != 0, the same as main's taau_upscale.comp.
There was a problem hiding this comment.
Thanks for the thorough review — all four addressed in 7ce5a07 (pushed to fsr1-easu-rcas, on top of the two earlier fixups):
-
Clamp bit:
taau_rcas.compnow tests(pc.valid & 1) != 0, same as main'staau_upscale.compafter feat(vk): FXAA in the gameplay TAAU stand-in (edges on cards without the network) #1115 — FXAA's bits (2, or 6 withRX_VK_TAAU_FXAA=2) can no longer turn the bound on, so no clamp against an unwrittenhiston the first frame after a resize. -
FXAA: documented as "FSR replaces FXAA", per your either/or — the comment at the mode decision in
vk_draw.cppnow saysRX_VK_TAAU_FXAAbelongs to the bilinear stand-in only (EASU's edge-adaptive kernel does the edge work it was added for), and the readme's switches section says the same: the two never run together,RX_VK_TAAU_MODE=bilineargets the FXAA picture back. -
Fixture:
tests/test_taau_env_lookup.pyextended to the current shape —ViewUse::Sample+VK_FORMAT_UNDEFINEDaccepted by thetexture_view_ofspy, aview_typestub,Texture::width/height, a 10-argtyped_upscalespy that checks the forwarded source size (width >> level), and a new no-resolved-input rejection case. All seven configurations pass. On the behaviour note: I kept the return-false (network runs) when the input can't be resolved rather than falling back to bilinear — it only happens when the source texture fails to resolve, in which case the blit fallback inupscale_stubwouldn't have the size either. Happy to switch it if you'd rather. -
Colour space: done as you suggested — the
c * cdecode is gone from EASU's loads, and RCAS squares once at its output, before the history bound. EASU and RCAS now run on the sqrt-encoded input (the space FSR 1's weights were tuned for, RCAS's1 - mx4in [0,1]), andmid's B10G11R11 keeps more of the darks. SPV headers regenerated.
There was a problem hiding this comment.
Thanks, all four look good. Keeping return-false when the input can't be resolved is fine with me: as you say, the blit fallback wouldn't have the size either.
| if (!src_w || !src_h) return false; | ||
| // (FSR 1's EASU + RCAS in the stand-in's place (shaders/taau_easu.comp, taau_rcas.comp); RX_VK_TAAU_MODE=bilinear: | ||
| // the old one-tap stand-in, for an A/B. RX_VK_TAAU_SHARPNESS=<stops> (0 the sharpest, 2 soft; default 0.2)) | ||
| static const bool bilinear = getenv("RX_VK_TAAU_MODE") && !strcmp(getenv("RX_VK_TAAU_MODE"), "bilinear"); |
There was a problem hiding this comment.
This is where the decision against #1115's FXAA goes (see the review body). As written after a rebase, the FSR path would silently drop FXAA, and RX_VK_TAAU_FXAA would only do anything when RX_VK_TAAU_MODE=bilinear is set. If that's the plan, please say so in the comment here and in the readme/env docs.
| Texture* in_tex = nullptr; | ||
| uint32_t in_level = 0; | ||
| if (in_handle) texture_view_of(in_handle, view_type(td.type), ViewUse::Sample, VK_FORMAT_UNDEFINED, &in_tex, &in_level); | ||
| if (!in_tex) return false; |
There was a problem hiding this comment.
tests/test_taau_env_lookup.py compiles this function out of vk_draw.cpp against spies. With the extra texture_view_of(..., ViewUse::Sample, ...) call and the two new typed_upscale args, the fixture won't build, and its texture_view_of spy also rejects anything that isn't Storage. Please extend the fixture: add view_type, ViewUse::Sample, Texture::width/height, and a 10-arg spy that checks the forwarded source size.
Also a small behaviour change: if the input texture can't be resolved, the stand-in now returns false and the network runs, where before it would still do the linear upscale. That's probably fine, but you could fall back to the bilinear pipeline instead of giving up.
| vec3 ld(ivec2 q) { // the source texel, squared (linear), clamped to the image | ||
| q = clamp(q, ivec2(0), pc.src_size - 1); | ||
| const vec3 c = texelFetch(src, q, 0).rgb; | ||
| return c * c; |
There was a problem hiding this comment.
Nit / suggestion: FSR 1 expects EASU and RCAS to run on perceptually encoded input, and the input is already sqrt-encoded. If you filter on c as-is and square once at the end of RCAS (before the history clamp), the edge detection and luma weights work in the space they were tuned for. B10G11R11 also keeps more precision in the darks for mid that way. This matters for RCAS too: its 1 - mx4 term assumes [0,1], so any pixel near a highlight above 1.0 in linear gets no sharpening at all (lobe clamps to 0). That's harmless, just worth knowing.
…olour space) Review on PR NotRexed#1439 (hckrman101), all four addressed: - taau_rcas.comp: the history bound tests (pc.valid & 1) only, as main's taau_upscale.comp after PR NotRexed#1115; the higher bits carry FXAA there and must never turn the bound on (first frame after a resize clamped against an unwritten hist). - vk_draw.cpp, readme: the FSR path replaces NotRexed#1115's FXAA, documented at the mode decision and in the readme's switches: RX_VK_TAAU_FXAA applies only with RX_VK_TAAU_MODE=bilinear; EASU's kernel does the edge work otherwise. - taau_easu/taau_rcas.comp: the sqrt decoding moved out of EASU's loads to a single square at RCAS's output (before the bound): FSR 1 runs on the perceptual encoding, its edge detection, luma weights and RCAS's 1 - mx4 term ([0,1]) now work in the space they were tuned for, and mid's B10G11R11 keeps more of the darks. spv headers regenerated (glslang 16.6.0). - tests/test_taau_env_lookup.py: the fixture's spies match the stand-in's current shape: ViewUse::Sample and VK_FORMAT_UNDEFINED in the spy, a view_type stub, Texture width/height, a 10-arg typed_upscale spy that checks the forwarded source size, and a no-resolved-input rejection case.
hckrman101
left a comment
There was a problem hiding this comment.
Thanks for the quick turnaround. All four points from last time are handled in 7ce5a07:
- RCAS now tests
(pc.valid & 1) != 0, so FXAA's bits can't turn the clamp on. - Making FSR the default and keeping FXAA for
RX_VK_TAAU_MODE=bilinearis a fine call, and the comment and readme spell it out. - The
test_taau_env_lookup.pyfixture now matches the new shape: it has the Sample view, source size forwarding, and the new no-input case. Main's copy of that test hasn't changed since your base, so this should carry over cleanly. - EASU/RCAS now filter the sqrt-encoded input and square once before the bound. That looks right.
Two things still stop this from being mergeable:
- Two unrelated commits are on the branch now. 26b9466 ("a readback's wait flushes the held batch", vk_replay.cpp) and 8fb0290 ("graceful retired-pool eviction", vk_memory.cpp) aren't about the upscaler, and they look like they were pushed to the wrong branch. The vk_memory one changes default behaviour for everyone on Windows: retired pools used to be kept on Intel/AMD and are now given back after 30s. That keep-by-default was there because of device losses on Arc (issue 17), the Ally X and the RX 6600. The vk_replay one adds a batch flush from
wait_recorded. Both may well be good changes, but they need their own PRs with their own testing. Please drop them from this one (see inline). - The branch is still on an old main (about 490 commits behind) and conflicts. The fixes above are written against #1115, but #1115 isn't on the branch. Head's
taau_upscale.compandtyped_upscalehave no FXAA yet, so the actual merge with main'styped_upscale(FXAA'ssize[2] |=bits, theRX_VK_TAAU_FXAAgetenv) and with #1008's edits aroundtaau_stand_inis still to be done. Please merge or rebase on main. After that,tests/test_taau_fxaa.pyneeds theif (temporal && fxaa) size[2] |= ...line and theRX_VK_TAAU_FXAAgetenv to stay intyped_upscale. That's fine with your design: the bilinear path keeps using them, and RCAS ignores bit 2. The new comment saying the flag "is read by nothing here" will need adjusting once that getenv sits in the same function.
On #1008 and #1386: no clash. FSR 1 is spatial, and the TemporalEngine interface in vk_temporal.cpp is for engines that use jitter, motion vectors and the actor-motion pass. So it's right for this to stay the fallback under fsr3_stand_in/temporal_stand_in and not become another engine. It doesn't use the dlss option values (3/4/5 = FSR 3.1/4/4 AA) or add any settings ids, and RX_VK_TAAU_MODE/RX_VK_TAAU_SHARPNESS don't exist on main. Since #1386 is merged and vendors ffx_fsr1.h, you could include from there someday, but that's not a blocker.
CI reports no checks on this branch.
| // 85) the wait shortens to RX_VK_RETIRE_MIN_SECS seconds (default 2), down to RX_VK_IMPORT_LOW percent (75), and | ||
| // the free itself still waits for the GPU (dead_imports). RX_VK_KEEP_RETIRED=1 keeps them for the session; =0 | ||
| // gives them back as soon as the GPU is past, both as before. The vendor split is gone: every vendor leaked by it | ||
| static const bool keep = getenv("RX_VK_KEEP_RETIRED") && getenv("RX_VK_KEEP_RETIRED")[0] != '0'; |
There was a problem hiding this comment.
This (and the rest of 8fb0290) doesn't belong in the FSR 1 PR. It removes the Windows keep-by-default for non-NVIDIA cards, which was added after the Arc B390 / Ally X / RX 6600 device losses. A time-based give-back may be the right fix, but it needs its own PR and testing on those vendors. Please drop it from this branch.
There was a problem hiding this comment.
Resolved: this is out of the branch now and lives in #1906.
| // set for another's flush, which queue_flush_locked's empty return would do) | ||
| { | ||
| std::lock_guard lk(dev.present_mu); | ||
| if (seq > rec_out && !pend.cbs.empty()) flush_reason = FR_READBACK, queue_flush_locked(); |
There was a problem hiding this comment.
Same here: 26b9466 (the readback-triggered batch flush) is unrelated to the upscaler. Please move it to its own PR so it can be reviewed and A/B'd on its own.
There was a problem hiding this comment.
Resolved: dropped from this branch, and it's now #1905.
| if (!src_w || !src_h) return false; | ||
| // (FSR 1's EASU + RCAS in the stand-in's place (shaders/taau_easu.comp, taau_rcas.comp); RX_VK_TAAU_MODE=bilinear: | ||
| // the old one-tap stand-in, for an A/B. RX_VK_TAAU_SHARPNESS=<stops> (0 the sharpest, 2 soft; default 0.2). | ||
| // RX_VK_TAAU_FXAA (PR #1115) belongs to the bilinear stand-in only: the FSR path replaces it, its EASU kernel |
There was a problem hiding this comment.
After merging main, typed_upscale will contain the RX_VK_TAAU_FXAA getenv and the if (temporal && fxaa) size[2] |= ... line, and test_taau_fxaa.py requires both. So "read by nothing here" won't be accurate. Something like "only the bilinear pipeline acts on it; RCAS tests bit 0 alone" would fit.
…olour space) Review on PR NotRexed#1439 (hckrman101), all four addressed: - taau_rcas.comp: the history bound tests (pc.valid & 1) only, as main's taau_upscale.comp after PR NotRexed#1115; the higher bits carry FXAA there and must never turn the bound on (first frame after a resize clamped against an unwritten hist). - vk_draw.cpp, readme: the FSR path replaces NotRexed#1115's FXAA, documented at the mode decision and in the readme's switches: RX_VK_TAAU_FXAA applies only with RX_VK_TAAU_MODE=bilinear; EASU's kernel does the edge work otherwise. - taau_easu/taau_rcas.comp: the sqrt decoding moved out of EASU's loads to a single square at RCAS's output (before the bound): FSR 1 runs on the perceptual encoding, its edge detection, luma weights and RCAS's 1 - mx4 term ([0,1]) now work in the space they were tuned for, and mid's B10G11R11 keeps more of the darks. spv headers regenerated (glslang 16.6.0). - tests/test_taau_env_lookup.py: the fixture's spies match the stand-in's current shape: ViewUse::Sample and VK_FORMAT_UNDEFINED in the spy, a view_type stub, Texture width/height, a 10-arg typed_upscale spy that checks the forwarded source size, and a no-resolved-input rejection case.
7ce5a07 to
b1009cc
Compare
|
Thanks for the thorough re-review! All four fixes are verified — I've addressed the two merge blockers:
The forced update is already on |
|
Heads-up: this conflicts with main now (a lot landed today). Could you merge main into the branch (or rebase) and resolve the conflicts? Thanks! |
7f70b92 to
b1009cc
Compare
hckrman101
left a comment
There was a problem hiding this comment.
Thanks for splitting the branch. The two unrelated commits are gone: the PR is now only the two EASU commits, and the readback flush and the retired-pool change are in #1905 and #1906. I checked the PR's files at b1009cc against 7ce5a07, and they're identical, so the four fixes from the earlier rounds are all still in.
The rebase isn't on the branch yet, though. Your comment says it's rebased onto v2.0.14, but fsr1-easu-rcas is still at b1009cc on top of 581e83f. That's 921 commits behind main, and GitHub reports it as conflicting. Maybe the rebased branch didn't get pushed? A trial three-way merge of the PR's files against current main gives:
- vk_draw.cpp, 3 conflicts. (a) The
typed_upscaleforward declaration: main addedtemporal_rejected_networknext to it. (b) The generic RGB9E5 fallback inupscale_stub: main now checksinput_view && output_viewbefore callingtyped_upscale. Please keep that check alongside the two new size args. (c) The dispatch block at the end oftyped_upscale: this is the FXAA merge from last time. Main hashist_view/hist_framealiases, theRX_VK_TAAU_FXAAgetenv andif (temporal && fxaa) size[2] |= .... The bilinear branch needs to keep those fortests/test_taau_fxaa.py. The "read by nothing here" comment also needs adjusting, as in the open inline thread. - readme.md, 1 conflict. It's next to the new
RX_VK_WB_MAX_KBparagraph and the link to the development guide's switches section. Easy to resolve. Since main moved the test and debug switches todocs/development.md, the TAAU switches could go there too, but that's up to you. - New on main:
tests/test_upscale_fallback.pywill break. It cutsupscale_stubout of vk_draw.cpp and compiles it against an 8-argtyped_upscalespy that asserts!temporal. After the merge, the RGB9E5 fallback calls it withsrc->width, src->heightas well, so the spy needs the two extra parameters. It could also check that they're 64x32, which is what itsTexturestub already has. Please update it in the same PR, the same way you didtest_taau_env_lookup.py. tests/test_taau_env_lookup.pymerges cleanly. Main only addedRUN_ALL_ARGS = ["--cxx", "g++"].
So: the scope is fixed, and the rest is the merge with main plus that one extra fixture. Once that's pushed and python -I -B tests/run_all.py passes, I'm happy to look again. CI reports no checks on this branch.
…olour space) Review on PR NotRexed#1439 (hckrman101), all four addressed: - taau_rcas.comp: the history bound tests (pc.valid & 1) only, as main's taau_upscale.comp after PR NotRexed#1115; the higher bits carry FXAA there and must never turn the bound on (first frame after a resize clamped against an unwritten hist). - vk_draw.cpp, readme: the FSR path replaces NotRexed#1115's FXAA, documented at the mode decision and in the readme's switches: RX_VK_TAAU_FXAA applies only with RX_VK_TAAU_MODE=bilinear; EASU's kernel does the edge work otherwise. - taau_easu/taau_rcas.comp: the sqrt decoding moved out of EASU's loads to a single square at RCAS's output (before the bound): FSR 1 runs on the perceptual encoding, its edge detection, luma weights and RCAS's 1 - mx4 term ([0,1]) now work in the space they were tuned for, and mid's B10G11R11 keeps more of the darks. spv headers regenerated (glslang 16.6.0). - tests/test_taau_env_lookup.py: the fixture's spies match the stand-in's current shape: ViewUse::Sample and VK_FORMAT_UNDEFINED in the spy, a view_type stub, Texture width/height, a 10-arg typed_upscale spy that checks the forwarded source size, and a no-resolved-input rejection case.
b1009cc to
c3728dc
Compare
|
Rebased onto current main (4d68bc8) per review, as commit c3728dc. The readme change applied clean; gpu/vk/vk_draw.cpp needed conflict resolution, as on the v2.1.0-rc.3 rebase:
Update: the two Python test suites pass against the rebased tree with the llvm-mingw toolchain — test_upscale_fallback.py (all 23 cases) and test_taau_env_lookup.py. |
Joey-Magnus
left a comment
There was a problem hiding this comment.
The c3728dc rebase is good: gpu/vk/vk_draw.cpp and readme.md merge cleanly with current main. The only conflict left is tests/test_taau_env_lookup.py, where #2042 rewrote the file to include hookshot_env.h and wrap env_on. Keep main's version and add your src_w/src_h spy changes on top. At the PR head, test_taau_fxaa, test_taau_env_lookup and test_upscale_fallback pass locally.
The blocker is CI. The title "gpu/vk: FSR 1 ..." fails the Conventional Commit check, so only that check has run, and the Windows and llvm-mingw builds of the new shaders have never been run. Something like feat(vk): FSR 1 EASU + RCAS in place of the bilinear TAAU stand-in would pass.
One thing to note before merge: taau_key picks the stand-in wherever mma_software is set (vk_coopmat.cpp). That covers AMD, Intel, the GTX 16xx cards and the Steam Deck, so FSR 1 replaces FXAA by default on every one of them. You measured 0.5 ms on a 6700 XT. A timing on the Deck or on an Intel iGPU would show the default is safe at the low end.
|
This now conflicts with main in |
The bilinear stand-in on AMD loses all edge detail at 50% internal scale. This replaces it with FSR 1's EASU (12-tap edge-adaptive) and RCAS (5-tap contrast-capped sharpening) as two GLSL compute shaders wired into the existing taau_stand_in/typed_upscale interception. - gpu/vk/shaders/taau_easu.comp: EASU ported from the WGSL in rx_present_fx.cpp, 8x8 workgroups, reads RGB9E5, writes B10G11R11 - gpu/vk/shaders/taau_rcas.comp: RCAS with the stand-in's flash/NaN history clamp preserved - gpu/vk/vk_draw.cpp: typed_upscale builds three pipelines (sampler/ mid/dst/hist), EASU then RCAS dispatch with full_barrier between - Escape hatches: RX_VK_TAAU_MODE=bilinear (old path), RX_VK_TAAU_SHARPNESS=<stops> (default 0.2) - Measured on RX 6700 XT: ~0.5ms GPU cost, visually sharper than bilinear at the same resolution Co-authored-by: GLM-5.3 <noreply@z.ai>
…olour space) Review on PR NotRexed#1439 (hckrman101), all four addressed: - taau_rcas.comp: the history bound tests (pc.valid & 1) only, as main's taau_upscale.comp after PR NotRexed#1115; the higher bits carry FXAA there and must never turn the bound on (first frame after a resize clamped against an unwritten hist). - vk_draw.cpp, readme: the FSR path replaces NotRexed#1115's FXAA, documented at the mode decision and in the readme's switches: RX_VK_TAAU_FXAA applies only with RX_VK_TAAU_MODE=bilinear; EASU's kernel does the edge work otherwise. - taau_easu/taau_rcas.comp: the sqrt decoding moved out of EASU's loads to a single square at RCAS's output (before the bound): FSR 1 runs on the perceptual encoding, its edge detection, luma weights and RCAS's 1 - mx4 term ([0,1]) now work in the space they were tuned for, and mid's B10G11R11 keeps more of the darks. spv headers regenerated (glslang 16.6.0). - tests/test_taau_env_lookup.py: the fixture's spies match the stand-in's current shape: ViewUse::Sample and VK_FORMAT_UNDEFINED in the spy, a view_type stub, Texture width/height, a 10-arg typed_upscale spy that checks the forwarded source size, and a no-resolved-input rejection case.
c3728dc to
4876761
Compare
|
Rebased onto current main (ef81aee) as asked; the head is now 4876761, still the same two commits. Conflict resolved: only
Tests at 4876761, llvm-mingw clang++ (x86_64-w64-mingw32) on Windows:
Title: changed to Not done: the timing on a Steam Deck or an Intel iGPU. I only have the 6700 XT figure, so whether FSR 1 is a safe default at the low end (everywhere |
Joey-Magnus
left a comment
There was a problem hiding this comment.
Both points from my last review are done: the title passes, and 4876761 merges cleanly with current main. I merged it into main and ran tests/run_all.py: 426 passed, and the only failure is test_lift_cache, which fails on main here too. test_taau_env_lookup, test_taau_fxaa and test_upscale_fallback all pass. CI's builds still haven't run because the workflows are waiting for approval, so the llvm-mingw build of the two new shaders is still unchecked.
The point still open is the default. typed_upscale now runs EASU+RCAS on every mma_software card (AMD, Intel, the GTX 16xx cards and the Deck), and in the generic RGB9E5 fallback too. That replaces #1115's FXAA on the strength of one 6700 XT timing. A number from a Deck or an Intel iGPU would settle it. Otherwise, keep FSR 1 opt-in until someone has one.
Smaller things:
- If pipe_easu or pipe_rcas fails to build, typed_upscale returns false and the network runs. Falling back to pipe_linear would keep the old stand-in instead.
- No test compares taau_easu_comp_spv.h or taau_rcas_comp_spv.h with their sources, the way test_taau_fxaa does for taau_upscale.
What
On AMD, the game's TAAU neural upscaler is replaced by a bilinear stand-in (HMMA cost issue #52). This replaces that bilinear with FSR 1's EASU (12-tap edge-adaptive) + RCAS (contrast-capped sharpening) as two GLSL compute shaders.
Why
Bilinear at 50% internal scale loses all edge detail — foliage smears, distant geometry loses definition. EASU preserves edges and detail at the same resolution with ~0.5ms GPU cost (measured on RX 6700 XT). The EASU/RCAS shader code was already in the repo (inherited from TOTKPort in
gpu/render/rx_present_fx.cpp) — this ports it to GLSL compute and wires it into the existing stand-in site.Changes
gpu/vk/shaders/taau_easu.comp(new) — FSR 1 EASU, 8×8 workgroups, reads RGB9E5, writes B10G11R11gpu/vk/shaders/taau_rcas.comp(new) — RCAS with the stand-in's flash/NaN history clamp preservedgpu/vk/vk_draw.cpp—typed_upscalebuilds three pipelines off one push-descriptor layout; EASU → barrier → RCASRX_VK_TAAU_MODE=bilinear(old path),RX_VK_TAAU_SHARPNESS=<stops>(default 0.2)Testing
RX_VK_TAAU_MODE=bilinearNote
This is quality-only — it won't raise fps in CPU-bound scenes (the frame is guest-emulation-bound). But it gives the 50% Resolution row a proper upscaler instead of a plain blit, at essentially zero GPU cost.
AI assistance disclosure: the EASU/RCAS port and pipeline wiring were developed with AI assistance, verified and tested by a human on the affected hardware.