Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cfd4408
Review LSFG frame generation and design the Android-side integration
maxjivi05 Aug 23, 2026
e6d0642
Extract LSFG shaders from Lossless.dll and probe GPU support
maxjivi05 Aug 23, 2026
8501ad5
Land the composited frame in an owned image before it reaches the swa…
maxjivi05 Aug 23, 2026
b539df6
Require Lossless Scaling 3.2.2 and say so when the DLL is older
maxjivi05 Aug 23, 2026
266861c
Drop the version floor; the SPIR-V blobs are not downloadable at all
maxjivi05 Aug 23, 2026
557c501
Translate the Lossless DXBC shaders so the chain runs on a real DLL
maxjivi05 Aug 23, 2026
b420c6a
Port eden's LSFG foundation and prove the shaders build pipelines on …
maxjivi05 Aug 23, 2026
994b9ac
Run the first LSFG shaders on the GPU: mipmaps and generate
maxjivi05 Aug 23, 2026
446ed98
Complete the LSFG chain and prove it interpolates on the GPU
maxjivi05 Aug 23, 2026
1f3a5ca
Drive the LSFG chain from the renderer and present generated frames
maxjivi05 Aug 23, 2026
998fd4b
Stop over-deepening the swapchain for frame generation
maxjivi05 Aug 23, 2026
d470084
Add live frame generation controls to the X server drawer
maxjivi05 Aug 23, 2026
841278b
Stop the on-screen sticks repainting the whole overlay, and auto-impo…
maxjivi05 Aug 23, 2026
d0bec9c
Gate Lossless import on ownership, follow Steam updates, and allow pi…
maxjivi05 Aug 23, 2026
5e5b933
Deliver the requested multiplier at 3x and 4x, and re-import Lossless…
maxjivi05 Aug 23, 2026
70e7856
Translate the frame generation strings into every shipped locale
maxjivi05 Aug 23, 2026
1112371
Present generated frames in motion order at 3x and 4x
maxjivi05 Aug 23, 2026
da62bba
Raise the panel to carry generated frames, and stop stranding per-sho…
maxjivi05 Aug 24, 2026
d769d64
Show the generated frame rate next to the rendered one on the HUD
maxjivi05 Aug 24, 2026
d625fb3
Present interpolated frames in rising time order at 3x and 4x
maxjivi05 Aug 24, 2026
a933511
Give frame generation its own drawer tab and let the limiter reach 15
maxjivi05 Aug 24, 2026
a4a5554
Stop frame generation from spending the game's own frames
maxjivi05 Aug 24, 2026
38ba89a
Document frame generation and credit its upstreams
maxjivi05 Aug 24, 2026
c13ae0d
Stop frame generation from compositing faster than the game renders
maxjivi05 Aug 24, 2026
df46f86
Estimate motion at the game's resolution and back frame generation of…
maxjivi05 Aug 25, 2026
105144c
Let shortcut frame generation settings override the container
maxjivi05 Aug 25, 2026
267df31
Download Lossless Scaling from the maintained depot
maxjivi05 Aug 25, 2026
f3ec87d
Frame generation: quality presets, reliable pacing, automatic shader …
maxjivi05 Aug 26, 2026
f4fccd8
Frame generation: cut redundant copies, barriers and enable fp16
maxjivi05 Aug 26, 2026
614ac6a
Translate the frame generation quality preset strings
maxjivi05 Aug 26, 2026
d622725
Frame generation: fix swapchain writes degrading over a session
maxjivi05 Aug 26, 2026
c5b6774
Rotate the captured logcat files
maxjivi05 Aug 26, 2026
f439adc
Label the frame generation tab FPS
maxjivi05 Aug 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,43 @@ in-game **Online** tab).

**How to use:** In the Library, tap **Add Custom Game** and select a ROM instead of an `.exe`. WinNative detects the console and adds the game to your Library. Tap **Play** to launch it with on-screen touch controls and physical gamepad support; the in-game menu (Back button or on-screen **MENU**) offers save/load state, reset, and fast-forward. PlayStation and PlayStation 2 BIOS files can be imported from **Settings → Retro**.

### Frame Generation

WinNative can interpolate extra frames between the ones your game actually renders, using the
Lossless Scaling frame generation shaders. Interpolation runs **on the Android side**, inside
WinNative's own Vulkan compositor rather than inside the Wine container, so it works with any
graphics API Wine can drive — DXVK, WineD3D or native Vulkan alike.

