Compare commits

..

32 Commits

Author SHA1 Message Date
CamilleLaVey 01ca6071c5 Quick tests 2026-09-11 02:15:04 -04:00
CamilleLaVey bbae69f09a Remove fp16 toggle 2026-09-11 01:15:41 -04:00
CamilleLaVey d1867be6eb More changes to UI. 2026-09-11 00:23:01 -04:00
CamilleLaVey 8968534d75 Another intent to refine LSFG UI 2026-09-10 22:34:59 -04:00
xbzk f3af5d0c25 [fs] moved temp folder cleanup upstream (per-application level) to avoid per-program retrigger (#4375)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This issue was observed in MGS Master Collection Volume II, MGS4: Guns of the Patriots, Chapter 4: Shadow Moses

That title is a bundle, and that specific chapter is a program (MGS 1st stage), launched by a program (MGS4), launched by an application (main title bundle).

Upon decoding guest panic message, it was exposed that an abort was triggered after trying to create a save in nand/temp/gclvar.bak file.
Further instrumentation exposed the error on that file's path, caused from a previous deletion of that folder.
In the same session that folder had been already cleaned and created successfully, which evidences an undesired re-cleaning of the temp structure.

The issue was the fact that the MGS4 to MGS transition also calls EnsureSaveData>...>SaveDataFactory creator, and that was destroying the previous created temp structure. After that stage, the return from MSG to MSG4 was causing the same issue again.

By moving the deletion to the guest application level, the factory creation was called only once, as it should be, and the issue is gone.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4375
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
2026-09-10 22:30:07 +02:00
xbzk 00a1c392e7 [opus] proper limit and error code for opus decoder instances (#4384)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

This is intended to fix Hades II save loading issue.

By analyzing panic dump it was found out that the error code was 0x160A. By decoding firmware it was found that error comes from 'nn::codec::InitializeHardwareOpusDecoder'.

Further decoding revealed that SDK tries to allocate multiple opus instances, up to the fw limit of 24 instances, and when it receives return error ResultOutOfOpusDecoders (385), a software fallback is gracefully used.

Implementing the limitation and the limit reached error code return makes saves load properly, multiple times in a row in both windows and android.

I still wonder why the game allocates too many parallel instances, but the current implementation is a real fw compliant solution, and whether the 24+ is real or some leak may be investigated later, or not.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4384
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-10 22:29:35 +02:00
lizzie e7a96c7907 [hle] Firmware 23.0.0 (#4390)
Preparing when something inevitably requires *at least* this reporting of version/etc.

Authored-by: @Maufeat
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Co-authored-by: Maufeat <sahyno1996@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4390
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-10 22:28:39 +02:00
CamilleLaVey ed566919f4 [vulkan, android] Add feature of post-processing shaders on Android (#4348)
Very self-explanatory; implementation of the feature for post-processing shaders for Android (at least for now); will allow users to enhance the graphic quality of video games based on the use of multiple shaders adjustable, presets and more, inspired on the PPSSPP implementation, this feature adds 22 customizable shaders (1 pass) that lives on the overlay of the screen, which means that aside of the capability to chain multiple shaders on the screen, this doesn't have depths (pixel/ depth z-buffer) so it ensures the performance with it's use. Few shaders were a faithful port from PPSSPP with their respective attribution on the shader headers for their respective owners; and there are adaptations from public references/ cinematographic (Anime4K) and the rest are my own addition.

_Special Credits:_

1.- PPSSPP Team for their contribution on the public references for shaders: Henrik Rydgard, ShadX, SimoneT, KillaMaaki and guest(r).
2.- Niklas Haas.
3.- bloc97.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4348
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-10 15:14:02 +02:00
Exverge 5f142c7926 [common/core/dynarmic] Optimize page table allocations (#4219)
Reduces the page entries from 32 bytes to 8 and rewrites `VirtualBuffer` to be more efficient in memory usage and specifically for large zero regions.

The page table will now only reserve 1GiB instead of 4GiB and of this memory it should only use at most ~8MiB.

This PR has the side effect of using Eden on Windows on low memory systems much more plausible since it would previously require ~10GiB of committable memory at front (despite using ~5-6 at most, inadvertently stalling other processes) where as now it should only require around the amount that it'll actually use.

Co-authored-by: Lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4219
Reviewed-by: Maufeat <sahyno1996@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-09 21:35:49 +02:00
lizzie c5f6f1ca5e [common/host_memory] Remove throw on benign path (#4359)
The caller of GetFuncAddress already handles the case gracefully. Fixes crashes when the symbol aint present

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4359
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 13:18:43 +02:00
lizzie dcbcdf16ad [hle/ns] implement IReadOnlyApplicationControlDataInterface::ListApplicationIcon (#3770)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3770
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 13:02:02 +02:00
lizzie ed57836903 [ips_layer] remove trailing whitespace to fix hex parsing on TLTD mods (#4336)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

...another edge case, this time with
`00297f24 34008052 // 60FPS`
would get parsed as
`34008052 `
which isn't quite correct

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4336
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 13:01:40 +02:00
lizzie 20f9aa4cfe [qt] Update FAQ links (#3710)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3710
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: Ghost <>
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-09 11:57:40 +02:00
lizzie 6f6a8f6d67 [svc] don't truncate 64-bit SVC args on trace logs (#4374)
Small inconvenience with args

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4374
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 11:14:22 +02:00
lizzie 2f787ddb8c [dynarmic] Fix broken tests and SIGABRT with f16 inst gen (#4366)
F16 instructions aren't supported yet, thus disable them. Also compilation/build errors.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4366
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 11:14:03 +02:00
lizzie 23585820bc [hle] correct IClientProcess::RegisterClient, impl cmd 3100,3102 for ns:vm (#4376)
ldn had wrong signature
ns:vm had missing function stubs that would make some games assert on missing impl

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4376
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-09 11:13:42 +02:00
lizzie e5b8b10188 [hle] Fix fsp-pr not being found (#4379)
Should be `fsp-pr`, not `fsp:pr`

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4379
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 21:17:19 +02:00
lizzie 8a10109e24 [vulkan] Masquerade VkInfo as PUBGMobile + UE (#4320)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

PR I was talking about with @CamilleLaVey , please tell me if this is a stupid idea OR has a chance to work
applicationname is a wild guess

Co-authored-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4320
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 21:04:08 +02:00
CamilleLaVey a538cd9aff [shader_recompiler, vulkan] Virtual buffer pages with multi-range + sparse buffers binding (#4362)
Storage buffers can span GPU pages that aren't contiguous in host memory, but the buffer cache assumed one contiguous buffer per binding, so anything crossing a mapping boundary read the wrong bytes. Multi-range binding resolves the real segments and presents them to the shader as one buffer, aliasing their memory into a sparse VkBuffer or gathering them into a copy, all of this was mostly fixed by making a path to use sparse on buffers (the same way as texture cache has it) and retrieve properly the mapping ranges to the virtual pages, including the actual structure on the use of binding sparse.

Meanwhile this fixes Monster Hunter Sunbreak z-fighting, bad performance and mostly vertex explosions (coming from the contiguous buffer read from the shader's game) it's only the basis for a further investigation towards how are we gonna treat phi calculations, optimized them and mostly eradicate the currently excess of exposure on the textures where the lightning trace should not being reflected.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4362
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:42:20 +02:00
lizzie ce202292cf [gpu] Destroy GPU thread after all non-trivial members had been destroyed (#4368)
Should help fix any game hangs after closing the emulator, or sporadic hangs when restarting.

Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4368
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:40:19 +02:00
lizzie 87e5d0b6e4 [hle] VirtualBoy classics fixes, QLaunch IPC fixes, implement extra ams SVCInfo (#4343)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

`OpenSystemApplicationProxy`
`OpenSystemApplicationProxy`
ALSO yes those cmds DO NOT take an attribute
Implemented SVC infos to match Atmosphere's values
Implement more stub I2C session cmds
stub CMD2103 for rhythm groove
better error handling on svc get info to match ams (see ams svc info)
Virtual Boy classic fix adept from https://git.ryujinx.app/projects/Kenji-NX/commit/2288084b531b185f5840a696b23265014f07e2f2 by @Mythrax
acc:u1 was missing the *Deprecated cmd

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4343
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:39:55 +02:00
lizzie fdd8d4252c [common] remove unused vector_math.h fluff (#4333)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

We don't use half of this file for anything -- it's dead code
also the NEON intrinsic replacements suck -- we are not beating GLM (plus we use registers for decomposition instead of using vec regs like everyone else does, so the "optimizations" dont matter)

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4333
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:39:39 +02:00
PavelBARABANOV 1036982d2f partial revert 54cd5fb8eb (#4355)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
It fixes display issues when the overlay applet is enabled, thanks Bruno.

Fixes freezes when enabling real applet keyboard  in qlaunch.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4355
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: Maufeat <sahyno1996@gmail.com>
2026-09-08 20:39:08 +02:00
xbzk e27650cf42 [nce] avoid cyrilic utf8 strings being interpreted as exclusive store instructions (#4369)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Problem: In cyrilic idioms, Absolum won't past beyond 1st area (intro miniboss).

From the user panic i`ve extracted these strings:

 > GlobalState.request_next()
 > save_state_to_saveGame(...)
 > platform.ProcessSave()
 > bin_serialize_to_file(...)
 > StandaloneTypeModel.Write(SaveGame, ...)
 > Write(SaveGame.AdventureSaveSlot, ...)
 > Write(PlayerState, ...)
 > ProtoBuf.ProtoWriter.WriteString(...) + 0xcc
 > ProtoBuf.Helpers.DebugAssert(...)
 > Debug.Fail(...)

By debugging ProtoBuf.ProtoWriter.WriteString(...) argument it was found that the string Отлично! (Great! expression for combos between 6 and 30 iirc) was reaching a size comparison of different values and causing an assert in guest side..

It seems the game tries to save highest combo level. I've managed to skip this bug by doing a perfect combo for entire level, so that the problematic word never was generated or attempted to be saved.

After further research, the issue chain was identified: Game maths literal string size via two ways and compare, and one of them was getting replaced by a wrong value due to Eden letting the literal string be interpreted as a real instruction.

 Logic added:
        const bool pair = decltype(l)::ExtractValue(raw) & 1;
        const bool fixed_rt2 = decltype(rt2)::ExtractValue(raw) == 0b11111;
        return pair || fixed_rt2;

   - pair checks bit 21 (L & 1): register-pair instructions actually use Rt2, so they remain accepted.
   - For single-register forms, require Rt2 == 31. The table word fails this check, so NCE leaves it intact.

Sources
https://www.scs.stanford.edu/~zyedidia/arm64/stxrb.html
https://www.scs.stanford.edu/~zyedidia/arm64/stxp.html
https://github.com/qemu/qemu/blob/master/target/arm/tcg/a64.decode#L344

Altough the solution is a case specific guard, it is data agnostic: This is a true fix to avoid a specific case in which a literal may be interpreted as a valid instruction, without interfering with other true instruction cases.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4369
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-08 20:10:41 +02:00
xbzk ecb2ae4076 [settings] move program_args and debug_knobs back to debugging category and make them pergameable (#4378)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

I have moved debug_knobs and program_args to System category, to make them pergameable without hassle.
But the hassle came: They were automatically readded to Qt's System tab, doubling them.
If it were to face hassle either way, the right thing is to move them back to Debugging category, then make them exceptionally pergameable.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4378
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-08 20:05:56 +02:00
Eden CI 753b57a8c2 [dist, android] Update translations from Transifex for Sep 07 (#4373)
Automatic translation update for Sep 07

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4373
2026-09-07 21:44:20 +02:00
xbzk 74ccea3def [debug,android] expose extend logging setting on android (#4340)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------
Team asked (gido/cam/liz). No idea what for. Not tested, but should be good.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4340
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-07 08:56:51 +02:00
lizzie 11de264541 [cmake] Fix some macOS compilation errors (#4350)
Don't compile GLSL/GLASM backends without OpenGL enabled. These codepaths are unused on non-OpenGL platforms.

Fixes other miscellaneous errors that pop up on Clang 23 as well.

Signed-off-by: Lizzie lizzie@eden-emu.dev
Signed-off-by: crueter crueter@eden-emu.dev

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4350
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-05 23:43:46 +02:00
lizzie f6e7686038 [ci] Fix macOS "no type named 'free' in namespace 'std' (#4347)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4347
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-04 13:32:36 +02:00
xbzk 1dcc574591 [gdb] fix conn state to use same pipe (instead of a copy) (#4339)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

I was trying to use GDB stub to investigate MHR and it was not connecting.
Error was suggesting it was not available for connection although i saw it booting on logs.
Debugged and noticed signal_pipe_ was not the correct one, and followed the example of client_socket_.
GDB stub is now working,  on windows, at least.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4339
Reviewed-by: Lizzie and Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-02 14:04:35 +02:00
lizzie 5c20f244c9 [cmake] stop building OpenGL on Android (#4342)
Signed-off-by: lizzie <lizzie@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

we don't even support OpenGL on android

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4342
Reviewed-by: crueter <crueter@eden-emu.dev>
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
2026-09-02 04:07:24 +02:00
crueter aa4e494c08 [android] Replace Oboe audio with SDL3 audio (#4338)
Supersedes #4335.

Replaces the Oboe audio backend with SDL3, since we already support it.
A few caveats:

- SDL defines a JVM OnLoad method already, so we have to instead hijack
  NativeLibrary for id_cache initialization
- This also means some internal fields in Double/Integer/Boolean (namely
  `value`) can't be accessed, so I switched those to methods for now.
- SDL Java sources are pulled in from the CI package.
- Since we can't use SDLActivity, `SDL.setupJNI()` handles everything

Signed-off-by: crueter <crueter@eden-emu.dev>

- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4338
Reviewed-by: Lizzie and Samuel <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-02 00:29:58 +02:00
353 changed files with 19902 additions and 12633 deletions
-92
View File
@@ -1,92 +0,0 @@
#!/bin/sh -ex
# SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
# SPDX-License-Identifier: GPL-3.0-or-later
NUM_JOBS=$(nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 2)
: "${CCACHE:=false}"
RETURN=0
usage() {
cat <<EOF
Usage: $0 [-b|--build-type BUILD_TYPE] [-o|--outdir OUTPUT_DIRECTORY]
Build script for Emscripten (using wasm64).
Options:
--build-type Set the CMake build type (Release|RelWithDebInfo|MinSizeRel|Debug)
Default: Release
--outdir Set the output directory
Default: build
EOF
exit "$RETURN"
}
die() {
echo "-- ! $*" >&2
RETURN=1 usage
}
type() {
[ -z "$1" ] && die "You must specify a valid type."
TYPE="$1"
}
outdir() {
[ -z "$1" ] && die "You must specify a valid output directory."
OUTDIR="$1"
}
while true; do
case "$1" in
-r|--release) DEVEL=false ;;
-b|--build-type) type "$2"; shift ;;
-o|--outdir) outdir "$2"; shift ;;
-h|--help) usage ;;
*) break ;;
esac
shift
done
: "${TYPE:=Release}"
: "${DEVEL:=true}"
: "${OUTDIR:=build}"
# -sMEMORY64 must be specified twice, see below
# The CMake toolchain file will match against MEMORY64 but will fail to match if:
# - it's either -sMEMORY64
# - or it's either -sMEMORY64=1
# The line in question:
# if (CMAKE_C_FLAGS MATCHES "MEMORY64")
# However why need to specify -sMEMORY64=1 then? Oh that's because if you didn't set
# the =1, it would assume you meant =0, which equates to not specifying it at all
# This seems to be fixed in later versions but occurs atleast on 4.0.3-git and below.
emcmake cmake -B "$OUTDIR" -G "Unix Makefiles" \
-DCMAKE_BUILD_TYPE=${TYPE} \
-DENABLE_OPENGL=OFF \
-DENABLE_LTO=OFF \
-DENABLE_QT=OFF \
-DENABLE_UNITY_BUILD=OFF \
-DENABLE_QT_TRANSLATION=OFF \
-DENABLE_CUBEB=OFF \
-DENABLE_LIBUSB=OFF \
-DENABLE_UPDATE_CHECKER=OFF \
-DENABLE_WEB_SERVICE=OFF \
-DUSE_DISCORD_PRESENCE=OFF \
-DENABLE_WIFI_SCAN=OFF \
-DUSE_FASTER_LINKER=ON \
-DYUZU_STATIC_BUILD=ON \
-DYUZU_USE_BUNDLED_OPENSSL=OFF \
-DYUZU_USE_EXTERNAL_FFMPEG=ON \
-Dzstd_FORCE_BUNDLED=ON \
-DOpenSSL_FORCE_BUNDLED=ON \
-DEMSCRIPTEN_SYSTEM_PROCESSOR=wasm \
-DCMAKE_C_FLAGS="-s MEMORY64 -m64 -pipe -sMEMORY64=1" \
-DCMAKE_CXX_FLAGS="-s MEMORY64 -m64 -pipe -sMEMORY64=1" \
-DCMAKE_EXE_LINKER_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1" \
-DCMAKE_C_LINK_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1" \
-DCMAKE_CXX_LINK_FLAGS="-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1"
cmake --build "$OUTDIR" -- -j$NUM_JOBS
+3
View File
@@ -63,3 +63,6 @@ artifacts
/install*
vulkansdk*.exe
*.tar.zst
# Build-time generated external sources
externals/generated
@@ -1,40 +0,0 @@
diff --git a/cmake/ConfigureOpenSSL.cmake b/cmake/ConfigureOpenSSL.cmake
index 3012e05..2ae23ff 100644
--- a/cmake/ConfigureOpenSSL.cmake
+++ b/cmake/ConfigureOpenSSL.cmake
@@ -108,7 +108,8 @@ function(configure_openssl)
)
if(NOT "${CONFIGURE_OPTIONS_OLD}" STREQUAL "")
- if(CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
+ # TODO(lizzie): Emscripten has issues with rebuilding due to the wrapper it uses
+ if(CMAKE_SYSTEM_NAME MATCHES "Emscripten" OR CONFIGURE_OPTIONS STREQUAL CONFIGURE_OPTIONS_OLD)
message(STATUS "Found previous configure results. Don't perform configuration")
return()
endif()
@@ -134,10 +135,24 @@ function(configure_openssl)
set(VERBOSE_OPTION OUTPUT_QUIET)
endif()
+ if (CMAKE_SYSTEM_NAME MATCHES "Emscripten")
+ set(EMSCRIPTEN_CMAKE_WRAPPER "emcmake")
+ find_program(EMCC emcc REQUIRED)
+ set(EMSCRIPTEN_LINKER ${EMCC})
+ list(APPEND CONFIGURE_COMMAND wasm64)
+ else()
+ set(EMSCRIPTEN_CMAKE_WRAPPER "")
+ set(EMSCRIPTEN_LINKER ${CMAKE_LINKER})
+ endif ()
+
execute_process(
- COMMAND ${CMAKE_COMMAND} -E env
+ COMMAND ${EMSCRIPTEN_CMAKE_WRAPPER} ${CMAKE_COMMAND} -E env
"CFLAGS=${CMAKE_C_FLAGS}"
"CXXFLAGS=${CMAKE_CXX_FLAGS}"
+ "LDFLAGS=${CMAKE_CXX_LINK_FLAGS}"
+ "CC=${CMAKE_C_COMPILER}"
+ "CXX=${CMAKE_CXX_COMPILER}"
+ "LD=${EMSCRIPTEN_LINKER}"
${CONFIGURE_COMMAND}
WORKING_DIRECTORY ${CONFIGURE_BUILD_DIR}
${VERBOSE_OPTION}
-112
View File
@@ -1,112 +0,0 @@
diff --git a/Configurations/10-main.conf b/Configurations/10-main.conf
index e62721e..243feb4 100644
--- a/Configurations/10-main.conf
+++ b/Configurations/10-main.conf
@@ -1970,6 +1970,26 @@ my %targets = (
multilib => "64",
},
+ "wasm32" => {
+ inherit_from => [ "BASE_unix" ],
+ CC => "emcc",
+ CXX => "emc++",
+ cflags => combine("--target=wasm32-unknown-emscripten", threads("-pthread")),
+ cxxflags => combine("--target=wasm32-unknown-emscripten", threads("-pthread")),
+ lib_cppflags => add("-DL_ENDIAN"),
+ bn_ops => "THIRTY_TWO_BIT",
+ },
+ "wasm64" => {
+ inherit_from => [ "BASE_unix" ],
+ CC => "emcc",
+ CXX => "emc++",
+ cflags => combine("--target=wasm64-unknown-emscripten", threads("-pthread")),
+ cxxflags => combine("--target=wasm64-unknown-emscripten", threads("-pthread")),
+ lib_cppflags => add("-DL_ENDIAN"),
+ bn_ops => "SIXTY_FOUR_BIT_LONG",
+ },
+
+
#### uClinux
"uClinux-dist" => {
inherit_from => [ "BASE_unix" ],
diff --git a/crypto/rand/rand_lib.c b/crypto/rand/rand_lib.c
index d9e8f02..7faf347 100644
--- a/crypto/rand/rand_lib.c
+++ b/crypto/rand/rand_lib.c
@@ -379,17 +379,27 @@ void RAND_add(const void *buf, int num, double randomness)
#if !defined(OPENSSL_NO_DEPRECATED_1_1_0)
int RAND_pseudo_bytes(unsigned char *buf, int num)
{
+#if defined(__wasi__)
+ arc4random_buf(buf, num);
+ return 1;
+#elif defined(__EMSCRIPTEN__)
+ return 1;
+#else
const RAND_METHOD *meth = RAND_get_rand_method();
if (meth != NULL && meth->pseudorand != NULL)
return meth->pseudorand(buf, num);
ERR_raise(ERR_LIB_RAND, RAND_R_FUNC_NOT_IMPLEMENTED);
return -1;
+#endif
}
#endif
int RAND_status(void)
{
+#if defined(__EMSCRIPTEN__) || defined(__wasi__)
+ return 1;
+#else
EVP_RAND_CTX *rand;
#ifndef OPENSSL_NO_DEPRECATED_3_0
const RAND_METHOD *meth = RAND_get_rand_method();
@@ -401,6 +411,7 @@ int RAND_status(void)
if ((rand = RAND_get0_primary(NULL)) == NULL)
return 0;
return EVP_RAND_get_state(rand) == EVP_RAND_STATE_READY;
+#endif
}
#else /* !FIPS_MODULE */
@@ -420,6 +431,12 @@ const RAND_METHOD *RAND_get_rand_method(void)
int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
unsigned int strength)
{
+#if defined(__wasi__)
+ arc4random_buf(buf, num);
+ return 1;
+#elif defined(__EMSCRIPTEN__)
+ return 1;
+#else
RAND_GLOBAL *dgbl;
EVP_RAND_CTX *rand;
#if !defined(OPENSSL_NO_DEPRECATED_3_0) && !defined(FIPS_MODULE)
@@ -451,6 +468,7 @@ int RAND_priv_bytes_ex(OSSL_LIB_CTX *ctx, unsigned char *buf, size_t num,
return EVP_RAND_generate(rand, buf, num, strength, 0, NULL, 0);
return 0;
+#endif
}
int RAND_priv_bytes(unsigned char *buf, int num)
diff --git a/ssl/ssl_cert.c b/ssl/ssl_cert.c
index 3d21801..e026f8f 100644
--- a/ssl/ssl_cert.c
+++ b/ssl/ssl_cert.c
@@ -941,6 +941,7 @@ done:
return ret;
}
+#ifndef OPENSSL_NO_POSIX_IO
int SSL_add_dir_cert_subjects_to_stack(STACK_OF(X509_NAME) *stack,
const char *dir)
{
@@ -1016,6 +1017,7 @@ err:
return ret;
}
+#endif
static int add_uris_recursive(STACK_OF(X509_NAME) *stack,
const char *uri, int depth)
@@ -0,0 +1,11 @@
--- a/source/effect_lexer.cpp
+++ b/source/effect_lexer.cpp
@@ -1181,7 +1181,7 @@
}
else
{
-#if 0
+#if !defined(__cpp_lib_to_chars)
exponent += decimal_location - mantissa_size;
const bool exponent_negative = exponent < 0;
@@ -0,0 +1,112 @@
diff --git a/include/vk_mem_alloc.h b/include/vk_mem_alloc.h
index 8df0364..4856064 100644
--- a/include/vk_mem_alloc.h
+++ b/include/vk_mem_alloc.h
@@ -3017,7 +3017,7 @@ remove them if not needed.
#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16)
#include <cstdlib>
-static void* vma_aligned_alloc(size_t alignment, size_t size)
+static inline void* vma_aligned_alloc(size_t alignment, size_t size)
{
// alignment must be >= sizeof(void*)
if(alignment < sizeof(void*))
@@ -3860,7 +3860,7 @@ Returned value is the found element, if present in the collection or place where
new element with value (key) should be inserted.
*/
template <typename CmpLess, typename IterT, typename KeyT>
-static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)
+static inline IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp)
{
size_t down = 0;
size_t up = size_t(end - beg);
@@ -3898,7 +3898,7 @@ Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT.
T must be pointer type, e.g. VmaAllocation, VmaPool.
*/
template<typename T>
-static bool VmaValidatePointerArray(uint32_t count, const T* arr)
+static inline bool VmaValidatePointerArray(uint32_t count, const T* arr)
{
for (uint32_t i = 0; i < count; ++i)
{
@@ -4188,13 +4188,13 @@ static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr
}
template<typename T>
-static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
+static inline T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks)
{
return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T));
}
template<typename T>
-static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
+static inline T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count)
{
return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T));
}
@@ -4204,14 +4204,14 @@ static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, si
#define vma_new_array(allocator, type, count) new(VmaAllocateArray<type>((allocator), (count)))(type)
template<typename T>
-static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
+static inline void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr)
{
ptr->~T();
VmaFree(pAllocationCallbacks, ptr);
}
template<typename T>
-static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
+static inline void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count)
{
if (ptr != VMA_NULL)
{
@@ -4658,13 +4658,13 @@ void VmaVector<T, AllocatorT>::remove(size_t index)
#endif // _VMA_VECTOR_FUNCTIONS
template<typename T, typename allocatorT>
-static void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
+static inline void VmaVectorInsert(VmaVector<T, allocatorT>& vec, size_t index, const T& item)
{
vec.insert(index, item);
}
template<typename T, typename allocatorT>
-static void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
+static inline void VmaVectorRemove(VmaVector<T, allocatorT>& vec, size_t index)
{
vec.remove(index);
}
@@ -10620,19 +10620,19 @@ static void VmaFree(VmaAllocator hAllocator, void* ptr)
}
template<typename T>
-static T* VmaAllocate(VmaAllocator hAllocator)
+static inline T* VmaAllocate(VmaAllocator hAllocator)
{
return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T));
}
template<typename T>
-static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
+static inline T* VmaAllocateArray(VmaAllocator hAllocator, size_t count)
{
return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T));
}
template<typename T>
-static void vma_delete(VmaAllocator hAllocator, T* ptr)
+static inline void vma_delete(VmaAllocator hAllocator, T* ptr)
{
if(ptr != VMA_NULL)
{
@@ -10642,7 +10642,7 @@ static void vma_delete(VmaAllocator hAllocator, T* ptr)
}
template<typename T>
-static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
+static inline void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count)
{
if(ptr != VMA_NULL)
{
+12 -25
View File
@@ -3,6 +3,7 @@
cmake_minimum_required(VERSION 3.31)
set(CMAKE_OSX_DEPLOYMENT_TARGET "15.0" CACHE STRING "macOS deployment target")
project(yuzu)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules")
@@ -84,6 +85,8 @@ option(ENABLE_WERROR "Enable -Werror diagnostics" ON)
# Lossless Scaling frame generation. Only Android.
cmake_dependent_option(ENABLE_LSFG "Enable Lossless Scaling frame generation" ON "ANDROID" OFF)
option(ENABLE_RESHADE "Enable ReShade FX post-processing effects" ON)
# non-linux bundled qt are static
if (YUZU_USE_BUNDLED_QT AND (APPLE OR NOT UNIX))
set(YUZU_STATIC_BUILD ON)
@@ -214,8 +217,6 @@ if(MSVC)
endif()
# TODO(crueter): Cleanup, each dep that has a bundled option should allow to choose between bundled, external, system
cmake_dependent_option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" "${MSVC}" "NOT ANDROID" OFF)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
set(EXT_DEFAULT OFF)
@@ -223,6 +224,8 @@ if (MSVC OR ANDROID)
set(EXT_DEFAULT ON)
endif()
option(YUZU_USE_BUNDLED_SDL3 "Download bundled SDL3 build" ${EXT_DEFAULT})
# ffmpeg
option(YUZU_USE_BUNDLED_FFMPEG "Download bundled FFmpeg" ${EXT_DEFAULT})
cmake_dependent_option(YUZU_USE_EXTERNAL_FFMPEG "Build FFmpeg from external source" "${SOLARIS}" "NOT WIN32 AND NOT ANDROID" OFF)
@@ -238,7 +241,7 @@ option(YUZU_USE_BUNDLED_SIRIT "Download bundled sirit" ${BUNDLED_SIRIT_DEFAULT})
# FreeBSD 15+ has libusb, versions below should disable it
cmake_dependent_option(ENABLE_LIBUSB "Enable the use of LibUSB" ON "WIN32 OR LINUX OR FREEBSD OR APPLE" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE" OFF)
cmake_dependent_option(ENABLE_OPENGL "Enable OpenGL" ON "NOT (WIN32 AND ARCHITECTURE_arm64) AND NOT APPLE AND NOT ANDROID" OFF)
mark_as_advanced(FORCE ENABLE_OPENGL)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
@@ -377,15 +380,6 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
# Prefer the -pthread flag on Linux.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
# It is absolutely promordial to enable on Emscripten
# Not only this allows to use std::thread and std::jthread without exceptions
# but it also fixes several issues related to MT operations.
# ...and CMake doesn't include it by default even when we specify
# that we prefer the pthread flag; why is that? I don't know.
if (EMSCRIPTEN)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthread>)
add_link_options($<$<COMPILE_LANGUAGE:C,CXX>:-pthread>)
endif()
find_package(RenderDoc MODULE)
@@ -414,12 +408,7 @@ set(BUILD_TESTING OFF)
set(ENABLE_TESTING OFF)
# boost
if (EMSCRIPTEN)
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap headers filesystem crc variant)
set(BOOST_CONTAINER_HEADER_ONLY ON)
else()
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
endif()
set(BOOST_INCLUDE_LIBRARIES algorithm icl pool container heap asio headers process filesystem crc variant)
AddJsonPackage(boost)
@@ -440,12 +429,10 @@ if (Boost_ADDED)
target_compile_options(boost_heap INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
target_compile_options(boost_icl INTERFACE $<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shadow>)
# May not exist (i.e emscripten)
if (TARGET boost_asio)
target_compile_options(boost_asio INTERFACE
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>)
endif()
target_compile_options(boost_asio INTERFACE
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-conversion>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-implicit-fallthrough>
)
endif()
endif()
@@ -528,7 +515,7 @@ endfunction()
# =============================================
if (APPLE)
foreach(fw Carbon Metal Cocoa IOKit CoreVideo CoreMedia Security UniformTypeIdentifiers)
foreach(fw Carbon Metal Cocoa IOKit CoreVideo CoreMedia Security UniformTypeIdentifiers Foundation)
find_library(${fw}_LIBRARY ${fw} REQUIRED)
list(APPEND PLATFORM_LIBRARIES ${${fw}_LIBRARY})
endforeach()
+16 -21
View File
@@ -1,12 +1,4 @@
{
"": {
"ci": true,
"hash": "9f50d993c39529e022ad456163de91ac5934e16faf8fc348f305355fc0a643c534f87ded707e11bd03bcbc0a1760bd20852b6533a67ac0558a078385181cb184",
"name": "SDL3",
"package": "SDL3",
"repo": "crueter-ci/SDL3",
"version": "3.4.14-1788231389-147a8ee32d"
},
"biscuit": {
"hash": "1229f345b014f7ca544dedb4edb3311e41ba736f9aa9a67f88b5f26f3c983288c6bb6cdedcfb0b8a02c63088a37e6a0d7ba97d9c2a4d721b213916327cffe28a",
"min_version": "0.9.1",
@@ -68,10 +60,10 @@
},
"discord-rpc": {
"find_args": "MODULE",
"hash": "8213c43dcb0f7d479f5861091d111ed12fbdec1e62e6d729d65a4bc181d82f48a35d5fd3cd5c291f2393ac7c9681eabc6b76609755f55376284c8a8d67e148f3",
"hash": "8d680b3a16d6f6bf292ad823cf8635595ff986f2a49f758e3009f505b540a9d75194a8cf44cddea75736a8c3e3b5160166e716a0939ce3f18cc463cf648753fe",
"package": "DiscordRPC",
"repo": "eden-emulator/discord-rpc",
"version": "0d8b2d6a37"
"version": "76616d8675"
},
"enet": {
"find_args": "MODULE",
@@ -115,7 +107,7 @@
"find_args": "MODULE GLOBAL",
"hash": "159ed94965018f2a371d45a3bfc1961e5fb1549e501ded70a6b4532d7fe99d0579c18b5195aff6e35f96f399b426cea2650ec9fb75ef80d4c9edeccb51f2e6c9",
"options": [
"HTTPLIB_REQUIRE_OPENSSL OFF",
"HTTPLIB_REQUIRE_OPENSSL ON",
"HTTPLIB_DISABLE_MACOSX_AUTOMATIC_ROOT_CERTIFICATES ON"
],
"patches": [
@@ -186,19 +178,12 @@
"repo": "eden-emulator/oaknut",
"version": "v2.0.3"
},
"oboe": {
"bundled": true,
"hash": "ce4011afe7345370d4ead3b891cd69a5ef224b129535783586c0ca75051d303ed446e6c7f10bde8da31fff58d6e307f1732a3ffd03b249f9ef1fd48fd4132715",
"repo": "google/oboe",
"version": "1.10.0"
},
"openssl": {
"hash": "29002ce50cb95a4f4f1d0e9d3f684401fbd4eac34203dc2eef3b6334af5d44aa46bf788b63a6f5c139c383eafb7269ae87a58a9a3ad5912903b9773e545ccc0a",
"min_version": "3.0.0",
"package": "OpenSSL",
"patches": [
"0001-add-bundled-cert.patch",
"0002-wasm-support.patch"
"0001-add-bundled-cert.patch"
],
"repo": "openssl/openssl",
"version": "openssl-3.6.2"
@@ -220,8 +205,7 @@
"0001-cpmutil-compat.patch",
"0002-use-ccache.patch",
"0003-use-cmake-compiler-flags.patch",
"0004-use-shell-wrapper.patch",
"0005-wasm-support.patch"
"0004-use-shell-wrapper.patch"
],
"repo": "jimmy-park/openssl-cmake",
"version": "3.6.2"
@@ -254,6 +238,14 @@
"repo": "stachenov/quazip",
"version": "2e95c9001b"
},
"reshade": {
"hash": "6fc9b39821ca6e2d91160f05846b3a42159fec759f5d6fba91d1a57801a1bdaf6047740578c136f0b1a60a9c29ad160d87dbfe0dbfbcdf6ea879a8a078377b8c",
"patches": [
"0001-from-chars-fallback.patch"
],
"repo": "crosire/reshade",
"version": "v6.8.0"
},
"sdl3": {
"hash": "df5a323af7ac366661a3c0e887969c72584d232f3cc211419d59b0487b620b6b2859d4549c9e8df002ee489290062e466fcfddf7edc0872a37b1f2845e81c0f3",
"min_version": "3.2.10",
@@ -319,6 +311,9 @@
"find_args": "CONFIG",
"hash": "deb5902ef8db0e329fbd5f3f4385eb0e26bdd9f14f3a2334823fb3fe18f36bc5d235d620d6e5f6fe3551ec3ea7038638899db8778c09f6d5c278f5ff95c3344b",
"package": "VulkanMemoryAllocator",
"patches": [
"0001-macos-clang.patch"
],
"repo": "GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator",
"version": "v3.3.0"
},
+320 -328
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+371 -369
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+315 -323
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+327 -335
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+387 -381
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+309 -317
View File
File diff suppressed because it is too large Load Diff
+3
View File
@@ -0,0 +1,3 @@
name=Anime
description=Cel shaded look: the picture is cleaned first, then the lighting is collapsed into flat bands with inked edges, finished with a small glow.
chain=Denoise.fx|Denoise|Strength=0.18,Radius=1.2;CelShading.fx|CelShading|Bands=5,BandContrast=0.35,BandEdge=0.2,Detail=0.7,OutlineStrength=0.7,OutlineThreshold=0.2,Saturation=1.3;Bloom.fx|Bloom|Radius=1.2,Amount=0.25
+3
View File
@@ -0,0 +1,3 @@
name=Cinematic
description=Film look: soft glow on the highlights, a filmic contrast curve, cool shadows against warm highlights, fine grain and a gentle vignette.
chain=Bloom.fx|Bloom|Radius=1.6,Amount=0.35;FilmicCurve.fx|FilmicCurve|Toe=1.35,Shoulder=1.3,Amount=0.85;SplitToning.fx|SplitToning|ShadowHue=210,ShadowStrength=0.25,HighlightHue=38,HighlightStrength=0.3;FilmGrain.fx|FilmGrain|Intensity=0.022;Vignette.fx|Vignette|Strength=0.5
+3
View File
@@ -0,0 +1,3 @@
name=Clean
description=Cleans up a low internal resolution: removes dithering and compression noise, smooths banded skies and brings edge detail back. The safe starting point if you are not sure what you want.
chain=Denoise.fx|Denoise|Strength=0.14,Curve=1.2;Deband.fx|Deband|;Sharpen.fx|Sharpen|Amount=0.75
+3
View File
@@ -0,0 +1,3 @@
name=Dreamy
description=Soft focus: the centre of the screen stays sharp while everything around it blurs, with a wide glow over the highlights. Good for slower games and cutscenes.
chain=Blur.fx|Blur|Radius=6,Strength=0.85,FocusSize=0.28,FocusSoftness=0.5;Bloom.fx|Bloom|Radius=2.0,Amount=0.55
+3
View File
@@ -0,0 +1,3 @@
name=Retro
description=Old television look: colour fringing at the edges of the screen, curved scanlines with a phosphor mask, and darkened corners. The scanline roll is switched off so it does not crawl.
chain=ChromaticAberration.fx|ChromaticAberration|Strength=1.0;CRT.fx|CRT|RollSpeed=0,Bleed=1.2;Vignette.fx|Vignette|Strength=0.7
+3
View File
@@ -0,0 +1,3 @@
name=Vivid
description=Punchier colour without stylising the image: warmer and more saturated, blacks and whites pushed to the ends of the range, and a light sharpen.
chain=NaturalColors.fx|NaturalColors|Luma=1.15,Chroma=1.35;Levels.fx|Levels|InputBlack=0.02,InputWhite=0.98;Sharpen.fx|Sharpen|Amount=0.5
+70
View File
@@ -0,0 +1,70 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Ported from bloomnoblur.fsh in PPSSPP.
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Radius <
ui_type = "slider";
ui_label = "Radius";
ui_tooltip = "How far the glow spreads from bright areas.";
ui_min = 0.0; ui_max = 4.0; ui_step = 0.05;
> = 1.0;
uniform float Amount <
ui_type = "slider";
ui_label = "Amount";
ui_tooltip = "Strength of the glow added on top of the image.";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
> = 0.6;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float Weight(float3 color)
{
float gray = (color.r + color.g + color.b) / 3.0;
float saturation = (abs(color.r - gray) + abs(color.g - gray) + abs(color.b - gray)) / 3.0;
return gray * gray / max(saturation, 0.25);
}
float4 PS_Bloom(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 color = tex2D(BackBuffer, uv).rgb;
float gray = (color.r + color.g + color.b) / 3.0;
float saturation = (abs(color.r - gray) + abs(color.g - gray) + abs(color.b - gray)) / 3.0;
float spread = 0.002 * gray / max(saturation, 0.25) * Radius;
float3 sum = float3(0.0, 0.0, 0.0);
[unroll] for (int x = -3; x <= 3; x += 2)
{
[unroll] for (int y = -3; y <= 3; y += 2)
{
float3 tap = tex2D(BackBuffer, uv + float2(x, y) * spread).rgb;
sum += tap * Weight(tap);
}
}
sum /= 16.0;
return float4(saturate(color + sum * Amount), 1.0);
}
technique Bloom <
ui_label = "Bloom";
ui_tooltip = "Bleeds light out of the brightest areas into a soft halo, the way a camera does when pointed at something too bright.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Bloom;
}
}
+87
View File
@@ -0,0 +1,87 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Radius <
ui_type = "slider";
ui_label = "Radius";
ui_tooltip = "How far the blur reaches, in pixels.";
ui_min = 1.0; ui_max = 12.0; ui_step = 0.5;
> = 4.0;
uniform float Strength <
ui_type = "slider";
ui_label = "Strength";
ui_tooltip = "Blend between the original image and the blurred one.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 1.0;
uniform float FocusSize <
ui_type = "slider";
ui_label = "Sharp Centre";
ui_tooltip = "Size of the region left in focus at the centre of the screen. At zero the whole image is blurred evenly.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.0;
uniform float FocusSoftness <
ui_type = "slider";
ui_label = "Focus Falloff";
ui_tooltip = "How gradually the sharp centre gives way to the blur.";
ui_min = 0.05; ui_max = 1.0; ui_step = 0.05;
> = 0.4;
static const int TAPS = 17;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_Blur(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
float3 original = tex2D(BackBuffer, uv).rgb;
float2 centred = (uv - 0.5) * float2(BUFFER_WIDTH * BUFFER_RCP_HEIGHT, 1.0) * 2.0;
float distance_from_centre = length(centred);
float no_focus = 1.0 - step(0.001, FocusSize);
float focus = max(smoothstep(FocusSize, FocusSize + FocusSoftness, distance_from_centre), no_focus);
float amount = Strength * focus;
float angle = frac(sin(dot(pos.xy, float2(12.9898, 78.233))) * 43758.5453) * 6.2831853;
float2 spoke = float2(cos(angle), sin(angle));
const float2 turn = float2(cos(2.39996323), sin(2.39996323));
const float decay = exp(-2.0 / float(TAPS));
float weight = exp(-1.0 / float(TAPS));
float3 sum = float3(0.0, 0.0, 0.0);
float total = 0.0;
[unroll] for (int i = 0; i < TAPS; ++i)
{
float reach = sqrt((float(i) + 0.5) / float(TAPS));
sum += tex2D(BackBuffer, uv + spoke * reach * texel * Radius).rgb * weight;
total += weight;
weight *= decay;
spoke = float2(spoke.x * turn.x - spoke.y * turn.y,
spoke.x * turn.y + spoke.y * turn.x);
}
float3 blurred = sum / total;
return float4(lerp(original, blurred, amount), 1.0);
}
technique Blur <
ui_label = "Blur";
ui_tooltip = "Gaussian blur with an optional sharp centre, for softening the picture or faking depth of field.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Blur;
}
}
+74
View File
@@ -0,0 +1,74 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Ported from crt.fsh in PPSSPP, by KillaMaaki.
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Timer < source = "timer"; > = 0.0;
uniform float Density <
ui_type = "slider";
ui_label = "Line Density";
ui_min = 60.0; ui_max = 720.0; ui_step = 10.0;
> = 272.0;
uniform float RollSpeed <
ui_type = "slider";
ui_label = "Roll Speed";
ui_tooltip = "Speed of the rolling bar. Zero disables it.";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
> = 1.0;
uniform float Bleed <
ui_type = "slider";
ui_label = "Colour Bleed";
ui_tooltip = "Horizontal separation of the red and green channels.";
ui_min = 0.0; ui_max = 4.0; ui_step = 0.1;
> = 1.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_CRT(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float seconds = Timer * 0.001;
float scan = floor((uv.y + seconds * RollSpeed * 0.5) * Density);
float line_intensity = frac(scan * 0.5) * 2.0;
float2 shift = float2(line_intensity * 0.0005, 0.0);
float2 bleed = float2(BUFFER_RCP_WIDTH * Bleed, 0.0);
float r = tex2D(BackBuffer, uv + bleed + shift).r;
float g = tex2D(BackBuffer, uv - bleed + shift).g;
float b = tex2D(BackBuffer, uv).b;
float3 color = float3(r, g * 0.99, b) * clamp(line_intensity, 0.85, 1.0);
if (RollSpeed > 0.0)
{
float rollbar = sin((uv.y + seconds * RollSpeed) * 4.0);
color += rollbar * 0.02;
}
return float4(saturate(color), 1.0);
}
technique CRT <
ui_label = "CRT";
ui_tooltip = "Curved scanlines and the phosphor mask of a CRT television, for games that were drawn with that kind of screen in mind.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_CRT;
}
}
+79
View File
@@ -0,0 +1,79 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project
// SPDX-FileCopyrightText: guest(r)
// SPDX-License-Identifier: GPL-2.0-or-later
// Ported from cartoon.fsh in PPSSPP, Advanced Cartoon shader I by guest(r).
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float EdgeStrength <
ui_type = "slider";
ui_label = "Edge Strength";
ui_tooltip = "Darkness of the ink outline drawn around detected edges.";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
> = 0.5;
uniform float Levels <
ui_type = "slider";
ui_label = "Colour Levels";
ui_tooltip = "How many bands the colours are quantised into.";
ui_min = 2.0; ui_max = 16.0; ui_step = 1.0;
> = 4.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_Cartoon(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
float3 c00 = tex2D(BackBuffer, uv + texel * float2(-1.0, -1.0)).rgb;
float3 c10 = tex2D(BackBuffer, uv + texel * float2( 0.0, -1.0)).rgb;
float3 c20 = tex2D(BackBuffer, uv + texel * float2( 1.0, -1.0)).rgb;
float3 c01 = tex2D(BackBuffer, uv + texel * float2(-1.0, 0.0)).rgb;
float3 c11 = tex2D(BackBuffer, uv).rgb;
float3 c21 = tex2D(BackBuffer, uv + texel * float2( 1.0, 0.0)).rgb;
float3 c02 = tex2D(BackBuffer, uv + texel * float2(-1.0, 1.0)).rgb;
float3 c12 = tex2D(BackBuffer, uv + texel * float2( 0.0, 1.0)).rgb;
float3 c22 = tex2D(BackBuffer, uv + texel * float2( 1.0, 1.0)).rgb;
const float3 dt = float3(1.0, 1.0, 1.0);
float d1 = dot(abs(c00 - c22), dt);
float d2 = dot(abs(c20 - c02), dt);
float hl = dot(abs(c01 - c21), dt);
float vl = dot(abs(c10 - c12), dt);
float edge = EdgeStrength * (d1 + d2 + hl + vl) / (dot(c11, dt) + 0.15);
float lc = Levels * length(c11);
float f = frac(lc);
f *= f;
lc = (floor(lc) + f * f) / Levels + 0.05;
float3 unit = normalize(max(c11, 0.0001));
float3 quant = Levels * unit;
float3 frct = frac(quant);
frct *= frct;
quant = floor(quant) + 0.05 * dt + frct * frct;
float3 color = lc * (1.1 - edge * sqrt(edge)) * quant / Levels;
return float4(saturate(color), 1.0);
}
technique Cartoon <
ui_label = "Cartoon";
ui_tooltip = "Ink outlines and flat colour bands. Faithful port of the PPSSPP shader; the outline gets heavy in dark scenes, so try Cartoon Soft or Cel Shading if the blacks smear.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Cartoon;
}
}
+100
View File
@@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project
// SPDX-FileCopyrightText: guest(r)
// SPDX-License-Identifier: GPL-2.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float EdgeStrength <
ui_type = "slider";
ui_label = "Edge Strength";
ui_tooltip = "Darkness of the ink outline drawn around detected edges.";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
> = 0.45;
uniform float ShadowGuard <
ui_type = "slider";
ui_label = "Shadow Guard";
ui_tooltip = "Holds the outline back in dark areas. Raise it if shadows turn into black blobs.";
ui_min = 0.1; ui_max = 2.0; ui_step = 0.05;
> = 0.8;
uniform float Levels <
ui_type = "slider";
ui_label = "Colour Levels";
ui_tooltip = "How many bands the colours are quantised into.";
ui_min = 2.0; ui_max = 16.0; ui_step = 1.0;
> = 6.0;
uniform float Smoothing <
ui_type = "slider";
ui_label = "Banding";
ui_tooltip = "How far the picture is pushed towards flat bands.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.75;
uniform float Saturation <
ui_type = "slider";
ui_label = "Saturation";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
> = 1.15;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_CartoonSoft(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
float3 c00 = tex2D(BackBuffer, uv + texel * float2(-1.0, -1.0)).rgb;
float3 c10 = tex2D(BackBuffer, uv + texel * float2( 0.0, -1.0)).rgb;
float3 c20 = tex2D(BackBuffer, uv + texel * float2( 1.0, -1.0)).rgb;
float3 c01 = tex2D(BackBuffer, uv + texel * float2(-1.0, 0.0)).rgb;
float3 c11 = tex2D(BackBuffer, uv).rgb;
float3 c21 = tex2D(BackBuffer, uv + texel * float2( 1.0, 0.0)).rgb;
float3 c02 = tex2D(BackBuffer, uv + texel * float2(-1.0, 1.0)).rgb;
float3 c12 = tex2D(BackBuffer, uv + texel * float2( 0.0, 1.0)).rgb;
float3 c22 = tex2D(BackBuffer, uv + texel * float2( 1.0, 1.0)).rgb;
const float3 dt = float3(1.0, 1.0, 1.0);
const float3 luma_weights = float3(0.299, 0.587, 0.114);
float d1 = dot(abs(c00 - c22), dt);
float d2 = dot(abs(c20 - c02), dt);
float hl = dot(abs(c01 - c21), dt);
float vl = dot(abs(c10 - c12), dt);
float luma = dot(c11, luma_weights);
float response = (d1 + d2 + hl + vl) / (luma * 2.0 + ShadowGuard);
float ink = 1.0 - saturate(response * EdgeStrength);
float scaled = luma * Levels;
float step_position = frac(scaled);
float eased = step_position * step_position * (3.0 - 2.0 * step_position);
float banded = (floor(scaled) + eased) / Levels;
float target = lerp(luma, banded, Smoothing);
float3 tinted = c11 * (target / max(luma, 0.001));
float3 grey = dot(tinted, luma_weights);
float3 color = lerp(grey, tinted, Saturation) * ink;
return float4(saturate(color), 1.0);
}
technique CartoonSoft <
ui_label = "Cartoon Soft";
ui_tooltip = "Ink outlines and flat colour bands, with the outline held back in the shadows so dark scenes do not turn into black blobs.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_CartoonSoft;
}
}
+120
View File
@@ -0,0 +1,120 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Bands <
ui_type = "slider";
ui_label = "Shading Bands";
ui_tooltip = "How many flat steps the lighting is collapsed into. Three or four gives the classic look.";
ui_min = 2.0; ui_max = 8.0; ui_step = 1.0;
> = 4.0;
uniform float BandContrast <
ui_type = "slider";
ui_label = "Band Contrast";
ui_tooltip = "Spreads the bands towards pure black and white. Lower it if the shadows crush.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.5;
uniform float BandEdge <
ui_type = "slider";
ui_label = "Band Edge";
ui_tooltip = "Width of the transition between bands. Low values give hard cel steps.";
ui_min = 0.02; ui_max = 1.0; ui_step = 0.02;
> = 0.15;
uniform float Detail <
ui_type = "slider";
ui_label = "Texture Detail";
ui_tooltip = "How much surface texture survives the flattening. At zero the bands are completely flat.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.6;
uniform float OutlineStrength <
ui_type = "slider";
ui_label = "Outline Strength";
ui_tooltip = "Opacity of the ink line drawn along detected edges.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.8;
uniform float OutlineThreshold <
ui_type = "slider";
ui_label = "Outline Threshold";
ui_tooltip = "How strong an edge has to be before it is inked. Raise it to keep ink off textures and specular highlights.";
ui_min = 0.01; ui_max = 0.4; ui_step = 0.01;
> = 0.18;
uniform float Saturation <
ui_type = "slider";
ui_label = "Saturation";
ui_tooltip = "Colour intensity of the flat regions.";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
> = 1.25;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_CelShading(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
const float3 luma_weights = float3(0.2126, 0.7152, 0.0722);
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
float3 centre = tex2D(BackBuffer, uv).rgb;
float luma = dot(centre, luma_weights);
float2 near = texel * 1.5;
float2 far = texel * 3.5;
float a00 = dot(tex2D(BackBuffer, uv + near * float2(-1.0, -1.0)).rgb, luma_weights);
float a20 = dot(tex2D(BackBuffer, uv + near * float2( 1.0, -1.0)).rgb, luma_weights);
float a02 = dot(tex2D(BackBuffer, uv + near * float2(-1.0, 1.0)).rgb, luma_weights);
float a22 = dot(tex2D(BackBuffer, uv + near * float2( 1.0, 1.0)).rgb, luma_weights);
float b00 = dot(tex2D(BackBuffer, uv + far * float2(-1.0, -1.0)).rgb, luma_weights);
float b20 = dot(tex2D(BackBuffer, uv + far * float2( 1.0, -1.0)).rgb, luma_weights);
float b02 = dot(tex2D(BackBuffer, uv + far * float2(-1.0, 1.0)).rgb, luma_weights);
float b22 = dot(tex2D(BackBuffer, uv + far * float2( 1.0, 1.0)).rgb, luma_weights);
float gx = (a00 + a02) - (a20 + a22);
float gy = (a00 + a20) - (a02 + a22);
float gradient = sqrt(gx * gx + gy * gy);
float ink = smoothstep(OutlineThreshold, OutlineThreshold + 0.04, gradient);
float lighting = (a00 + a20 + a02 + a22 + b00 + b20 + b02 + b22) * 0.125;
float detail = luma - lighting;
float softness = max(BandEdge, 0.001);
float coord = lighting * Bands - softness * 0.5;
float index = floor(coord) + smoothstep(1.0 - softness, 1.0, frac(coord));
float centred = (index + 0.5) / Bands;
float stretched = index / max(Bands - 1.0, 1.0);
float banded = saturate(lerp(centred, stretched, BandContrast));
float target = saturate(banded + detail * Detail);
float gain = min(target / max(luma, 0.001), 4.0);
float3 shaded = centre * gain;
float3 grey = dot(shaded, luma_weights);
float3 color = lerp(grey, shaded, Saturation);
color *= 1.0 - ink * OutlineStrength;
return float4(saturate(color), 1.0);
}
technique CelShading <
ui_label = "Cel Shading";
ui_tooltip = "Collapses the lighting into a few hard steps and inks the edges, keeping texture detail and hue intact.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_CelShading;
}
}
+53
View File
@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Strength <
ui_type = "slider";
ui_label = "Strength";
ui_tooltip = "Channel separation in pixels, measured at the edge of the screen.";
ui_min = 0.0; ui_max = 8.0; ui_step = 0.1;
> = 1.5;
uniform float Falloff <
ui_type = "slider";
ui_label = "Falloff";
ui_tooltip = "How fast the separation grows away from the centre. Higher keeps the middle clean.";
ui_min = 1.0; ui_max = 4.0; ui_step = 0.1;
> = 2.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_ChromaticAberration(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
float2 direction = uv - float2(0.5, 0.5);
float radius = length(direction);
float2 unit = direction / max(radius, 0.0001);
float2 offset = unit * Strength * pow(radius * 2.0, Falloff) * texel;
float red = tex2D(BackBuffer, uv + offset).r;
float green = tex2D(BackBuffer, uv).g;
float blue = tex2D(BackBuffer, uv - offset).b;
return float4(red, green, blue, 1.0);
}
technique ChromaticAberration <
ui_label = "Chromatic Aberration";
ui_tooltip = "Splits the colour channels apart towards the edges of the screen, the way a cheap lens fails to focus every colour on the same spot.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_ChromaticAberration;
}
}
+64
View File
@@ -0,0 +1,64 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Based on colorcorrection.fsh in PPSSPP.
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Saturation <
ui_type = "slider";
ui_label = "Saturation";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
> = 1.0;
uniform float Brightness <
ui_type = "slider";
ui_label = "Brightness";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
> = 1.0;
uniform float Contrast <
ui_type = "slider";
ui_label = "Contrast";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
> = 1.0;
uniform float Gamma <
ui_type = "slider";
ui_label = "Gamma";
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
> = 1.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_ColorGrade(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 rgb = tex2D(BackBuffer, uv).rgb;
float luma = dot(rgb, float3(0.2126, 0.7152, 0.0722));
rgb = lerp(float3(luma, luma, luma), rgb, Saturation);
rgb *= Brightness;
rgb = (rgb - 0.5) * Contrast + 0.5;
rgb = pow(max(rgb, 0.0), 1.0 / max(Gamma, 0.0001));
return float4(saturate(rgb), 1.0);
}
technique ColorGrade <
ui_label = "Colour Grade";
ui_tooltip = "The four basic colour controls in one pass: saturation, brightness, contrast and gamma. Reach for this before anything more specialised.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_ColorGrade;
}
}
+91
View File
@@ -0,0 +1,91 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2015 Niklas Haas
// SPDX-License-Identifier: MIT
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Threshold <
ui_type = "slider";
ui_label = "Threshold";
ui_tooltip = "How flat a neighbourhood must be before it gets smoothed. Raise it to catch wider bands, lower it to keep more detail.";
ui_min = 0.002; ui_max = 0.05; ui_step = 0.001;
> = 0.012;
uniform float Radius <
ui_type = "slider";
ui_label = "Radius";
ui_tooltip = "How far the sampling reaches, in pixels. Wider gradients need a larger radius.";
ui_min = 1.0; ui_max = 32.0; ui_step = 1.0;
> = 8.0;
uniform float Grain <
ui_type = "slider";
ui_label = "Dither";
ui_tooltip = "Noise added to break up whatever banding survives the smoothing.";
ui_min = 0.0; ui_max = 0.02; ui_step = 0.001;
> = 0.004;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float Hash(float2 p)
{
float3 scattered = frac(float3(p.x, p.y, p.x) * 0.1031);
scattered += dot(scattered, scattered.yzx + 33.33);
return frac((scattered.x + scattered.y) * scattered.z);
}
float4 PS_Deband(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
float3 centre = tex2D(BackBuffer, uv).rgb;
float base = Hash(pos.xy) * 6.2831853;
float3 total = float3(0.0, 0.0, 0.0);
float3 deviation = float3(0.0, 0.0, 0.0);
[unroll] for (int ring = 1; ring <= 2; ++ring)
{
float angle = base + float(ring) * 2.3999632;
float reach = Radius * float(ring) * 0.5;
float2 along = float2(cos(angle), sin(angle)) * reach;
float2 across = float2(-along.y, along.x);
float3 s0 = tex2D(BackBuffer, uv + along * texel).rgb;
float3 s1 = tex2D(BackBuffer, uv - along * texel).rgb;
float3 s2 = tex2D(BackBuffer, uv + across * texel).rgb;
float3 s3 = tex2D(BackBuffer, uv - across * texel).rgb;
total += s0 + s1 + s2 + s3;
deviation = max(deviation, max(max(abs(s0 - centre), abs(s1 - centre)),
max(abs(s2 - centre), abs(s3 - centre))));
}
float3 average = total * 0.125;
float3 flatness = float3(1.0, 1.0, 1.0) -
smoothstep(Threshold * 0.5, Threshold, deviation);
float3 result = lerp(centre, average, flatness);
float dither = (Hash(pos.xy + float2(71.3, 41.7)) - 0.5) * Grain;
return float4(saturate(result + dither), 1.0);
}
technique Deband <
ui_label = "Deband";
ui_tooltip = "Smooths the visible steps in gradients such as skies, then dithers whatever survives. Worth adding whenever large flat areas show rings.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Deband;
}
}
+83
View File
@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019-2021 bloc97
// SPDX-License-Identifier: MIT
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Strength <
ui_type = "slider";
ui_label = "Strength";
ui_min = 0.01; ui_max = 0.5; ui_step = 0.01;
> = 0.1;
uniform float Radius <
ui_type = "slider";
ui_label = "Radius";
ui_min = 0.3; ui_max = 2.0; ui_step = 0.05;
> = 1.0;
uniform float Curve <
ui_type = "slider";
ui_label = "Shadow Bias";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.05;
> = 1.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float3 IntensityWeight(float3 value, float3 sigma, float3 centre)
{
float3 scaled = (value - centre) / sigma;
return exp(-0.5 * scaled * scaled);
}
float SpatialWeight(float distance, float sigma)
{
float scaled = distance / sigma;
return exp(-0.5 * scaled * scaled);
}
float4 PS_Denoise(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
float3 centre = tex2D(BackBuffer, uv).rgb;
float3 intensity_sigma = max(pow(centre + 0.0001, Curve) * Strength, 0.0001);
float spatial_sigma = max(Radius, 0.05);
float3 sum = float3(0.0, 0.0, 0.0);
float3 total = float3(0.0, 0.0, 0.0);
[unroll] for (int y = -2; y <= 2; ++y)
{
[unroll] for (int x = -2; x <= 2; ++x)
{
float2 offset = float2(x, y);
float3 tap = tex2D(BackBuffer, uv + offset * texel).rgb;
float3 weight = IntensityWeight(tap, intensity_sigma, centre) *
SpatialWeight(length(offset), spatial_sigma);
sum += weight * tap;
total += weight;
}
}
return float4(sum / total, 1.0);
}
technique Denoise <
ui_label = "Denoise";
ui_tooltip = "Edge preserving blur that clears dithering and compression noise while leaving outlines sharp. Ported from Anime4K.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Denoise;
}
}
+65
View File
@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Intensity <
ui_type = "slider";
ui_label = "Intensity";
ui_min = 0.0; ui_max = 0.2; ui_step = 0.005;
> = 0.03;
uniform float Size <
ui_type = "slider";
ui_label = "Size";
ui_tooltip = "Grain cell size in pixels.";
ui_min = 1.0; ui_max = 4.0; ui_step = 1.0;
> = 1.0;
uniform float Colored <
ui_type = "slider";
ui_label = "Colour";
ui_tooltip = "Zero gives monochrome grain, one gives independent noise per channel.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
> = 0.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float Hash(float2 p)
{
float3 scattered = frac(float3(p.x, p.y, p.x) * 0.1031);
scattered += dot(scattered, scattered.yzx + 33.33);
return frac((scattered.x + scattered.y) * scattered.z);
}
float4 PS_FilmGrain(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 rgb = tex2D(BackBuffer, uv).rgb;
float2 cell = floor(pos.xy / max(Size, 1.0));
float mono = Hash(cell) - 0.5;
float3 chroma = float3(Hash(cell + 11.7), Hash(cell + 23.1), Hash(cell + 37.5)) - 0.5;
float3 noise = lerp(float3(mono, mono, mono), chroma, Colored);
float luma = dot(rgb, float3(0.2126, 0.7152, 0.0722));
float response = 1.0 - abs(luma * 2.0 - 1.0);
return float4(saturate(rgb + noise * Intensity * response), 1.0);
}
technique FilmGrain <
ui_label = "Film Grain";
ui_tooltip = "Adds photographic grain, strongest in the midtones and fading out in blacks and whites.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_FilmGrain;
}
}
+60
View File
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Exposure <
ui_type = "slider";
ui_label = "Exposure";
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
> = 1.0;
uniform float Toe <
ui_type = "slider";
ui_label = "Toe";
ui_tooltip = "Above 1.0 deepens the shadows, below 1.0 lifts them.";
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
> = 1.2;
uniform float Shoulder <
ui_type = "slider";
ui_label = "Shoulder";
ui_tooltip = "Above 1.0 opens up the highlights, below 1.0 compresses them.";
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
> = 1.2;
uniform float Amount <
ui_type = "slider";
ui_label = "Amount";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
> = 0.7;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_FilmicCurve(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 rgb = tex2D(BackBuffer, uv).rgb;
float3 curved = saturate(rgb * Exposure);
curved = pow(max(curved, 0.0), Toe);
curved = 1.0 - pow(max(1.0 - curved, 0.0), Shoulder);
return float4(saturate(lerp(rgb, curved, Amount)), 1.0);
}
technique FilmicCurve <
ui_label = "Filmic Curve";
ui_tooltip = "Filmic contrast curve. Deepens the shadows and opens the highlights without clipping either end.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_FilmicCurve;
}
}
+53
View File
@@ -0,0 +1,53 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Distortion <
ui_type = "slider";
ui_label = "Distortion";
ui_tooltip = "Positive bulges the picture outwards, negative pinches it inwards.";
ui_min = -0.5; ui_max = 0.5; ui_step = 0.01;
> = 0.1;
uniform float Zoom <
ui_type = "slider";
ui_label = "Zoom";
ui_tooltip = "Scales the picture to hide the edges the warp pulls in.";
ui_min = 0.5; ui_max = 1.5; ui_step = 0.01;
> = 1.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_LensDistortion(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float aspect = BUFFER_WIDTH * BUFFER_RCP_HEIGHT;
float2 half_size = float2(aspect, 1.0);
float2 unit = half_size / length(half_size);
float2 centred = (uv - 0.5) * 2.0 * unit;
float r2 = dot(centred, centred);
centred *= 1.0 + Distortion * r2;
centred /= max(Zoom, 0.001);
float2 source = centred / (2.0 * unit) + 0.5;
return float4(tex2D(BackBuffer, source).rgb, 1.0);
}
technique LensDistortion <
ui_label = "Lens Distortion";
ui_tooltip = "Barrel or pincushion warp, like looking through a wide angle lens.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_LensDistortion;
}
}
+66
View File
@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float InputBlack <
ui_type = "slider";
ui_label = "Input Black";
ui_tooltip = "Input level mapped to black. Raise it to deepen washed out shadows.";
ui_min = 0.0; ui_max = 0.5; ui_step = 0.005;
> = 0.0;
uniform float InputWhite <
ui_type = "slider";
ui_label = "Input White";
ui_min = 0.5; ui_max = 1.0; ui_step = 0.005;
> = 1.0;
uniform float Gamma <
ui_type = "slider";
ui_label = "Gamma";
ui_min = 0.2; ui_max = 3.0; ui_step = 0.01;
> = 1.0;
uniform float OutputBlack <
ui_type = "slider";
ui_label = "Output Black";
ui_tooltip = "Lifts crushed shadows so detail stops collapsing into one flat black.";
ui_min = 0.0; ui_max = 0.5; ui_step = 0.005;
> = 0.0;
uniform float OutputWhite <
ui_type = "slider";
ui_label = "Output White";
ui_min = 0.5; ui_max = 1.0; ui_step = 0.005;
> = 1.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_Levels(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 rgb = tex2D(BackBuffer, uv).rgb;
rgb = saturate((rgb - InputBlack) / max(InputWhite - InputBlack, 0.001));
rgb = pow(max(rgb, 0.0), 1.0 / max(Gamma, 0.001));
rgb = lerp(OutputBlack, OutputWhite, rgb);
return float4(saturate(rgb), 1.0);
}
technique Levels <
ui_label = "Levels";
ui_tooltip = "Black point, white point, gamma and output range. Use it to fix crushed or washed out shadows.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Levels;
}
}
+88
View File
@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Length <
ui_type = "slider";
ui_label = "Length";
ui_tooltip = "How far the streaks reach, as a percentage of screen height.";
ui_min = 0.0; ui_max = 20.0; ui_step = 0.5;
> = 5.0;
uniform float Zoom <
ui_type = "slider";
ui_label = "Zoom";
ui_tooltip = "Streaks running out from the centre of the screen, as if rushing forward. The centre stays sharp.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 1.0;
uniform float Pan <
ui_type = "slider";
ui_label = "Pan";
ui_tooltip = "Streaks running in one fixed direction, as if the camera were sweeping across.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.0;
uniform float Angle <
ui_type = "slider";
ui_label = "Pan Angle";
ui_tooltip = "Direction of the sweep, in degrees.";
ui_min = 0.0; ui_max = 360.0; ui_step = 5.0;
> = 0.0;
uniform float Spin <
ui_type = "slider";
ui_label = "Spin";
ui_tooltip = "Streaks curling around the centre, as if the camera were rolling. Negative turns the other way.";
ui_min = -1.0; ui_max = 1.0; ui_step = 0.05;
> = 0.0;
static const int TAPS = 24;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_MotionBlur(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float aspect = BUFFER_WIDTH * BUFFER_RCP_HEIGHT;
float2 to_screen = float2(aspect, 1.0);
float2 centred = (uv - 0.5) * to_screen * 2.0;
float2 outward = centred;
float2 around = float2(-centred.y, centred.x);
float radians_angle = Angle * 0.01745329;
float2 sweep = float2(cos(radians_angle), sin(radians_angle));
float2 velocity = sweep * Pan + outward * Zoom + around * Spin;
velocity *= Length * 0.01;
velocity /= to_screen;
float jitter = frac(sin(dot(pos.xy, float2(12.9898, 78.233))) * 43758.5453);
float3 sum = float3(0.0, 0.0, 0.0);
[unroll] for (int i = 0; i < TAPS; ++i)
{
float t = (float(i) + jitter) / float(TAPS) - 0.5;
sum += tex2D(BackBuffer, uv + velocity * t).rgb;
}
return float4(sum / float(TAPS), 1.0);
}
technique MotionBlur <
ui_label = "Motion Blur";
ui_tooltip = "Camera style motion blur: streaks the picture outwards, sideways or around, without touching still detail at the centre.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_MotionBlur;
}
}
+60
View File
@@ -0,0 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Ported from naturalA.fsh in PPSSPP, by ShadX, modified by SimoneT.
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Luma <
ui_type = "slider";
ui_label = "Luma Curve";
ui_tooltip = "Gamma applied to the luminance channel in YIQ space.";
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
> = 1.2;
uniform float Chroma <
ui_type = "slider";
ui_label = "Chroma Gain";
ui_tooltip = "Boost applied to the two colour difference channels.";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
> = 1.2;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_Natural(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
const float3x3 RGBtoYIQ = float3x3(0.299, 0.587, 0.114,
0.596, -0.275, -0.321,
0.212, -0.523, 0.311);
const float3x3 YIQtoRGB = float3x3(1.0, 0.95568806, 0.61985809,
1.0, -0.27158180, -0.64687382,
1.0, -1.10817733, 1.70506456);
float3 rgb = tex2D(BackBuffer, uv).rgb;
float3 yiq = mul(RGBtoYIQ, rgb);
yiq.x = pow(max(yiq.x, 0.0), Luma);
yiq.yz *= Chroma;
return float4(saturate(mul(YIQtoRGB, yiq)), 1.0);
}
technique NaturalColors <
ui_label = "Natural Colours";
ui_tooltip = "Warms the picture and lifts saturation for a less washed out look. Ported from the PPSSPP shader.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Natural;
}
}
+90
View File
@@ -0,0 +1,90 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Timer < source = "timer"; > = 0.0;
uniform float Horizon <
ui_type = "slider";
ui_label = "Reflection Line";
ui_tooltip = "Height on screen where the reflective floor begins. Everything above it is left untouched.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
> = 0.55;
uniform float Amount <
ui_type = "slider";
ui_label = "Amount";
ui_tooltip = "How strongly the reflection shows through the floor.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.35;
uniform float Falloff <
ui_type = "slider";
ui_label = "Falloff";
ui_tooltip = "How quickly the reflection fades as the floor comes towards the viewer. At zero it stays even.";
ui_min = 0.0; ui_max = 4.0; ui_step = 0.1;
> = 1.2;
uniform float Perspective <
ui_type = "slider";
ui_label = "Perspective";
ui_tooltip = "Stretches or squashes the mirrored image. One is a true mirror, higher values pull it towards the line.";
ui_min = 0.2; ui_max = 2.0; ui_step = 0.05;
> = 1.0;
uniform float Ripple <
ui_type = "slider";
ui_label = "Ripple";
ui_tooltip = "Amplitude of the waves disturbing the reflection. At zero the mirror is perfectly still.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.0;
uniform float RippleSpeed <
ui_type = "slider";
ui_label = "Ripple Speed";
ui_tooltip = "How fast the waves travel.";
ui_min = 0.0; ui_max = 4.0; ui_step = 0.1;
> = 1.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_Reflections(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 color = tex2D(BackBuffer, uv).rgb;
float depth = uv.y - Horizon;
float on_floor = step(0.0, depth);
float span = max(1.0 - Horizon, 0.001);
float distance_down = saturate(depth / span);
float seconds = Timer * 0.001;
float wave = sin(uv.x * 38.0 + seconds * RippleSpeed * 2.0) *
sin(uv.y * 21.0 - seconds * RippleSpeed * 1.3);
float2 disturbance = float2(wave * 0.004, wave * 0.002) * Ripple * distance_down;
float2 mirrored = float2(uv.x, Horizon - depth * Perspective) + disturbance;
float3 reflection = tex2D(BackBuffer, saturate(mirrored)).rgb;
float fade = pow(max(1.0 - distance_down, 0.0001), Falloff);
float strength = Amount * fade * on_floor;
return float4(lerp(color, reflection, strength), 1.0);
}
technique Reflections <
ui_label = "Reflections";
ui_tooltip = "Mirrors the picture into a reflective floor below a line you choose, with optional water ripples.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Reflections;
}
}
+66
View File
@@ -0,0 +1,66 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Ported from scanlines.fsh in PPSSPP.
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Density <
ui_type = "slider";
ui_label = "Line Density";
ui_tooltip = "Number of scanline pairs across the screen.";
ui_min = 60.0; ui_max = 720.0; ui_step = 10.0;
> = 340.0;
uniform float Intensity <
ui_type = "slider";
ui_label = "Intensity";
ui_tooltip = "How dark the gaps between lines become.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 0.5;
uniform float Tint <
ui_type = "slider";
ui_label = "Phosphor Tint";
ui_tooltip = "Strength of the green-warm phosphor cast.";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.05;
> = 1.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_Scanlines(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float line_pos = uv.y * Density * 0.5;
float gate = cos((frac(line_pos) - 0.5) * 3.1415926 * Intensity) * 1.5;
float3 rgb = tex2D(BackBuffer, uv).rgb;
float3 color = rgb * 0.5 + 0.5 * rgb * rgb * 1.2;
float3 phosphor = lerp(float3(1.0, 1.0, 1.0), float3(0.9, 1.0, 0.7), Tint);
color *= phosphor;
float2 diff = uv - 0.5;
color *= 1.1 - 0.6 * (dot(diff, diff) * 2.0);
return float4(saturate(color * saturate(gate)), 1.0);
}
technique Scanlines <
ui_label = "Scanlines";
ui_tooltip = "Darkens alternating rows of pixels to imitate the horizontal scanlines of a CRT. Cheaper than the full CRT effect when you only want the lines.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Scanlines;
}
}
+43
View File
@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Amount <
ui_type = "slider";
ui_label = "Amount";
ui_min = 0.0; ui_max = 3.0; ui_step = 0.01;
> = 0.6;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_Sharpen(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 texel = float2(BUFFER_RCP_WIDTH, BUFFER_RCP_HEIGHT);
float3 centre = tex2D(BackBuffer, uv).rgb;
float3 blur = tex2D(BackBuffer, uv + float2(-texel.x, 0.0)).rgb;
blur += tex2D(BackBuffer, uv + float2(texel.x, 0.0)).rgb;
blur += tex2D(BackBuffer, uv + float2(0.0, -texel.y)).rgb;
blur += tex2D(BackBuffer, uv + float2(0.0, texel.y)).rgb;
blur *= 0.25;
return float4(centre + (centre - blur) * Amount, 1.0);
}
technique Sharpen <
ui_label = "Sharpen";
ui_tooltip = "Unsharp mask that brings back edge detail lost to scaling. Most useful after upscaling a low internal resolution.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Sharpen;
}
}
+75
View File
@@ -0,0 +1,75 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float ShadowHue <
ui_type = "slider";
ui_label = "Shadow Hue";
ui_min = 0.0; ui_max = 360.0; ui_step = 1.0;
> = 210.0;
uniform float ShadowStrength <
ui_type = "slider";
ui_label = "Shadow Strength";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
> = 0.0;
uniform float HighlightHue <
ui_type = "slider";
ui_label = "Highlight Hue";
ui_min = 0.0; ui_max = 360.0; ui_step = 1.0;
> = 45.0;
uniform float HighlightStrength <
ui_type = "slider";
ui_label = "Highlight Strength";
ui_min = 0.0; ui_max = 1.0; ui_step = 0.01;
> = 0.0;
uniform float Balance <
ui_type = "slider";
ui_label = "Balance";
ui_tooltip = "Moves the split between what counts as shadow and what counts as highlight.";
ui_min = -0.5; ui_max = 0.5; ui_step = 0.01;
> = 0.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float3 HueToRGB(float hue)
{
float h = frac(hue / 360.0) * 6.0;
return saturate(float3(abs(h - 3.0) - 1.0, 2.0 - abs(h - 2.0), 2.0 - abs(h - 4.0)));
}
float4 PS_SplitToning(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 rgb = tex2D(BackBuffer, uv).rgb;
float luma = saturate(dot(rgb, float3(0.2126, 0.7152, 0.0722)) + Balance);
float3 shadow_tint = HueToRGB(ShadowHue) - 0.5;
float3 highlight_tint = HueToRGB(HighlightHue) - 0.5;
rgb += shadow_tint * ShadowStrength * 0.25 * (1.0 - luma);
rgb += highlight_tint * HighlightStrength * 0.25 * luma;
return float4(saturate(rgb), 1.0);
}
technique SplitToning <
ui_label = "Split Toning";
ui_tooltip = "Tints the shadows and the highlights towards two different hues.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_SplitToning;
}
}
+52
View File
@@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2012 PPSSPP Project
// SPDX-License-Identifier: GPL-2.0-or-later
// Ported from vignette.fsh in PPSSPP, by Henrik Rydgard.
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Strength <
ui_type = "slider";
ui_label = "Strength";
ui_tooltip = "How dark the corners become.";
ui_min = 0.0; ui_max = 2.0; ui_step = 0.01;
> = 0.6;
uniform float Aspect <
ui_type = "slider";
ui_label = "Aspect";
ui_min = 0.5; ui_max = 2.0; ui_step = 0.01;
> = 1.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float4 PS_Vignette(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float2 diff = uv - 0.5;
diff.x *= Aspect;
diff.y /= max(Aspect, 0.0001);
float falloff = 1.0 - min(1.0, Strength * dot(diff, diff) * 2.0);
float3 rgb = tex2D(BackBuffer, uv).rgb;
return float4(rgb * falloff, 1.0);
}
technique Vignette <
ui_label = "Vignette";
ui_tooltip = "Darkens the corners of the screen to pull the eye towards the middle, the way a camera lens falls off at its edges.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_Vignette;
}
}
+76
View File
@@ -0,0 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
texture BackBufferTex : COLOR;
sampler BackBuffer { Texture = BackBufferTex; };
uniform float Temperature <
ui_type = "slider";
ui_label = "Temperature";
ui_tooltip = "Negative cools the picture towards blue, positive warms it towards orange.";
ui_min = -100.0; ui_max = 100.0; ui_step = 1.0;
> = 0.0;
uniform float Tint <
ui_type = "slider";
ui_label = "Tint";
ui_tooltip = "Negative shifts towards green, positive towards magenta.";
ui_min = -100.0; ui_max = 100.0; ui_step = 1.0;
> = 0.0;
void VS_PostProcess(in uint id : SV_VertexID, out float4 pos : SV_Position, out float2 uv : TEXCOORD)
{
uv = float2(float(id & 2), float((id & 1) << 1));
pos = float4(uv * float2(2.0, -2.0) + float2(-1.0, 1.0), 0.0, 1.0);
}
float3 WhitePointLMS(float t1, float t2)
{
float shift = 0.05;
if (t1 < 0.0)
{
shift = 0.10;
}
float x = 0.31271 - t1 * shift;
float y = 2.87 * x - 3.0 * x * x - 0.27509507 + t2 * 0.05;
float big_y = 1.0;
float big_x = big_y * x / y;
float big_z = big_y * (1.0 - x - y) / y;
return float3( 0.7328 * big_x + 0.4296 * big_y - 0.1624 * big_z,
-0.7036 * big_x + 1.6975 * big_y + 0.0061 * big_z,
0.0030 * big_x + 0.0136 * big_y + 0.9834 * big_z);
}
float4 PS_WhiteBalance(float4 pos : SV_Position, float2 uv : TEXCOORD) : SV_Target
{
float3 rgb = pow(max(tex2D(BackBuffer, uv).rgb, 0.0), 2.2);
float3 balance = float3(0.949237, 1.03542, 1.08728) /
WhitePointLMS(Temperature / 65.0, Tint / 65.0);
const float3x3 rgb_to_lms = float3x3(0.390405, 0.549941, 0.008926,
0.070841, 0.963172, 0.001358,
0.023108, 0.128021, 0.936245);
const float3x3 lms_to_rgb = float3x3( 2.858470, -1.628790, -0.024891,
-0.210182, 1.158200, 0.000324,
-0.041812, -0.118169, 1.068670);
float3 lms = mul(rgb_to_lms, rgb) * balance;
rgb = mul(lms_to_rgb, lms);
return float4(saturate(pow(max(rgb, 0.0), 1.0 / 2.2)), 1.0);
}
technique WhiteBalance <
ui_label = "White Balance";
ui_tooltip = "Corrects a picture that looks too cool, too warm or tinted.";
>
{
pass
{
VertexShader = VS_PostProcess;
PixelShader = PS_WhiteBalance;
}
}
-17
View File
@@ -12,7 +12,6 @@
- [NetBSD](#netbsd)
- [MSYS2](#msys2)
- [RedoxOS](#redoxos)
- [WebAssembly](#webassembly)
<!-- /TOC -->
## Arch Linux
@@ -243,19 +242,3 @@ find ./*/ -name "*.dll" | while read -r dll; do deps "$dll"; done
The package install may randomly hang at times, in which case it has to be restarted. ALWAYS do a `sudo pkg update` or the chances of it hanging will be close to 90%. If "multiple" installs fail at once, try installing 1 by 1 the packages.
When CMake invokes certain file syscalls - it may sometimes cause crashes or corruptions on the (kernel?) address space - so reboot the system if there is a "hang" in CMake.
## WebAssembly
**It doesn't run on a browser yet.**
WebAssembly or "WASM" for short is a *mainly 32-bit* virtual "architecture" which we only bootstrap on 64-bit only. This means not only the program runs 2x slower than it would due to using JS BigInt, it also means we need to go out of our way to enable proper 64-bit support via `-sMEMORY64=1`, however once again, some Emscripten quirks force us to specify `-s MEMORY64` and `-sMEMORY64=1` at the same time: see the [CI build script](../.ci/wasm/build.sh).
The WebAssembly target is very heavy on resources and requires at least 4 times the normal amount of resources that a native build would. Additionally, the only supported environment is Firefox at the moment, node.js and `wasmtime are not supported (PRs welcome!)
If running under Firefox and you hit "out of memory" on dev console, close the entire tab, then open it back again, see [this issue](https://github.com/emscripten-core/emscripten/issues/8126).
To run the binary (after building) you should be fine with `node ./eden-cli.js`. For obvious reasons no Qt frontend is available on WASM, support for Vulkan is done charily via [llvmpipe2wasm](https://github.com/Devsh-Graphics-Programming/llvmpipe2wasm).
If you run into the error "acorn.js can't be found" check [this associated issue](https://github.com/emscripten-core/emscripten/issues/13368), the fix in short is `npm --global install acorn`. On FreeBSD you could run `npm` under root, or you could do the sane thing and do `sudo chown -R $USER /usr/local/lib/node_modules/ /usr/local/bin/` (remember to restore permissions afterwards!) unless you wish to run `npm` under root which is generally a bad idea.
2026-06-09: As of writing, no Dynarmic-based JIT is possible on this target, full interpreted emulation is the only reasonable option. While there is some efforts on making a JIT like [here](https://github.com/wingo/wasm-jit) or [here](https://wingolog.org/archives/2022/08/18/just-in-time-code-generation-within-webassembly), the result is so latency expensive we're better off using an interpreter instead.
-8
View File
@@ -361,14 +361,6 @@ pkgman install git cmake patch libfmt_devel nlohmann_json lz4_devel opus_devel b
[Caveats](./Caveats.md#haikuos).
</details>
<details>
<summary>WebAssembly</summary>
Emscripten: The default installation should provide enough.
[Caveats](./Caveats.md#wasm).
</details>
<details>
<summary>RedoxOS</summary>
+1 -1
View File
@@ -76,7 +76,7 @@ The following options are desktop only.
- `ENABLE_LIBUSB` (ON) Enable the use of the libusb input backend (HIGHLY RECOMMENDED)
- `ENABLE_OPENGL` (ON) Enable the OpenGL graphics backend
- Unavailable on Windows/ARM64
- Unavailable on Windows/ARM64 and on Android
- You probably shouldn't turn this off.
### Qt
+81 -43
View File
@@ -136,48 +136,49 @@ if(ENABLE_CUBEB)
endif()
endif()
if (NOT ANDROID)
if (NOT YUZU_USE_BUNDLED_SDL3)
if (NOT WIN32)
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
# CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
set(SDL_UNUSED_SUBSYSTEMS
File Filesystem
Locale Power Render)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
set(SDL_${_OPT} OFF)
endforeach()
if (NOT YUZU_USE_BUNDLED_SDL3)
if (NOT WIN32)
# Yuzu itself needs: Atomic Audio Events Joystick Haptic Sensor Threads Timers
# Since 2.0.18 Atomic+Threads required for HIDAPI/libusb (see https://github.com/libsdl-org/SDL/issues/5095)
# Yuzu-cmd also needs: Video (depends on Loadso/Dlopen)
# CPUinfo also required for SDL Audio, at least until 2.28.0 (see https://github.com/libsdl-org/SDL/issues/7809)
set(SDL_UNUSED_SUBSYSTEMS
File Filesystem
Locale Power Render)
foreach(_SUB ${SDL_UNUSED_SUBSYSTEMS})
string(TOUPPER ${_SUB} _OPT)
set(SDL_${_OPT} OFF)
endforeach()
set(HIDAPI ON)
endif()
if (APPLE)
set(SDL_FILE ON)
endif()
AddJsonPackage(sdl3)
else()
message(STATUS "Using bundled SDL3")
AddJsonPackage(sdl3-ci)
set(HIDAPI ON)
endif()
# Normalize SDL3 link target across package variants.
# Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static.
if (NOT TARGET SDL3::SDL3)
if (TARGET SDL3::SDL3-shared)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared)
elseif (TARGET SDL3::SDL3-static)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static)
else()
message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported")
endif()
if (APPLE)
set(SDL_FILE ON)
endif()
AddJsonPackage(sdl3)
else()
message(STATUS "Using bundled SDL3")
AddJsonPackage(sdl3-ci)
# copy sdl java sources for consumption by Gradle
if (ANDROID)
file(COPY ${SDL3_SOURCE_DIR}/java DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/generated/sdl)
endif()
endif()
set(BUILD_SHARED_LIBS OFF)
# Normalize SDL3 link target across package variants.
# Some SDL3 packages export only SDL3::SDL3-shared or SDL3::SDL3-static.
if (NOT TARGET SDL3::SDL3)
if (TARGET SDL3::SDL3-shared)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-shared)
elseif (TARGET SDL3::SDL3-static)
add_library(SDL3::SDL3 ALIAS SDL3::SDL3-static)
else()
message(FATAL_ERROR "SDL3 package found, but no usable SDL3 target was exported")
endif()
endif()
# SPIRV Headers
AddJsonPackage(spirv-headers)
@@ -194,6 +195,50 @@ else()
endif()
endif()
# reshadefx
if (ENABLE_RESHADE)
AddJsonPackage(NAME reshade DOWNLOAD_ONLY)
set(RESHADEFX_SHIM_DIR ${CMAKE_CURRENT_BINARY_DIR}/reshadefx_shim)
file(WRITE ${RESHADEFX_SHIM_DIR}/spirv.hpp "#include <spirv/unified1/spirv.hpp>\n")
file(WRITE ${RESHADEFX_SHIM_DIR}/GLSL.std.450.h "#include <spirv/unified1/GLSL.std.450.h>\n")
if (APPLE)
file(WRITE ${RESHADEFX_SHIM_DIR}/malloc.h "#include <stdlib.h>\n#include <alloca.h>\n")
endif()
add_library(reshadefx STATIC
${reshade_SOURCE_DIR}/source/effect_codegen_spirv.cpp
${reshade_SOURCE_DIR}/source/effect_expression.cpp
${reshade_SOURCE_DIR}/source/effect_lexer.cpp
${reshade_SOURCE_DIR}/source/effect_parser_exp.cpp
${reshade_SOURCE_DIR}/source/effect_parser_stmt.cpp
${reshade_SOURCE_DIR}/source/effect_preprocessor.cpp
${reshade_SOURCE_DIR}/source/effect_symbol_table.cpp
)
target_include_directories(reshadefx SYSTEM PUBLIC ${reshade_SOURCE_DIR}/source)
target_include_directories(reshadefx PRIVATE ${RESHADEFX_SHIM_DIR})
target_link_libraries(reshadefx PUBLIC SPIRV-Headers::SPIRV-Headers)
if (NOT MSVC)
target_compile_options(reshadefx PRIVATE -w -fno-char8_t)
else()
target_compile_options(reshadefx PRIVATE /w /Zc:char8_t-)
endif()
if (WIN32)
if (NOT MSVC)
target_compile_options(reshadefx PRIVATE -include share.h)
else()
target_compile_options(reshadefx PRIVATE /FIshare.h)
endif()
endif()
if (NOT TARGET reshadefx::reshadefx)
add_library(reshadefx::reshadefx ALIAS reshadefx)
endif()
endif()
# Catch2
if (YUZU_TESTS OR DYNARMIC_TESTS)
AddJsonPackage(catch2)
@@ -385,13 +430,6 @@ if (YUZU_CRASH_DUMPS AND NOT TARGET libbreakpad_client)
endif()
endif()
# oboe
if (ANDROID)
AddJsonPackage(oboe)
add_library(oboe::oboe ALIAS oboe)
endif()
if (APPLE)
# moltenvk
if (NOT YUZU_USE_BUNDLED_MOLTENVK)
+5 -9
View File
@@ -94,10 +94,6 @@ function(detect_architecture_symbols)
endfunction()
# arches here are put in a sane default order of importance
# EXCEPT FOR WASM, which must be probed for FIRST, because some genius
# decided to also allow the host architecture to be defined when building
# for the emscripten target, absolutely lovely detail.
#
# notably, amd64, arm64, and riscv (in order) are BY FAR the most common
# mips is pretty popular in embedded
# ppc64 is pretty popular in supercomputing
@@ -105,11 +101,6 @@ endfunction()
# ia64 exists
# the rest exist, but are probably less popular than ia64
detect_architecture_symbols(
ARCH wasm
SYMBOLS
"__EMSCRIPTEN__")
detect_architecture_symbols(
ARCH arm64
SYMBOLS
@@ -212,6 +203,11 @@ detect_architecture_symbols(
"__loongarch__"
"__loongarch64")
detect_architecture_symbols(
ARCH wasm
SYMBOLS
"__EMSCRIPTEN__")
# "generic" target
# If you have reached this point, you're on some as-of-yet unsupported architecture.
# See the docs up above for known unsupported architectures
-25
View File
@@ -20,9 +20,6 @@ elseif (${CMAKE_SYSTEM_NAME} STREQUAL "managarm")
set(MANAGARM ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Haiku")
set(HAIKUOS ON)
elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
set(EMSCRIPTEN ON)
message(WARNING "${CMAKE_LIBRARY_ARCHITECTURE} support is highly experimental!!!")
endif()
# BSD
@@ -100,25 +97,3 @@ if (MINGW)
set(CMAKE_EXE_LINKER_FLAGS_RELEASE
"${CMAKE_EXE_LINKER_FLAGS_RELEASE} ${MINGW_FLAGS}")
endif()
if (EMSCRIPTEN)
set(EMSCRIPTEN_C_FLAGS "-s MEMORY64 -m64 -pipe -sMEMORY64=1")
set(EMSCRIPTEN_LINK_FLAGS "-sMEMORY64=1 -m64 -Wl,-mwasm64 -sASYNCIFY=1")
# This prevents FFmpeg and other libraries from assuming it's the host's CPU
# Additionally some Emscripten installs may not be very good... generally
set(EMSCRIPTEN_SYSTEM_PROCESSOR wasm)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_C_FLAGS}")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} ${EMSCRIPTEN_C_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} ${EMSCRIPTEN_LINK_FLAGS}")
unset(EMSCRIPTEN_C_FLAGS)
unset(EMSCRIPTEN_LINK_FLAGS)
endif()
# awesome
if (FREEBSD OR DRAGONFLYBSD)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${CMAKE_SYSROOT}/usr/local/lib")
endif()
+19 -54
View File
@@ -35,37 +35,26 @@ if (NOT YUZU_USE_BUNDLED_FFMPEG)
elseif (NOT (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES CMAKE_SYSTEM_PROCESSOR
AND CMAKE_HOST_SYSTEM_NAME MATCHES CMAKE_SYSTEM_NAME))
string(TOLOWER "${CMAKE_SYSTEM_NAME}" FFmpeg_SYSTEM_NAME)
# All of these platforms are supported by ffmpeg as native build OSes
# anything else (like Redox or Managarm or PS4) is NOT natively supported
# hence, assume the "unix like" is just "none" for the sake of OUR sanity.
# If YOUR OS/platform has actual native support:
# 1. fucking congrats
# 2. feel free to add it on the condition below
if (NOT (NETBSD OR SOLARIS OR FREEBSD OR OPENBSD OR DRAGONFLYBSD OR HAIKU
OR LINUX OR MSYS OR WIN32 OR ANDROID OR APPLE))
if (FFmpeg_SYSTEM_NAME STREQUAL "openorbis" OR FFmpeg_SYSTEM_NAME STREQUAL "managarm")
set(FFmpeg_SYSTEM_NAME "none")
endif()
# TODO: Can we really do better? Auto-detection? Something clever?
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--enable-cross-compile
--arch="${CMAKE_SYSTEM_PROCESSOR}"
--target-os="${FFmpeg_SYSTEM_NAME}")
# Emscripten doesn't use CMAKE_SYSROOT I'm afraid, but it does well use EMSCRIPTEN_SYSROOT
if (EMSCRIPTEN)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --sysroot="${EMSCRIPTEN_SYSROOT}")
else()
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --sysroot="${CMAKE_SYSROOT}")
endif()
--target-os="${FFmpeg_SYSTEM_NAME}"
--sysroot="${CMAKE_SYSROOT}"
)
if (DEFINED FFmpeg_CROSS_PREFIX)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --cross-prefix="${FFmpeg_CROSS_PREFIX}")
else()
message(WARNING "Please set FFmpeg_CROSS_PREFIX to your cross toolchain prefix, for example: ${CMAKE_STAGING_PREFIX}/bin/${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}")
message(WARNING "Please set FFmpeg_CROSS_PREFIX to your cross toolchain prefix, for example: \${CMAKE_STAGING_PREFIX}/bin/${CMAKE_SYSTEM_PROCESSOR}-${CMAKE_SYSTEM_NAME}-")
endif()
set(FFmpeg_IS_CROSS_COMPILING TRUE)
endif()
endif()
if (OPENORBIS OR MANAGARM OR EMSCRIPTEN)
if (OPENORBIS OR MANAGARM)
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
elseif (ANDROID)
@@ -173,7 +162,8 @@ if (OPENORBIS)
-lSceUserService
-lSceSysmodule
-lSceNet
-lSceLibcInternal)
-lSceLibcInternal
)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--disable-pthreads
--extra-cflags=${CMAKE_SYSROOT}/usr/include
@@ -184,18 +174,8 @@ elseif (MANAGARM)
# Required for proper stuff
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS
--disable-pthreads
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}")
endif()
# Usually used by Emscripten
if (DEFINED CMAKE_AR)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --ar=${CMAKE_AR})
endif()
if (DEFINED CMAKE_NM)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --nm=${CMAKE_NM})
endif()
if (DEFINED CMAKE_RANLIB)
list(APPEND FFmpeg_CROSS_COMPILE_FLAGS --ranlib=${CMAKE_RANLIB})
--extra-libs="${FFmpeg_CROSS_COMPILE_LIBS}"
)
endif()
if (YUZU_USE_BUNDLED_FFMPEG)
@@ -225,7 +205,6 @@ else()
# Build FFmpeg from externals
message(STATUS "Using FFmpeg from externals")
set(FFmpeg_LD ${CMAKE_LINKER})
if (CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64|amd64)")
# FFmpeg has source that requires one of nasm or yasm to assemble it.
# REQUIRED throws an error if not found here during configuration rather than during compilation.
@@ -234,12 +213,6 @@ else()
message(FATAL_ERROR "One of either `nasm` or `yasm` not found but is required.")
endif()
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Emscripten")
# TODO: this is 100% redundant but we need it because CMAKE_LINKER can resolve to ld.lld
# which is NOT compatible
find_program(EMCC NAMES emcc REQUIRED)
set(FFmpeg_LD ${EMCC})
endif()
find_program(AUTOCONF autoconf)
if ("${AUTOCONF}" STREQUAL "AUTOCONF-NOTFOUND")
@@ -273,13 +246,7 @@ else()
CACHE PATH "Paths to FFmpeg libraries" FORCE)
endforeach()
# Some SDKs (especially wasm) require us, actually, WANT us to use their "emconfigure"
# otherwise they will cry a billion tears
if (EMSCRIPTEN)
find_program(FFmpeg_CONFIGURE_WRAPPER emconfigure REQUIRED)
else()
find_program(FFmpeg_CONFIGURE_WRAPPER bash REQUIRED)
endif()
find_program(BASH_PROGRAM bash REQUIRED)
# `configure` parameters builds only exactly what yuzu needs from FFmpeg
# `--disable-vdpau` is needed to avoid linking issues
@@ -289,7 +256,7 @@ else()
OUTPUT
${FFmpeg_MAKEFILE}
COMMAND
${FFmpeg_CONFIGURE_WRAPPER} ${FFmpeg_PREFIX}/configure
${BASH_PROGRAM} ${FFmpeg_PREFIX}/configure
--disable-avdevice
--disable-avformat
--disable-doc
@@ -298,10 +265,6 @@ else()
--disable-ffprobe
--disable-network
--disable-swresample
--disable-autodetect
--disable-runtime-cpudetect
--disable-debug
--disable-programs
--enable-decoder=h264
--enable-decoder=vp8
--enable-decoder=vp9
@@ -309,7 +272,7 @@ else()
--enable-pic
--cc=${FFmpeg_CC}
--cxx=${FFmpeg_CXX}
--ld=${FFmpeg_LD}
--ld=${CMAKE_LINKER}
--extra-cflags=${CMAKE_C_FLAGS}
--extra-cxxflags=${CMAKE_CXX_FLAGS}
--extra-ldflags=${CMAKE_C_LINK_FLAGS}
@@ -325,7 +288,11 @@ else()
# Workaround for Ubuntu 18.04's older version of make not being able to call make as a child
# with context of the jobserver. Also helps ninja users.
cmake_host_system_information(RESULT SYSTEM_THREADS QUERY NUMBER_OF_LOGICAL_CORES)
execute_process(
COMMAND
nproc
OUTPUT_VARIABLE
SYSTEM_THREADS)
set(FFmpeg_BUILD_LIBRARIES ${FFmpeg_LIBRARIES})
@@ -333,8 +300,6 @@ else()
if (LINUX OR ANDROID OR APPLE OR WIN32 OR FREEBSD)
set(FFmpeg_MAKE_ARGS -j${SYSTEM_THREADS})
else()
# No GNU make implies that this system may be highly non-GNU
find_program(MAKE make required)
set(FFmpeg_MAKE_ARGS "")
endif()
@@ -342,7 +307,7 @@ else()
OUTPUT
${FFmpeg_BUILD_LIBRARIES}
COMMAND
${MAKE} ${FFmpeg_MAKE_ARGS}
gmake ${FFmpeg_MAKE_ARGS}
WORKING_DIRECTORY
${FFmpeg_BUILD_DIR}
)
-2
View File
@@ -14,9 +14,7 @@
namespace Tz {
namespace {
#ifndef EINVAL
#define EINVAL 22
#endif
static Rule gmtmem{};
static Rule* const gmtptr = &gmtmem;
+1
View File
@@ -11,6 +11,7 @@
#include <limits>
#include <span>
#include <array>
#include <algorithm>
#include <time.h>
namespace Tz {
+2 -5
View File
@@ -177,6 +177,7 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-invalid-offsetof>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-unused-parameter>
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-missing-field-initializers>)
if (CXX_CLANG OR CXX_ICC OR CXX_APPLE) # Clang, AppleClang, or Intel C++
if (NOT MSVC)
add_compile_options(
@@ -190,11 +191,7 @@ else()
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-nullability-completeness>)
endif()
if (ARCHITECTURE_wasm)
# we are evil but fmt is even more evil
add_compile_options(
$<$<COMPILE_LANGUAGE:C,CXX>:-Wno-shorten-64-to-32>)
elseif (ARCHITECTURE_x86_64)
if (ARCHITECTURE_x86_64)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mcx16>)
if (LINUX OR FREEBSD)
add_compile_options($<$<COMPILE_LANGUAGE:C,CXX>:-mtls-dialect=gnu2>)
+7
View File
@@ -272,6 +272,13 @@ android {
resValue("string", "app_name_suffixed", "$currentName$suffix")
resValue("string", "app_name", "Eden$suffix")
}
sourceSets {
named("main") {
java.srcDir("${edenDir}/externals/generated/sdl/java")
kotlin.srcDir("${edenDir}/externals/generated/sdl/java")
}
}
}
idea {
+3
View File
@@ -9,6 +9,9 @@
-keep class org.ini4j.spi.IniBuilder
-keep class org.ini4j.spi.IniFormatter
-keep class org.libsdl.app.** { *; }
-keepclassmembers class org.libsdl.app.** { *; }
# Suppress warnings for R8
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
@@ -16,6 +16,7 @@ import android.widget.TextView
import androidx.annotation.Keep
import androidx.core.net.toUri
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.libsdl.app.SDL
import java.lang.ref.WeakReference
import org.yuzu.yuzu_emu.activities.EmulationActivity
import org.yuzu.yuzu_emu.fragments.CoreErrorDialogFragment
@@ -52,6 +53,8 @@ object NativeLibrary {
init {
try {
System.loadLibrary("yuzu-android")
SDL.setupJNI()
initJvm()
} catch (ex: UnsatisfiedLinkError) {
error("[NativeLibrary] $ex")
}
@@ -367,6 +370,7 @@ object NativeLibrary {
NetPlayManager.clearChat()
}
external fun initJvm()
external fun initMultiplayer()
@Keep
@@ -3,10 +3,13 @@
package org.yuzu.yuzu_emu.dialogs
import android.content.res.ColorStateList
import android.graphics.Rect
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RadioGroup
import android.widget.TextView
import androidx.drawerlayout.widget.DrawerLayout
@@ -18,11 +21,28 @@ import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.fragments.EmulationFragment
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NativePostProcessing
import org.yuzu.yuzu_emu.features.settings.model.AbstractSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractShortSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
class QuickSettings(val emulationFragment: EmulationFragment) {
private val expandedShaders = mutableSetOf<Int>()
private fun forgetShaderSlot(index: Int) {
val shifted = mutableSetOf<Int>()
for (slot in expandedShaders) {
if (slot < index) {
shifted.add(slot)
}
if (slot > index) {
shifted.add(slot - 1)
}
}
expandedShaders.clear()
expandedShaders.addAll(shifted)
}
private fun saveSettings() {
if (emulationFragment.shouldUseCustom) {
NativeConfig.savePerGameConfig()
@@ -232,6 +252,589 @@ class QuickSettings(val emulationFragment: EmulationFragment) {
container.addView(itemView)
}
fun addChoice(
title: String,
container: ViewGroup,
choices: List<String>,
selectedIndex: Int,
onSelected: (Int) -> Unit
) {
val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
val headerView = itemView.findViewById<ViewGroup>(R.id.setting_header)
val titleView = itemView.findViewById<TextView>(R.id.setting_title)
val valueView = itemView.findViewById<TextView>(R.id.setting_value)
val expandIcon = itemView.findViewById<android.widget.ImageView>(R.id.expand_icon)
val radioGroup = itemView.findViewById<RadioGroup>(R.id.radio_group)
titleView.text = title
var current = ""
if (selectedIndex in choices.indices) {
current = choices[selectedIndex]
}
valueView.text = current
headerView.visibility = View.VISIBLE
var isExpanded = false
choices.forEachIndexed { index, name ->
val radioButton = com.google.android.material.radiobutton.MaterialRadioButton(
emulationFragment.requireContext()
)
radioButton.text = name
radioButton.id = View.generateViewId()
radioButton.isChecked = index == selectedIndex
radioButton.setPadding(16, 8, 16, 8)
radioButton.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
valueView.text = name
onSelected(index)
}
}
radioGroup.addView(radioButton)
}
headerView.setOnClickListener {
isExpanded = !isExpanded
if (isExpanded) {
radioGroup.visibility = View.VISIBLE
expandIcon.animate().rotation(180f).setDuration(200).start()
} else {
radioGroup.visibility = View.GONE
expandIcon.animate().rotation(0f).setDuration(200).start()
}
}
container.addView(itemView)
}
fun addStepSlider(
title: String,
container: ViewGroup,
steps: Int,
selectedStep: Int,
describe: (Int) -> String,
onCommitted: (Int) -> Unit,
onChanged: (Int) -> Unit
) {
val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_menu, container, false)
val sliderContainer = itemView.findViewById<ViewGroup>(R.id.slider_container)
val titleView = itemView.findViewById<TextView>(R.id.slider_title)
val valueDisplay = itemView.findViewById<TextView>(R.id.slider_value_display)
val slider = itemView.findViewById<com.google.android.material.slider.Slider>(
R.id.setting_slider
)
titleView.text = title
sliderContainer.visibility = View.VISIBLE
slider.valueFrom = 0f
slider.valueTo = steps.toFloat()
slider.stepSize = 1f
slider.value = selectedStep.toFloat().coerceIn(0f, steps.toFloat())
valueDisplay.text = describe(slider.value.toInt())
slider.addOnChangeListener { _, value, fromUser ->
if (fromUser) {
val step = value.toInt()
onChanged(step)
valueDisplay.text = describe(step)
}
}
var pressedValue = slider.value
slider.setOnTouchListener { _, event ->
val drawer = emulationFragment.view?.findViewById<DrawerLayout>(R.id.drawer_layout)
when (event.action) {
MotionEvent.ACTION_DOWN -> {
drawer?.requestDisallowInterceptTouchEvent(true)
pressedValue = slider.value
}
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
drawer?.requestDisallowInterceptTouchEvent(false)
if (slider.value != pressedValue) {
onCommitted(slider.value.toInt())
}
}
}
false
}
container.addView(itemView)
}
fun addShaderCard(
index: Int,
title: String,
summary: String,
container: ViewGroup,
onRemove: () -> Unit
): ViewGroup {
val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_shader, container, false)
val headerView = itemView.findViewById<ViewGroup>(R.id.shader_header)
val titleView = itemView.findViewById<TextView>(R.id.shader_title)
val summaryView = itemView.findViewById<TextView>(R.id.shader_summary)
val removeView = itemView.findViewById<android.widget.ImageView>(R.id.shader_remove)
val expandIcon = itemView.findViewById<android.widget.ImageView>(R.id.shader_expand)
val bodyView = itemView.findViewById<ViewGroup>(R.id.shader_body)
titleView.text = title
if (summary.isEmpty()) {
summaryView.visibility = View.GONE
} else {
summaryView.text = summary
}
var isExpanded = expandedShaders.contains(index)
if (isExpanded) {
bodyView.visibility = View.VISIBLE
expandIcon.rotation = 180f
}
headerView.setOnClickListener {
isExpanded = !isExpanded
if (isExpanded) {
expandedShaders.add(index)
bodyView.visibility = View.VISIBLE
expandIcon.animate().rotation(180f).setDuration(200).start()
} else {
expandedShaders.remove(index)
bodyView.visibility = View.GONE
expandIcon.animate().rotation(0f).setDuration(200).start()
}
}
removeView.setOnClickListener {
onRemove()
}
container.addView(itemView)
return bodyView
}
fun addEffectPicker(
container: ViewGroup,
choices: List<String>,
hasEffects: Boolean,
onRemoveAll: () -> Unit,
onPicked: (Int) -> Unit
) {
val context = emulationFragment.requireContext()
val inflater = LayoutInflater.from(context)
val itemView = inflater.inflate(R.layout.item_quick_settings_add, container, false)
val button = itemView.findViewById<com.google.android.material.button.MaterialButton>(
R.id.add_button
)
val removeButton =
itemView.findViewById<com.google.android.material.button.MaterialButton>(
R.id.remove_all_button
)
val choiceGroup = itemView.findViewById<RadioGroup>(R.id.add_choices)
choices.forEachIndexed { index, name ->
val radioButton = com.google.android.material.radiobutton.MaterialRadioButton(context)
radioButton.text = name
radioButton.id = View.generateViewId()
radioButton.setPadding(16, 8, 16, 8)
radioButton.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
onPicked(index)
}
}
choiceGroup.addView(radioButton)
}
var closedLabel = R.string.post_processing_add
if (hasEffects) {
closedLabel = R.string.post_processing_open_list
removeButton.visibility = View.VISIBLE
}
button.setText(closedLabel)
val removeBackground = MaterialColors.getColor(
removeButton,
com.google.android.material.R.attr.colorErrorContainer
)
val removeForeground = MaterialColors.getColor(
removeButton,
com.google.android.material.R.attr.colorOnErrorContainer
)
removeButton.backgroundTintList = ColorStateList.valueOf(removeBackground)
removeButton.setTextColor(removeForeground)
removeButton.iconTint = ColorStateList.valueOf(removeForeground)
removeButton.setOnClickListener {
onRemoveAll()
}
val slide = context.resources.displayMetrics.density * 24.0f
var isOpen = false
button.setOnClickListener {
isOpen = !isOpen
if (isOpen) {
choiceGroup.alpha = 0.0f
choiceGroup.translationY = slide
choiceGroup.visibility = View.VISIBLE
choiceGroup.animate()
.alpha(1.0f)
.translationY(0.0f)
.setDuration(220)
.withEndAction {
choiceGroup.requestRectangleOnScreen(
Rect(0, 0, choiceGroup.width, choiceGroup.height),
false
)
}
.start()
button.setText(R.string.post_processing_close_list)
button.setIconResource(R.drawable.ic_clear)
} else {
choiceGroup.animate()
.alpha(0.0f)
.translationY(slide)
.setDuration(160)
.withEndAction {
choiceGroup.visibility = View.GONE
}
.start()
button.setText(closedLabel)
button.setIconResource(R.drawable.ic_add)
}
}
container.addView(itemView)
}
fun addPresetBand(container: ViewGroup, name: String, summary: String) {
val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_preset, container, false)
val titleView = itemView.findViewById<TextView>(R.id.preset_title)
val summaryView = itemView.findViewById<TextView>(R.id.preset_summary)
val switchView = itemView.findViewById<MaterialSwitch>(R.id.preset_switch)
titleView.text = name
if (summary.isEmpty()) {
summaryView.visibility = View.GONE
} else {
summaryView.text = summary
}
switchView.isChecked = NativePostProcessing.isEnabled()
switchView.setOnCheckedChangeListener { _, checked ->
emulationFragment.editPostProcessing {
NativePostProcessing.setEnabled(checked)
}
}
container.addView(itemView)
}
fun addPostProcessing(container: ViewGroup, onStructureChanged: () -> Unit) {
val usable = NativePostProcessing.catalog().filter { it.valid }
if (usable.isEmpty()) {
return
}
val preset = NativePostProcessing.getActivePreset()
if (preset.isNotEmpty()) {
addDivider(container)
var summary = ""
val described = NativePostProcessing.presets().firstOrNull { it.name == preset }
if (described != null) {
summary = described.description
}
if (summary.isEmpty()) {
summary =
YuzuApplication.appContext.getString(R.string.post_processing_preset_locked)
}
if (NativePostProcessing.isPresetModified()) {
summary = summary + "\n" +
YuzuApplication.appContext.getString(R.string.post_processing_preset_modified)
}
addPresetBand(container, preset, summary)
return
}
val labels = mutableListOf<String>()
val files = mutableListOf<String>()
val techniques = mutableListOf<String>()
for (effect in usable) {
for (technique in effect.techniques) {
var label = effect.label
if (effect.techniques.size > 1) {
label = effect.label + " \u00b7 " + technique
}
labels.add(label)
files.add(effect.file)
techniques.add(technique)
}
}
addDivider(container)
val chain = NativePostProcessing.chain()
chain.forEachIndexed { index, entry ->
val effect = usable.firstOrNull { it.file == entry.file }
var title = entry.file
var summary = ""
if (effect != null) {
title = effect.label
if (effect.techniques.size > 1) {
title = effect.label + " \u00b7 " + entry.technique
}
summary = effect.description
}
val body = addShaderCard(index, title, summary, container) {
emulationFragment.editPostProcessing {
NativePostProcessing.remove(index)
}
forgetShaderSlot(index)
onStructureChanged()
}
if (effect != null) {
for (uniform in effect.uniforms) {
addUniformSliders(body, index, uniform)
}
}
}
addEffectPicker(
container,
labels,
chain.isNotEmpty(),
{
emulationFragment.editPostProcessing {
NativePostProcessing.clearChain()
}
expandedShaders.clear()
onStructureChanged()
}
) { picked ->
emulationFragment.editPostProcessing {
NativePostProcessing.append(files[picked], techniques[picked])
}
onStructureChanged()
}
}
private fun addUniformSliders(
container: ViewGroup,
index: Int,
uniform: NativePostProcessing.Uniform
) {
if (uniform.uiType == NativePostProcessing.UI_HIDDEN) {
return
}
for (component in 0 until uniform.components) {
var title = uniform.label
if (uniform.components > 1) {
title = uniform.label + " [" + component + "]"
}
var value = uniform.defaultAt(component)
if (NativePostProcessing.hasValue(index, uniform.name)) {
value = NativePostProcessing.getValue(index, uniform.name, component)
}
val steps = uniform.steps
val step = Math.round((value - uniform.min) / uniform.step)
addStepSlider(
title,
container,
steps,
step,
{ position -> uniform.describe(position) },
{ emulationFragment.persistPostProcessing() }
) { position ->
NativePostProcessing.setValue(
index,
uniform.name,
component,
uniform.min + position * uniform.step
)
}
}
}
private fun addFrameGenCell(inflater: LayoutInflater, row: ViewGroup): TextView {
val cell = inflater.inflate(R.layout.item_quick_settings_frame_gen_cell, row, false)
as TextView
row.addView(cell)
return cell
}
fun addFrameGen(container: ViewGroup) {
val inflater = LayoutInflater.from(emulationFragment.requireContext())
val itemView = inflater.inflate(R.layout.item_quick_settings_frame_gen, container, false)
val multiplierRow = itemView.findViewById<LinearLayout>(R.id.frame_gen_multipliers)
val targetRow = itemView.findViewById<LinearLayout>(R.id.frame_gen_targets)
val targetSection = itemView.findViewById<ViewGroup>(R.id.frame_gen_target_section)
val flowRow = itemView.findViewById<LinearLayout>(R.id.frame_gen_flow)
val flowSection = itemView.findViewById<ViewGroup>(R.id.frame_gen_flow_section)
val multiplierNames =
emulationFragment.resources.getStringArray(R.array.frameGenMultiplierNames)
val multiplierValues =
emulationFragment.resources.getIntArray(R.array.frameGenMultiplierValues)
val targetValues =
emulationFragment.resources.getIntArray(R.array.frameGenTargetRateValues)
val flowValues =
emulationFragment.resources.getIntArray(R.array.frameGenFlowScaleValues)
val columns = maxOf(
multiplierValues.size + 1,
targetValues.size,
flowValues.size + 1
).toFloat()
multiplierRow.weightSum = columns
targetRow.weightSum = columns
flowRow.weightSum = columns
val powerCell = addFrameGenCell(inflater, multiplierRow)
val multiplierCells = mutableListOf<TextView>()
for (name in multiplierNames) {
val cell = addFrameGenCell(inflater, multiplierRow)
cell.text = name
multiplierCells.add(cell)
}
val targetCells = mutableListOf<TextView>()
for (value in targetValues) {
val cell = addFrameGenCell(inflater, targetRow)
if (value == 0) {
cell.setText(R.string.frame_gen_fixed)
} else {
cell.text = value.toString()
}
targetCells.add(cell)
}
val autoCell = addFrameGenCell(inflater, flowRow)
autoCell.setText(R.string.frame_gen_flow_auto)
val flowCells = mutableListOf<TextView>()
for (value in flowValues) {
val cell = addFrameGenCell(inflater, flowRow)
cell.text = "$value%"
flowCells.add(cell)
}
val inactiveAlpha = 0.38f
fun refresh() {
val enabled = BooleanSetting.RENDERER_FRAME_GEN.getBoolean(needsGlobal = false)
val multiplier = IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.getInt(needsGlobal = false)
val target = IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.getInt(needsGlobal = false)
val fixed = target == 0
val flowAuto =
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.getBoolean(needsGlobal = false)
val flowScale = IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.getInt(needsGlobal = false)
var powerLabel = R.string.frame_gen_off
var rowAlpha = inactiveAlpha
if (enabled) {
powerLabel = R.string.frame_gen_on
rowAlpha = 1.0f
}
var multiplierAlpha = inactiveAlpha
if (enabled && fixed) {
multiplierAlpha = 1.0f
}
powerCell.setText(powerLabel)
powerCell.isSelected = enabled
multiplierCells.forEachIndexed { index, cell ->
cell.isEnabled = enabled
cell.isSelected = enabled && fixed && multiplierValues[index] == multiplier
cell.alpha = multiplierAlpha
}
targetSection.alpha = rowAlpha
targetCells.forEachIndexed { index, cell ->
cell.isEnabled = enabled
cell.isSelected = enabled && targetValues[index] == target
}
flowSection.alpha = rowAlpha
autoCell.isEnabled = enabled
autoCell.isSelected = enabled && flowAuto
flowCells.forEachIndexed { index, cell ->
cell.isEnabled = enabled
cell.isSelected = enabled && !flowAuto && flowValues[index] == flowScale
}
}
powerCell.setOnClickListener {
if (BooleanSetting.RENDERER_FRAME_GEN.getBoolean(needsGlobal = false)) {
BooleanSetting.RENDERER_FRAME_GEN.setBoolean(false)
} else {
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.setInt(multiplierValues.first())
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(0)
BooleanSetting.RENDERER_FRAME_GEN.setBoolean(true)
}
saveSettings()
refresh()
}
multiplierCells.forEachIndexed { index, cell ->
cell.setOnClickListener {
IntSetting.RENDERER_FRAME_GEN_MULTIPLIER.setInt(multiplierValues[index])
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(0)
saveSettings()
refresh()
}
}
targetCells.forEachIndexed { index, cell ->
cell.setOnClickListener {
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.setInt(targetValues[index])
saveSettings()
refresh()
}
}
autoCell.setOnClickListener {
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.setBoolean(true)
saveSettings()
refresh()
}
flowCells.forEachIndexed { index, cell ->
cell.setOnClickListener {
IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.setInt(flowValues[index])
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.setBoolean(false)
saveSettings()
refresh()
}
}
refresh()
container.addView(itemView)
}
fun addDivider(container: ViewGroup) {
val inflater = LayoutInflater.from(emulationFragment.requireContext())
val dividerView = inflater.inflate(R.layout.item_quick_settings_divider, container, false)
@@ -38,9 +38,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_VERTEX_INPUT_DYNAMIC_STATE("vertex_input_dynamic_state"),
RENDERER_SAMPLE_SHADING("sample_shading"),
RENDERER_FRAME_GEN("frame_gen"),
RENDERER_FRAME_GEN_FP16("frame_gen_fp16"),
RENDERER_FRAME_GEN_FLOW_SCALE_AUTO("frame_gen_flow_scale_auto"),
RENDERER_FRAME_GEN_DUMP_FLOW("frame_gen_dump_flow"),
GPU_UNSWIZZLE_ENABLED("gpu_unswizzle_enabled"),
PICTURE_IN_PICTURE("picture_in_picture"),
USE_CUSTOM_RTC("custom_rtc_enabled"),
@@ -83,6 +81,7 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
SHOW_SHADERS_BUILDING("show_shaders_building"),
DEBUG_FLUSH_BY_LINE("flush_line"),
EXTENDED_LOGGING("extended_logging"),
DONT_SHOW_DRIVER_SHADER_WARNING("dont_show_driver_shader_warning"),
ENABLE_OVERLAY("enable_overlay"),
@@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
class FxPresetNameSetting(private val onNamed: (String) -> Unit) : AbstractStringSetting {
override val key: String
get() = "fx_preset_name"
override val defaultValue: Any
get() = ""
override val isRuntimeModifiable: Boolean
get() = true
override val pairedSettingKey: String
get() = ""
override val isSwitchable: Boolean
get() = false
override val isSaveable: Boolean
get() = true
override var global: Boolean
get() = true
set(_) {}
override fun getString(needsGlobal: Boolean): String = ""
override fun setString(value: String) = onNamed(value)
override fun getValueAsString(needsGlobal: Boolean): String = ""
override fun reset() {}
}
@@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model
import org.yuzu.yuzu_emu.utils.NativePostProcessing
import kotlin.math.roundToInt
abstract class FxUniformSetting(
protected val index: Int,
protected val uniform: NativePostProcessing.Uniform,
protected val component: Int
) : AbstractSetting {
override val key: String
get() = "fx_${index}_${uniform.name}_$component"
override val isRuntimeModifiable: Boolean
get() = true
override val pairedSettingKey: String
get() = ""
override val isSwitchable: Boolean
get() = false
override val isSaveable: Boolean
get() = true
override var global: Boolean
get() = true
set(_) {}
protected fun currentValue(): Float {
if (NativePostProcessing.hasValue(index, uniform.name)) {
return NativePostProcessing.getValue(index, uniform.name, component)
}
return uniform.defaultAt(component)
}
protected fun commit(value: Float) {
NativePostProcessing.setValue(index, uniform.name, component, value)
NativePostProcessing.store()
}
override fun reset() = commit(uniform.defaultAt(component))
}
class FxUniformSliderSetting(
index: Int,
uniform: NativePostProcessing.Uniform,
component: Int
) : FxUniformSetting(index, uniform, component), AbstractIntSetting {
override val defaultValue: Any
get() = ((uniform.defaultAt(component) - uniform.min) / uniform.step).roundToInt()
override fun getInt(needsGlobal: Boolean): Int =
((currentValue() - uniform.min) / uniform.step).roundToInt()
override fun setInt(value: Int) = commit(uniform.min + value * uniform.step)
override fun getValueAsString(needsGlobal: Boolean): String {
if (uniform.kind == NativePostProcessing.KIND_FLOAT) {
return String.format("%.3f", currentValue())
}
return currentValue().roundToInt().toString()
}
}
@@ -12,6 +12,7 @@ object Settings {
SECTION_SYSTEM(R.string.preferences_system),
SECTION_RENDERER(R.string.preferences_graphics),
SECTION_FRAME_GEN(R.string.frame_gen),
SECTION_POST_PROCESSING(R.string.post_processing),
SECTION_PERFORMANCE_STATS(R.string.stats_overlay_options),
SECTION_INPUT_OVERLAY(R.string.input_overlay_options),
SECTION_SOC_OVERLAY(R.string.soc_overlay_options),
@@ -13,6 +13,7 @@ enum class StringSetting(override val key: String) : AbstractStringSetting {
DEVICE_NAME("device_name"),
LOG_FILTER("log_filter"),
PROGRAM_ARGS("program_args"),
POST_SHADER_CHAIN("post_shader_chain"),
WEB_TOKEN("eden_token"),
WEB_USERNAME("eden_username")
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.StringRes
class FxButtonSetting(
@StringRes titleId: Int,
val onClick: () -> Unit
) : SettingsItem(emptySetting, titleId, "", 0, "") {
override val type = TYPE_FX_BUTTON
}
@@ -0,0 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
class FxPresetSetting(
titleString: String,
descriptionString: String = "",
val deletable: Boolean = false,
val onApply: () -> Unit,
val onDelete: () -> Unit = {}
) : SettingsItem(emptySetting, 0, titleString, 0, descriptionString) {
override val type = TYPE_FX_PRESET
}
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import org.yuzu.yuzu_emu.utils.NativePostProcessing
class FxShaderCardSetting(
titleString: String,
descriptionString: String,
val index: Int,
val expanded: Boolean,
val uniforms: List<NativePostProcessing.Uniform>,
val onToggle: () -> Unit,
val onRemove: () -> Unit,
val onReset: () -> Unit
) : SettingsItem(emptySetting, 0, titleString, 0, descriptionString) {
override val type = TYPE_FX_SHADER
}
@@ -0,0 +1,19 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.model.view
import androidx.annotation.StringRes
class FxToolbarSetting(
@StringRes val addLabelId: Int,
val listOpen: Boolean,
val presetLabel: String,
val hasEffects: Boolean,
val createPreset: StringInputSetting,
val onAdd: () -> Unit,
val onPresets: () -> Unit,
val onRemoveAll: () -> Unit
) : SettingsItem(emptySetting, 0, "", 0, "") {
override val type = TYPE_FX_TOOLBAR
}
@@ -123,9 +123,7 @@ abstract class SettingsItem(
IntSetting.RENDERER_FRAME_GEN_TARGET_RATE.key,
IntSetting.RENDERER_FRAME_GEN_QUEUE_TARGET.key,
BooleanSetting.RENDERER_FRAME_GEN_FLOW_SCALE_AUTO.key,
IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key,
BooleanSetting.RENDERER_FRAME_GEN_FP16.key,
BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key
IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key
)
const val TYPE_HEADER = 0
@@ -144,6 +142,10 @@ abstract class SettingsItem(
const val TYPE_LAUNCHABLE = 13
const val TYPE_PATH = 14
const val TYPE_GPU_UNSWIZZLE = 15
const val TYPE_FX_TOOLBAR = 16
const val TYPE_FX_PRESET = 17
const val TYPE_FX_SHADER = 18
const val TYPE_FX_BUTTON = 19
const val FASTMEM_COMBINED = "fastmem_combined"
const val GPU_UNSWIZZLE_COMBINED = "gpu_unswizzle_combined"
@@ -262,6 +264,13 @@ abstract class SettingsItem(
descriptionId = R.string.flush_by_line_description
)
)
put(
SwitchSetting(
BooleanSetting.EXTENDED_LOGGING,
titleId = R.string.extended_logging,
descriptionId = R.string.extended_logging_description
)
)
val dockedModeSetting = object : AbstractBooleanSetting {
override val key = BooleanSetting.USE_DOCKED_MODE.key
@@ -698,20 +707,6 @@ abstract class SettingsItem(
units = "%"
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_FRAME_GEN_FP16,
titleId = R.string.frame_gen_fp16,
descriptionId = R.string.frame_gen_fp16_description
)
)
put(
SwitchSetting(
BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW,
titleId = R.string.frame_gen_dump_flow,
descriptionId = R.string.frame_gen_dump_flow_description
)
)
put(
SingleChoiceSetting(
IntSetting.RENDERER_SCREEN_LAYOUT,
@@ -23,6 +23,10 @@ import com.google.android.material.timepicker.TimeFormat
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.SettingsNavigationDirections
import org.yuzu.yuzu_emu.databinding.ListItemSettingBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxButtonBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxPresetBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxShaderBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxToolbarBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingInputBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingSwitchBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingsHeaderBinding
@@ -106,6 +110,34 @@ class SettingsAdapter(
GpuUnswizzleViewHolder(ListItemSettingBinding.inflate(inflater), this)
}
SettingsItem.TYPE_FX_TOOLBAR -> {
FxToolbarViewHolder(
ListItemSettingFxToolbarBinding.inflate(inflater, parent, false),
this
)
}
SettingsItem.TYPE_FX_PRESET -> {
FxPresetViewHolder(
ListItemSettingFxPresetBinding.inflate(inflater, parent, false),
this
)
}
SettingsItem.TYPE_FX_SHADER -> {
FxShaderCardViewHolder(
ListItemSettingFxShaderBinding.inflate(inflater, parent, false),
this
)
}
SettingsItem.TYPE_FX_BUTTON -> {
FxButtonViewHolder(
ListItemSettingFxButtonBinding.inflate(inflater, parent, false),
this
)
}
else -> {
HeaderViewHolder(ListItemSettingsHeaderBinding.inflate(inflater), this)
}
@@ -18,6 +18,7 @@ import org.yuzu.yuzu_emu.features.input.model.NpadStyleIndex
import org.yuzu.yuzu_emu.features.settings.model.AbstractBooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.AbstractIntSetting
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.FxPresetNameSetting
import org.yuzu.yuzu_emu.features.settings.model.ByteSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
import org.yuzu.yuzu_emu.features.settings.model.LongSetting
@@ -30,6 +31,7 @@ import org.yuzu.yuzu_emu.features.settings.model.view.*
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NativePostProcessing
import org.yuzu.yuzu_emu.utils.DirectoryInitialization
import org.yuzu.yuzu_emu.utils.FullscreenHelper
import androidx.core.content.edit
@@ -44,6 +46,14 @@ class SettingsFragmentPresenter(
) {
private var settingsList = ArrayList<SettingsItem>()
private val expandedShaderSlots = mutableSetOf<Int>()
private var shaderPickerOpen = false
private var presetPickerOpen = false
private var postProcessingSynced = false
private val context get() = YuzuApplication.appContext
// Extension for altering settings list based on each setting's properties
@@ -114,7 +124,6 @@ class SettingsFragmentPresenter(
) {
add(IntSetting.RENDERER_FRAME_GEN_FLOW_SCALE.key)
}
add(BooleanSetting.RENDERER_FRAME_GEN_FP16.key)
}
}
@@ -163,6 +172,7 @@ class SettingsFragmentPresenter(
MenuTag.SECTION_SYSTEM -> addSystemSettings(sl)
MenuTag.SECTION_RENDERER -> addGraphicsSettings(sl)
MenuTag.SECTION_FRAME_GEN -> addFrameGenSettings(sl)
MenuTag.SECTION_POST_PROCESSING -> addPostProcessingSettings(sl)
MenuTag.SECTION_PERFORMANCE_STATS -> addPerformanceOverlaySettings(sl)
MenuTag.SECTION_SOC_OVERLAY -> addSocOverlaySettings(sl)
MenuTag.SECTION_INPUT_OVERLAY -> addInputOverlaySettings(sl)
@@ -190,6 +200,209 @@ class SettingsFragmentPresenter(
}
}
private fun addPostProcessingSettings(sl: ArrayList<SettingsItem>) {
if (!postProcessingSynced) {
postProcessingSynced = true
NativePostProcessing.reload()
}
val usable = NativePostProcessing.catalog().filter { it.valid }
sl.apply {
if (usable.isEmpty()) {
add(
RunnableSetting(
titleId = R.string.post_processing_empty,
descriptionString = NativePostProcessing.getShaderDirectory(),
isRunnable = false
) {}
)
return@apply
}
val labels = mutableListOf<String>()
val summaries = mutableListOf<String>()
val files = mutableListOf<String>()
val techniques = mutableListOf<String>()
for (effect in usable) {
for (technique in effect.techniques) {
if (effect.techniques.size == 1) {
labels.add(effect.label)
} else {
labels.add(effect.label + " \u00b7 " + technique)
}
summaries.add(effect.description)
files.add(effect.file)
techniques.add(technique)
}
}
val chain = NativePostProcessing.chain()
val active = NativePostProcessing.getActivePreset()
var addLabel = R.string.post_processing_add
if (chain.isNotEmpty()) {
addLabel = R.string.post_processing_open_list
}
if (shaderPickerOpen) {
addLabel = R.string.post_processing_close_list
}
var presetLabel = context.getString(R.string.post_processing_presets)
if (active.isNotEmpty()) {
presetLabel = active
}
val createPreset = StringInputSetting(
setting = FxPresetNameSetting { name ->
NativePostProcessing.savePreset(name, "")
settingsViewModel.setReloadListAndNotifyDataset(true)
},
titleId = R.string.post_processing_preset_new,
descriptionId = R.string.post_processing_preset_new_description,
validator = { it != null && it.isNotBlank() && !it.contains('=') },
errorId = R.string.post_processing_preset_name_invalid
)
add(
FxToolbarSetting(
addLabelId = addLabel,
listOpen = shaderPickerOpen,
presetLabel = presetLabel,
hasEffects = chain.isNotEmpty(),
createPreset = createPreset,
onAdd = {
shaderPickerOpen = !shaderPickerOpen
presetPickerOpen = false
settingsViewModel.setReloadListAndNotifyDataset(true)
},
onPresets = {
presetPickerOpen = !presetPickerOpen
shaderPickerOpen = false
settingsViewModel.setReloadListAndNotifyDataset(true)
},
onRemoveAll = {
NativePostProcessing.clearChain()
NativePostProcessing.clearPreset()
NativePostProcessing.store()
expandedShaderSlots.clear()
shaderPickerOpen = false
settingsViewModel.setReloadListAndNotifyDataset(true)
}
)
)
if (shaderPickerOpen) {
for (choice in labels.indices) {
add(
RunnableSetting(
titleString = labels[choice],
descriptionString = summaries[choice],
isRunnable = true
) {
NativePostProcessing.append(files[choice], techniques[choice])
NativePostProcessing.store()
shaderPickerOpen = false
settingsViewModel.setReloadListAndNotifyDataset(true)
}
)
}
}
if (presetPickerOpen) {
add(
FxPresetSetting(
titleString = context.getString(R.string.post_processing_preset_none),
onApply = {
NativePostProcessing.clearPreset()
presetPickerOpen = false
settingsViewModel.setReloadListAndNotifyDataset(true)
}
)
)
for (preset in NativePostProcessing.presets()) {
add(
FxPresetSetting(
titleString = preset.name,
descriptionString = preset.description,
deletable = !preset.bundled,
onApply = {
NativePostProcessing.applyPreset(preset.name)
NativePostProcessing.store()
presetPickerOpen = false
expandedShaderSlots.clear()
settingsViewModel.setReloadListAndNotifyDataset(true)
},
onDelete = {
NativePostProcessing.deletePreset(preset.name)
settingsViewModel.setReloadListAndNotifyDataset(true)
}
)
)
}
}
if (active.isNotEmpty()) {
add(
FxButtonSetting(titleId = R.string.post_processing_preset_reset) {
NativePostProcessing.applyPreset(active)
NativePostProcessing.store()
expandedShaderSlots.clear()
settingsViewModel.setReloadListAndNotifyDataset(true)
}
)
}
for (index in chain.indices) {
val entry = chain[index]
val effect = usable.firstOrNull { it.file == entry.file }
var header = entry.file
var summary = ""
var uniforms = emptyList<NativePostProcessing.Uniform>()
if (effect != null) {
header = effect.label
if (effect.techniques.size > 1) {
header = effect.label + " \u00b7 " + entry.technique
}
summary = effect.description
uniforms = effect.uniforms
}
val isOpen = expandedShaderSlots.contains(index)
add(
FxShaderCardSetting(
titleString = header,
descriptionString = summary,
index = index,
expanded = isOpen,
uniforms = uniforms,
onToggle = {
if (isOpen) {
expandedShaderSlots.remove(index)
} else {
expandedShaderSlots.add(index)
}
settingsViewModel.setReloadListAndNotifyDataset(true)
},
onRemove = {
NativePostProcessing.remove(index)
NativePostProcessing.store()
expandedShaderSlots.clear()
settingsViewModel.setReloadListAndNotifyDataset(true)
},
onReset = {
NativePostProcessing.resetValues(index)
NativePostProcessing.store()
settingsViewModel.setReloadListAndNotifyDataset(true)
}
)
)
}
}
}
private fun addConfigSettings(sl: ArrayList<SettingsItem>) {
sl.apply {
add(
@@ -1323,6 +1536,7 @@ class SettingsFragmentPresenter(
add(HeaderSetting(R.string.log))
add(BooleanSetting.DEBUG_FLUSH_BY_LINE.key)
add(BooleanSetting.EXTENDED_LOGGING.key)
add(StringSetting.LOG_FILTER.key)
}
@@ -1338,7 +1552,6 @@ class SettingsFragmentPresenter(
add(BooleanSetting.DUMP_GUEST_SHADERS.key)
add(BooleanSetting.GPU_LOG_SHADER_DUMPS.key)
add(BooleanSetting.DUMP_MACROS.key)
add(BooleanSetting.RENDERER_FRAME_GEN_DUMP_FLOW.key)
add(BooleanSetting.GPU_LOG_MEMORY_TRACKING.key)
add(BooleanSetting.GPU_LOG_DRIVER_DEBUG.key)
add(IntSetting.GPU_LOG_RING_BUFFER_SIZE.key)
@@ -0,0 +1,28 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxButtonBinding
import org.yuzu.yuzu_emu.features.settings.model.view.FxButtonSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class FxButtonViewHolder(
val binding: ListItemSettingFxButtonBinding,
adapter: SettingsAdapter
) : SettingViewHolder(binding.root, adapter) {
private lateinit var setting: FxButtonSetting
override fun bind(item: SettingsItem) {
setting = item as FxButtonSetting
binding.fxButton.text = item.title
binding.fxButton.setOnClickListener { setting.onClick.invoke() }
}
override fun onClick(clicked: View) {}
override fun onLongClick(clicked: View): Boolean = true
}
@@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.View
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxPresetBinding
import org.yuzu.yuzu_emu.features.settings.model.view.FxPresetSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class FxPresetViewHolder(
val binding: ListItemSettingFxPresetBinding,
adapter: SettingsAdapter
) : SettingViewHolder(binding.root, adapter) {
private lateinit var setting: FxPresetSetting
override fun bind(item: SettingsItem) {
setting = item as FxPresetSetting
binding.presetName.text = item.title
binding.presetDescription.text = item.description
binding.presetDescription.setVisible(item.description.isNotEmpty())
binding.presetRow.setOnClickListener { setting.onApply.invoke() }
binding.presetDelete.setVisible(setting.deletable)
binding.presetDelete.setOnClickListener { setting.onDelete.invoke() }
}
override fun onClick(clicked: View) {}
override fun onLongClick(clicked: View): Boolean = true
}
@@ -0,0 +1,100 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.view.LayoutInflater
import android.view.View
import org.yuzu.yuzu_emu.databinding.ItemSettingFxActionsBinding
import org.yuzu.yuzu_emu.databinding.ItemSettingFxSliderBinding
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxShaderBinding
import org.yuzu.yuzu_emu.features.settings.model.FxUniformSliderSetting
import org.yuzu.yuzu_emu.features.settings.model.view.FxShaderCardSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
import org.yuzu.yuzu_emu.utils.NativePostProcessing
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
class FxShaderCardViewHolder(
val binding: ListItemSettingFxShaderBinding,
adapter: SettingsAdapter
) : SettingViewHolder(binding.root, adapter) {
private lateinit var setting: FxShaderCardSetting
override fun bind(item: SettingsItem) {
setting = item as FxShaderCardSetting
binding.shaderTitle.text = item.title
binding.shaderSummary.text = item.description
binding.shaderSummary.setVisible(item.description.isNotEmpty())
var rotation = 0f
if (setting.expanded) {
rotation = 180f
}
binding.shaderExpand.rotation = rotation
binding.shaderHeader.setOnClickListener { setting.onToggle.invoke() }
binding.shaderRemove.setOnClickListener { setting.onRemove.invoke() }
binding.shaderBody.removeAllViews()
binding.shaderBody.setVisible(setting.expanded)
if (!setting.expanded) {
return
}
val inflater = LayoutInflater.from(binding.root.context)
for (uniform in setting.uniforms) {
if (uniform.uiType == NativePostProcessing.UI_HIDDEN) {
continue
}
for (component in 0 until uniform.components) {
addSlider(inflater, uniform, component)
}
}
addActions(inflater)
}
private fun addSlider(
inflater: LayoutInflater,
uniform: NativePostProcessing.Uniform,
component: Int
) {
val row = ItemSettingFxSliderBinding.inflate(inflater, binding.shaderBody, false)
val value = FxUniformSliderSetting(setting.index, uniform, component)
var title = uniform.label
if (uniform.components > 1) {
title = uniform.label + " [" + component + "]"
}
row.fxSliderTitle.text = title
val steps = uniform.steps.toFloat()
row.fxSlider.valueFrom = 0f
row.fxSlider.valueTo = steps
row.fxSlider.stepSize = 1f
row.fxSlider.value = value.getInt(false).toFloat().coerceIn(0f, steps)
row.fxSliderValue.text = uniform.describe(row.fxSlider.value.toInt())
row.fxSlider.addOnChangeListener { _, position, fromUser ->
if (fromUser) {
value.setInt(position.toInt())
row.fxSliderValue.text = uniform.describe(position.toInt())
}
}
binding.shaderBody.addView(row.root)
}
private fun addActions(inflater: LayoutInflater) {
val row = ItemSettingFxActionsBinding.inflate(inflater, binding.shaderBody, false)
row.fxReset.setOnClickListener { setting.onReset.invoke() }
binding.shaderBody.addView(row.root)
}
override fun onClick(clicked: View) {}
override fun onLongClick(clicked: View): Boolean = true
}
@@ -0,0 +1,57 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.features.settings.ui.viewholder
import android.content.res.ColorStateList
import android.view.View
import com.google.android.material.color.MaterialColors
import org.yuzu.yuzu_emu.R
import org.yuzu.yuzu_emu.databinding.ListItemSettingFxToolbarBinding
import org.yuzu.yuzu_emu.features.settings.model.view.FxToolbarSetting
import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem
import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter
class FxToolbarViewHolder(
val binding: ListItemSettingFxToolbarBinding,
adapter: SettingsAdapter
) : SettingViewHolder(binding.root, adapter) {
private lateinit var setting: FxToolbarSetting
override fun bind(item: SettingsItem) {
setting = item as FxToolbarSetting
binding.fxAdd.setText(setting.addLabelId)
var addIcon = R.drawable.ic_add
if (setting.listOpen) {
addIcon = R.drawable.ic_clear
}
binding.fxAdd.setIconResource(addIcon)
binding.fxAdd.setOnClickListener { setting.onAdd.invoke() }
binding.fxPresets.text = setting.presetLabel
binding.fxPresets.setOnClickListener { setting.onPresets.invoke() }
binding.fxCreatePreset.isEnabled = setting.hasEffects
binding.fxCreatePreset.setOnClickListener {
adapter.onStringInputClick(setting.createPreset, bindingAdapterPosition)
}
val removeBackground = MaterialColors.getColor(
binding.fxRemoveAll,
com.google.android.material.R.attr.colorErrorContainer
)
val removeForeground = MaterialColors.getColor(
binding.fxRemoveAll,
com.google.android.material.R.attr.colorOnErrorContainer
)
binding.fxRemoveAll.backgroundTintList = ColorStateList.valueOf(removeBackground)
binding.fxRemoveAll.iconTint = ColorStateList.valueOf(removeForeground)
binding.fxRemoveAll.isEnabled = setting.hasEffects
binding.fxRemoveAll.setOnClickListener { setting.onRemoveAll.invoke() }
}
override fun onClick(clicked: View) {}
override fun onLongClick(clicked: View): Boolean = true
}
@@ -92,8 +92,10 @@ import org.yuzu.yuzu_emu.utils.GameIconUtils
import org.yuzu.yuzu_emu.utils.GpuDriverHelper
import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.Log
import org.yuzu.yuzu_emu.utils.LosslessScalingHelper
import org.yuzu.yuzu_emu.utils.NativeConfig
import org.yuzu.yuzu_emu.utils.NativeFreedrenoConfig
import org.yuzu.yuzu_emu.utils.NativePostProcessing
import org.yuzu.yuzu_emu.utils.ViewUtils
import org.yuzu.yuzu_emu.utils.ViewUtils.setVisible
import org.yuzu.yuzu_emu.utils.collect
@@ -883,6 +885,8 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
if (shouldUseCustom) {
SettingsFile.loadCustomConfig(game!!)
}
refreshPostProcessing()
addQuickSettings()
}
}
@@ -1087,6 +1091,34 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
}
}
private fun withPerGameConfig(create: Boolean, action: () -> Unit) {
val target = game
var owned = false
if (target != null && !NativeConfig.isPerGameConfigLoaded()) {
if (create || SettingsFile.getCustomSettingsFile(target).exists()) {
SettingsFile.loadCustomConfig(target)
owned = true
}
}
action()
if (owned) {
NativeConfig.unloadPerGameConfig()
}
}
fun refreshPostProcessing() = withPerGameConfig(false) {
NativePostProcessing.reload()
}
fun persistPostProcessing() = withPerGameConfig(true) {
NativePostProcessing.persist()
}
fun editPostProcessing(action: () -> Unit) = withPerGameConfig(true) {
action()
NativePostProcessing.persist()
}
private fun addQuickSettings() {
binding.quickSettingsSheet.apply {
val container = binding.quickSettingsSheet.findViewById<ViewGroup>(R.id.quick_settings_container)
@@ -1157,6 +1189,11 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
quickSettings.addDivider(container)
if (LosslessScalingHelper.isInstalled() && LosslessScalingHelper.isSupportedByGpu()) {
quickSettings.addFrameGen(container)
quickSettings.addDivider(container)
}
quickSettings.addIntSetting(
R.string.renderer_accuracy,
container,
@@ -1194,6 +1231,10 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.array.rendererAntiAliasingNames,
R.array.rendererAntiAliasingValues
)
quickSettings.addPostProcessing(container) {
addQuickSettings()
}
}
}
@@ -383,6 +383,20 @@ class GamePropertiesFragment : Fragment() {
}
)
)
add(
SubmenuProperty(
R.string.post_processing,
R.string.post_processing_per_game_description,
R.drawable.ic_post_processing,
action = {
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
args.game,
Settings.MenuTag.SECTION_POST_PROCESSING
)
binding.root.findNavController().navigate(action)
}
)
)
if (GpuDriverHelper.isAdrenoGpu()) {
add(
@@ -171,6 +171,20 @@ class HomeSettingsFragment : Fragment() {
)
)
}
add(
HomeSetting(
R.string.post_processing,
R.string.post_processing_description,
R.drawable.ic_post_processing,
{
val action = HomeNavigationDirections.actionGlobalSettingsActivity(
null,
Settings.MenuTag.SECTION_POST_PROCESSING
)
binding.root.findNavController().navigate(action)
}
)
)
add(
HomeSetting(
R.string.lossless_scaling,
@@ -0,0 +1,242 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
package org.yuzu.yuzu_emu.utils
import org.json.JSONArray
import org.json.JSONObject
object NativePostProcessing {
const val KIND_BOOL = 0
const val KIND_INT = 1
const val KIND_FLOAT = 2
const val UI_HIDDEN = 0
const val UI_SLIDER = 1
const val UI_DRAG = 2
const val UI_COMBO = 3
const val UI_RADIO = 4
const val UI_CHECKBOX = 5
const val UI_COLOR = 6
const val UI_INPUT_BOX = 7
external fun getCatalogJson(): String
external fun getChainJson(): String
external fun append(file: String, technique: String)
external fun replace(index: Int, file: String, technique: String)
external fun remove(index: Int)
external fun move(index: Int, delta: Int)
external fun resetValues(index: Int)
external fun getValue(index: Int, uniform: String, component: Int): Float
external fun hasValue(index: Int, uniform: String): Boolean
external fun setValue(index: Int, uniform: String, component: Int, value: Float)
external fun store()
external fun reload()
external fun clearChain()
external fun getPresetsJson(): String
external fun getActivePreset(): String
external fun isPresetModified(): Boolean
external fun applyPreset(name: String): Boolean
external fun savePreset(name: String, description: String): Boolean
external fun deletePreset(name: String): Boolean
external fun clearPreset()
external fun isEnabled(): Boolean
external fun setEnabled(enabled: Boolean)
external fun getPresetDirectory(): String
fun persist() {
val perGame = NativeConfig.isPerGameConfigLoaded()
store()
if (perGame) {
NativeConfig.savePerGameConfig()
} else {
NativeConfig.saveGlobalConfig()
}
}
external fun getShaderDirectory(): String
data class Uniform(
val name: String,
val label: String,
val tooltip: String,
val category: String,
val kind: Int,
val uiType: Int,
val components: Int,
val min: Float,
val max: Float,
val step: Float,
val items: List<String>,
val defaults: List<Float>
) {
val steps: Int
get() {
val span = max - min
if (step <= 0f) {
return 1
}
val count = Math.round(span / step)
if (count < 1) {
return 1
}
return count
}
fun describe(position: Int): String {
val value = min + position * step
if (kind == NativePostProcessing.KIND_FLOAT) {
return String.format("%.3f", value)
}
return Math.round(value).toString()
}
fun defaultAt(component: Int): Float {
if (component < defaults.size) {
return defaults[component]
}
return 0f
}
}
data class Effect(
val file: String,
val name: String,
val label: String,
val description: String,
val error: String,
val techniques: List<String>,
val uniforms: List<Uniform>
) {
val valid: Boolean
get() = error.isEmpty() && techniques.isNotEmpty()
}
data class ChainEntry(val file: String, val technique: String)
data class Preset(
val name: String,
val description: String,
val bundled: Boolean
)
fun presets(): List<Preset> {
val out = mutableListOf<Preset>()
val array = JSONArray(getPresetsJson())
for (i in 0 until array.length()) {
val obj = array.getJSONObject(i)
out.add(
Preset(
name = obj.optString("name"),
description = obj.optString("description"),
bundled = obj.optBoolean("bundled")
)
)
}
return out
}
fun catalog(): List<Effect> {
val out = mutableListOf<Effect>()
val array = JSONArray(getCatalogJson())
for (i in 0 until array.length()) {
val obj = array.getJSONObject(i)
out.add(
Effect(
file = obj.optString("file"),
name = obj.optString("name"),
label = obj.optString("label"),
description = obj.optString("description"),
error = obj.optString("error"),
techniques = obj.optJSONArray("techniques").toStringList(),
uniforms = obj.optJSONArray("uniforms").toUniformList()
)
)
}
return out
}
fun chain(): List<ChainEntry> {
val out = mutableListOf<ChainEntry>()
val array = JSONArray(getChainJson())
for (i in 0 until array.length()) {
val obj = array.getJSONObject(i)
out.add(ChainEntry(obj.optString("file"), obj.optString("technique")))
}
return out
}
fun findEffect(file: String): Effect? = catalog().firstOrNull { it.file == file }
private fun JSONArray?.toStringList(): List<String> {
if (this == null) {
return emptyList()
}
val out = mutableListOf<String>()
for (i in 0 until length()) {
out.add(optString(i))
}
return out
}
private fun JSONArray?.toFloatList(): List<Float> {
if (this == null) {
return emptyList()
}
val out = mutableListOf<Float>()
for (i in 0 until length()) {
out.add(optDouble(i, 0.0).toFloat())
}
return out
}
private fun JSONArray?.toUniformList(): List<Uniform> {
if (this == null) {
return emptyList()
}
val out = mutableListOf<Uniform>()
for (i in 0 until length()) {
val obj: JSONObject = optJSONObject(i) ?: continue
out.add(
Uniform(
name = obj.optString("name"),
label = obj.optString("label"),
tooltip = obj.optString("tooltip"),
category = obj.optString("category"),
kind = obj.optInt("kind", KIND_FLOAT),
uiType = obj.optInt("uiType", UI_HIDDEN),
components = obj.optInt("components", 1),
min = obj.optDouble("min", 0.0).toFloat(),
max = obj.optDouble("max", 1.0).toFloat(),
step = obj.optDouble("step", 0.01).toFloat(),
items = obj.optJSONArray("items").toStringList(),
defaults = obj.optJSONArray("defaults").toFloatList()
)
)
}
return out
}
}
+2 -1
View File
@@ -17,12 +17,13 @@ add_library(yuzu-android SHARED
android_config.cpp
android_config.h
native_input.cpp
native_post_processing.cpp
)
set_property(TARGET yuzu-android PROPERTY IMPORTED_LOCATION ${FFmpeg_LIBRARY_DIR})
target_link_libraries(yuzu-android PRIVATE audio_core common core input_common frontend_common video_core)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log)
target_link_libraries(yuzu-android PRIVATE android camera2ndk EGL glad jnigraphics log GLESv2)
if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools)
endif()
+17 -4
View File
@@ -1168,7 +1168,7 @@ VkPhysicalDeviceProperties GetVulkanDeviceProperties() {
return physical_device.GetProperties();
}
bool GetVulkanMemoryModelSupport() {
bool GetFrameGenerationSupport() {
Common::DynamicLibrary library;
if (!library.Open("libvulkan.so")) {
return false;
@@ -1183,9 +1183,13 @@ bool GetVulkanMemoryModelSupport() {
const Vulkan::vk::PhysicalDevice physical_device(physical_devices[0], dld);
VkPhysicalDeviceShaderFloat16Int8Features float16_int8{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES,
.pNext = nullptr,
};
VkPhysicalDeviceVulkanMemoryModelFeatures memory_model{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_MEMORY_MODEL_FEATURES,
.pNext = nullptr,
.pNext = &float16_int8,
};
VkPhysicalDeviceFeatures2 features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
@@ -1193,7 +1197,7 @@ bool GetVulkanMemoryModelSupport() {
};
physical_device.GetFeatures2(features);
return memory_model.vulkanMemoryModel == VK_TRUE;
return memory_model.vulkanMemoryModel == VK_TRUE && float16_int8.shaderFloat16 == VK_TRUE;
}
} // namespace
@@ -1272,7 +1276,7 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_getVulkanApiVersion(JNIEnv* env, j
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_supportsFrameGeneration(JNIEnv* env, jobject jobj) {
try {
return static_cast<jboolean>(GetVulkanMemoryModelSupport());
return static_cast<jboolean>(GetFrameGenerationSupport());
} catch (...) {
return static_cast<jboolean>(false);
}
@@ -1730,6 +1734,15 @@ jint Java_org_yuzu_yuzu_1emu_NativeLibrary_loadAmiibo(JNIEnv* env, jobject jobj,
return static_cast<jint>(info);
}
extern "C" JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_initJvm(JNIEnv *env, jclass clazz) {
JavaVM *vm;
if (env->GetJavaVM(&vm) != JNI_OK)
return;
Common::Android::Initialize(vm, env);
}
JNIEXPORT void JNICALL
Java_org_yuzu_yuzu_1emu_NativeLibrary_initMultiplayer(
JNIEnv* env, [[maybe_unused]] jobject obj) {
@@ -15,10 +15,22 @@
#include "frontend_common/config.h"
#include "frontend_common/settings_generator.h"
#include "native.h"
#ifdef HAS_RESHADE
#include "video_core/post_processing/fx_chain.h"
#endif
std::unique_ptr<AndroidConfig> global_config;
std::unique_ptr<AndroidConfig> per_game_config;
#ifdef HAS_RESHADE
static void ResetFxChainToGlobal() {
VideoCore::UseGlobalFxSettings();
VideoCore::FxChain::Instance().LoadFromSettings();
}
#else
static void ResetFxChainToGlobal() {}
#endif
template <typename T>
Settings::Setting<T>* getSetting(JNIEnv* env, jstring jkey) {
auto key = Common::Android::GetJString(env, jkey);
@@ -39,6 +51,7 @@ extern "C" {
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_initializeGlobalConfig(JNIEnv* env, jobject obj) {
global_config = std::make_unique<AndroidConfig>();
FrontendCommon::GenerateSettings();
ResetFxChainToGlobal();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env, jobject obj) {
@@ -47,6 +60,7 @@ void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_unloadGlobalConfig(JNIEnv* env,
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_reloadGlobalConfig(JNIEnv* env, jobject obj) {
global_config->AndroidConfig::ReloadAllValues();
ResetFxChainToGlobal();
}
void Java_org_yuzu_yuzu_1emu_utils_NativeConfig_saveGlobalConfig(JNIEnv* env, jobject obj) {

Some files were not shown because too many files have changed in this diff Show More