Co-authored-by: crueter <crueter@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/88
This implements the use of VK_FILTER_CUBIC_EXT as a replacement for the software-based bicubic window adapting filter, used primarily for texture sampling in upscaled or downscaled surfaces such as UI, transparency effects, and screen-space elements in Unreal Engine 4 titles.
The Vulkan cubic filter is now conditionally enabled if the following are satisfied:
The device supports VK_EXT_filter_cubic
The format used supports VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_CUBIC_BIT_EXT
This change improves visual quality while reducing GPU workload by offloading cubic filtering to the driver instead of running custom sampling code in shaders. On supported hardware (e.g. desktop GPUs or high-end Adreno/AMD devices), it results in smoother transitions, improved transparency sampling, and better fidelity with lower shader complexity.
Fallback to the original software bicubic logic remains in place for devices lacking the extension or format capability.
Tested on several UE4 titles and confirmed to preserve or enhance visual output, especially in alpha-blended and UI-heavy scenes.
Co-authored-by: lizzie <lizzie@eden-emu.dev>
Co-committed-by: lizzie <lizzie@eden-emu.dev>
This sample simply loops audio from input stream to output stream to demonstrate
the usage of the 2 stream interfaces.
Screenshots
Stream Configurations
48kHz
oboe::I16
stereo or mono
Customizing the App
If you want to customize the effects processing then modify the
onBothStreamsReady() method in "src/main/cpp/FullDuplexPass.h"
Caveats
OpenES SL does not allow setting the recording or playback device.
Synchronizing input and output streams for full-duplex operation is tricky.
Input and output have different startup times. The input side may have to charge up the microphone circuit.
Also the initial timing for the output callback may be bursty as it fills the buffer up.
So when the output stream makes its first callback, the input buffer may be overflowing or empty or partially full.
In order to get into sync we go through a few phases.
In Phase 1 we always drain the input buffer as much as possible, more than the output callback asks for. When we have done this for a while, we move to phase 2.
In Phase 2 we optionally skip reading the input once to allow it to fill up with one burst. This makes it less likely to underflow on future reads.
In Phase 3 we should be in a stable situation where the output is nearly full and the input is nearly empty. You should be able to run for hours like this with no glitches.