**You must own [Lossless Scaling](https://store.steampowered.com/) on Steam.** Its shaders are
not redistributable, so nothing ships with the APK. WinNative reads them out of your own copy of
`Lossless.dll`, translates them from DXBC to SPIR-V once, and caches the result in app storage.
The DLL is parsed as data and never executed.

**Setup:** sign in to Steam, install Lossless Scaling, then open **Container Settings → Frame
Generation**. WinNative finds the DLL automatically from your Steam library; if it can't, use
**Select Lossless.dll…** to point at it. The in-game **FG** tab stays disabled until the shaders
import successfully.

**In-game controls** live in the **FG** tab of the session drawer, between HUD and Gyro:

| Control | What it does |
| --- | --- |
| Generate Frames | Master toggle |
| Adaptive Target | Aim for a specific output rate (60/90/120/144/165) instead of a fixed multiplier |
| Multiplier | 2× / 3× / 4× — generated frames per rendered frame |
| Flow Scale | 25–100%, resolution of the optical-flow pyramid; lower is cheaper and softer |
| FPS Limiter | Caps the game's own frame rate, from 15 fps upward |

**What to expect.** Frame generation costs **one extra frame of input latency** — interpolating
between two frames means holding the newer one back. It also needs spare display refresh:
generated frames occupy vblanks, so WinNative sizes the multiplier against your panel's refresh
rate and the game's actual frame rate, and will hand back generated frames rather than take real
ones from the game. A game already running near your panel's refresh rate has nothing to gain.
Pairing a multiplier with an FPS limiter that divides the refresh rate evenly (120 Hz with a
60 fps cap at 2×, or 40 at 3×) gives the most even pacing.

---

### Contributing

We welcome community contributions! Feel free to open a pull request for bug fixes, driver updates, UI improvements, or anything else you'd like to add.
Expand All @@ -96,3 +133,7 @@ Please match the existing code style and ensure any AI-assisted code is thorough
- **LibretroDroid** by [Filippo Scognamiglio](https://github.com/Swordfish90/LibretroDroid) (GPL-3.0) — the embedded libretro host for retro console support
- **libretro / RetroArch** and the individual core authors, built from source: [FCEUmm](https://github.com/libretro/libretro-fceumm), [Snes9x](https://github.com/libretro/snes9x), [Gambatte](https://github.com/libretro/gambatte-libretro), [mGBA](https://github.com/libretro/mgba), [Genesis Plus GX](https://github.com/libretro/Genesis-Plus-GX), [Mupen64Plus-Next](https://github.com/libretro/mupen64plus-libretro-nx), [Beetle PSX](https://github.com/libretro/beetle-psx-libretro)
- **ARMSX2** by the [ARMSX2](https://github.com/ARMSX2/ARMSX2) team (GPL-3.0) — the PlayStation 2 core, a fork of **[PCSX2](https://github.com/pcsx2/pcsx2)** (GPL-3.0), built from source into `libemucore`. PS2 online play uses PCSX2's DEV9 network adapter
- **lsfg-vk** by [PancakeTAS](https://github.com/PancakeTAS/lsfg-vk) (GPL-3.0-or-later) — the original Vulkan reimplementation of the Lossless Scaling frame generation chain
- **Eden Emulator Project** by the [eden](https://github.com/eden-emu/eden) team (GPL-3.0-or-later) — the Android port of that chain, which WinNative's compute passes derive from
- **DXVK** by [Philip Rebohle and contributors](https://github.com/doitsujin/dxvk) (zlib/libpng) — the `dxbc` shader translator, vendored at `app/src/main/cpp/thirdparty/dxbc` to convert the frame generation shaders to SPIR-V
- **Lossless Scaling** (Steam) — the source of the frame generation shaders. They are read from the user's own installed copy at runtime; none are redistributed with WinNative
9 changes: 9 additions & 0 deletions app/src/main/app/PluviaApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,15 @@ class PluviaApp : Application() {
com.winlator.cmod.runtime.system.LogManager
.startAppLogging(this@PluviaApp)

runCatching {
val outcome =
com.winlator.cmod.feature.library.LosslessAutoImport
.sync(this@PluviaApp)
if (outcome.result != com.winlator.cmod.feature.library.LosslessAutoImport.RESULT_READY) {
Log.i("PluviaApp", "Lossless shader sync: result=${outcome.result}")
}
}.onFailure { Log.e("PluviaApp", "Lossless shader sync failed", it) }

if (steamLogsEnabled) {
withContext(Dispatchers.Main.immediate) {
if (timber.log.Timber.forest().none { it is timber.log.Timber.DebugTree }) {
Expand Down
17 changes: 17 additions & 0 deletions app/src/main/cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ if(NOT xz_POPULATED)
set(BUILD_SHARED_LIBS "${_winlator_saved_bsl}")
endif()

add_subdirectory(thirdparty/dxbc EXCLUDE_FROM_ALL)
add_subdirectory(patchelf)
add_subdirectory(adrenotools)
add_subdirectory(wn-steam-client)
Expand Down Expand Up @@ -147,6 +148,21 @@ add_library(winlator SHARED
winlator/vk/vk_dispatch.c
winlator/vk/vk_image.c
winlator/vk/vk_renderer.c
winlator/vk/lsfg/lsfg_dll.c
winlator/vk/lsfg/lsfg_dxbc.cpp
winlator/vk/lsfg/lsfg_common.cpp
winlator/vk/lsfg/lsfg_shaders.cpp
winlator/vk/lsfg/lsfg_mipmaps.cpp
winlator/vk/lsfg/lsfg_generate.cpp
winlator/vk/lsfg/lsfg_alpha.cpp
winlator/vk/lsfg/lsfg_beta.cpp
winlator/vk/lsfg/lsfg_gamma.cpp
winlator/vk/lsfg/lsfg_delta.cpp
winlator/vk/lsfg/lsfg_chain.cpp
winlator/vk/lsfg/lsfg_pacer.cpp
winlator/vk/lsfg/vkr_lsfg.cpp
winlator/vk/lsfg/lsfg_probe.c
winlator/vk/lsfg/lsfg_jni.c
)

add_dependencies(winlator winlator_shaders)
Expand All @@ -169,6 +185,7 @@ target_link_libraries(winlator
jnigraphics
vulkan
adrenotools
dxbc
libzstd_static
liblzma
curl::curl
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/cpp/thirdparty/dxbc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# cmake files
.cache/
build/
Empty file.
29 changes: 29 additions & 0 deletions app/src/main/cpp/thirdparty/dxbc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.10)

project(dxbc
VERSION 2.6.2
DESCRIPTION "DXVK's DXBC"
LANGUAGES CXX)

file(GLOB SOURCES
"src/dxbc/*.cpp"
"src/spirv/*.cpp"
"src/util/*.cpp"
)

add_library(dxbc STATIC ${SOURCES})

# target
set_target_properties(dxbc PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED ON)
target_include_directories(dxbc SYSTEM
PUBLIC include/dxbc
PUBLIC include/spirv include/util include/dxvk)
target_compile_options(dxbc PRIVATE
-fPIC)

if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set_target_properties(dxbc PROPERTIES
EXPORT_COMPILE_COMMANDS ON)
endif()
24 changes: 24 additions & 0 deletions app/src/main/cpp/thirdparty/dxbc/LICENSE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Copyright (c) 2017 Philip Rebohle
Copyright (c) 2019 Joshua Ashton
Copyright (c) 2019 Robin Kertels
Copyright (c) 2023 Jeffrey Ellison

zlib/libpng license

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

– The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.

– Altered source versions must be plainly marked as such, and must not
be misrepresented as being the original software.

– This notice may not be removed or altered from any source distribution.
4 changes: 4 additions & 0 deletions app/src/main/cpp/thirdparty/dxbc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This GitHub repo hosts the files for running DXVK's shader translation independently of DXVK.

Please check out the original project:
https://github.com/doitsujin/dxvk
110 changes: 110 additions & 0 deletions app/src/main/cpp/thirdparty/dxbc/include/dxbc/dxbc_analysis.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#pragma once

#include "dxbc_chunk_isgn.h"
#include "dxbc_decoder.h"
#include "dxbc_defs.h"
#include "dxbc_names.h"
#include "dxbc_modinfo.h"
#include "dxbc_util.h"

namespace dxvk {

/**
* \brief Info about unordered access views
*
* Stores whether an UAV is accessed with typed
* read or atomic instructions. This information
* will be used to generate image types.
*/
struct DxbcUavInfo {
bool accessTypedLoad = false;
bool accessAtomicOp = false;
bool sparseFeedback = false;
bool nonInvariantAccess = false;
DxvkAccessOp accessOp = DxvkAccessOp::None;
VkAccessFlags accessFlags = 0;
};

/**
* \brief Info about shader resource views
*
* Stores whether an SRV is accessed with
* sparse feedback. Useful for buffers.
*/
struct DxbcSrvInfo {
bool sparseFeedback = false;
};

/**
* \brief Counts cull and clip distances
*/
struct DxbcClipCullInfo {
uint32_t numClipPlanes = 0;
uint32_t numCullPlanes = 0;
};

/**
* \brief Shader analysis info
*/
struct DxbcAnalysisInfo {
std::array<DxbcUavInfo, 64> uavInfos;
std::array<DxbcSrvInfo, 128> srvInfos;
std::array<DxbcRegMask, 4096> xRegMasks;

DxbcClipCullInfo clipCullIn;
DxbcClipCullInfo clipCullOut;

DxbcBindingMask bindings = { };

bool usesDerivatives = false;
bool usesKill = false;
};

/**
* \brief DXBC shader analysis pass
*
* Collects information about the shader itself
* and the resources used by the shader, which
* will later be used by the actual compiler.
*/
class DxbcAnalyzer {

public:

DxbcAnalyzer(
const DxbcModuleInfo& moduleInfo,
const DxbcProgramInfo& programInfo,
const Rc<DxbcIsgn>& isgn,
const Rc<DxbcIsgn>& osgn,
const Rc<DxbcIsgn>& psgn,
DxbcAnalysisInfo& analysis);

~DxbcAnalyzer();

/**
* \brief Processes a single instruction
* \param [in] ins The instruction
*/
void processInstruction(
const DxbcShaderInstruction& ins);

private:

Rc<DxbcIsgn> m_isgn;
Rc<DxbcIsgn> m_osgn;
Rc<DxbcIsgn> m_psgn;

DxbcAnalysisInfo* m_analysis = nullptr;

DxbcClipCullInfo getClipCullInfo(
const Rc<DxbcIsgn>& sgn) const;

void setUavAccessOp(uint32_t uav, DxvkAccessOp op);

static DxvkAccessOp getStoreAccessOp(DxbcRegMask writeMask, const DxbcRegister& src);

static DxvkAccessOp getConstantStoreOp(uint32_t value);

};

}
68 changes: 68 additions & 0 deletions app/src/main/cpp/thirdparty/dxbc/include/dxbc/dxbc_chunk_isgn.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

#include <cctype>

#include "dxbc_common.h"
#include "dxbc_decoder.h"
#include "dxbc_enums.h"
#include "dxbc_reader.h"

namespace dxvk {

/**
* \brief Signature entry
*
* Stores the semantic name of an input or
* output and the corresponding register.
*/
struct DxbcSgnEntry {
std::string semanticName;
uint32_t semanticIndex;
uint32_t registerId;
DxbcRegMask componentMask;
DxbcRegMask componentUsed;
DxbcScalarType componentType;
DxbcSystemValue systemValue;
uint32_t streamId;
};

/**
* \brief Input/Output signature chunk
*
* Stores information about the input and
* output registers used by the shader stage.
*/
class DxbcIsgn : public RcObject {

public:

DxbcIsgn(DxbcReader reader, DxbcTag tag);
~DxbcIsgn();

auto begin() const { return m_entries.cbegin(); }
auto end () const { return m_entries.cend(); }

const DxbcSgnEntry* findByRegister(
uint32_t registerId) const;

const DxbcSgnEntry* find(
const std::string& semanticName,
uint32_t semanticIndex,
uint32_t streamIndex) const;

DxbcRegMask regMask(
uint32_t registerId) const;

uint32_t maxRegisterCount() const;

static bool compareSemanticNames(
const std::string& a,
const std::string& b);

private:

std::vector<DxbcSgnEntry> m_entries;

};

}
39 changes: 39 additions & 0 deletions app/src/main/cpp/thirdparty/dxbc/include/dxbc/dxbc_chunk_shex.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#pragma once

#include "dxbc_common.h"
#include "dxbc_decoder.h"
#include "dxbc_reader.h"

namespace dxvk {

/**
* \brief Shader code chunk
*
* Stores the DXBC shader code itself, as well
* as some meta info about the shader, i.e. what
* type of shader this is.
*/
class DxbcShex : public RcObject {

public:

DxbcShex(DxbcReader reader);
~DxbcShex();

DxbcProgramInfo programInfo() const {
return m_programInfo;
}

DxbcCodeSlice slice() const {
return DxbcCodeSlice(m_code.data(),
m_code.data() + m_code.size());
}

private:

DxbcProgramInfo m_programInfo;
std::vector<uint32_t> m_code;

};

}
Loading
Loading