PostProcessing: Add ReShade .fx shader support

This commit is contained in:
Stenzek 2023-08-13 23:23:54 +10:00
parent 288757ca9a
commit 8a40c7bf94
9 changed files with 1522 additions and 34 deletions

View file

@ -22,37 +22,37 @@ DuckStation features a fully-featured frontend built using Qt, as well as a full
Other features include: Other features include:
- CPU Recompiler/JIT (x86-64, armv7/AArch32 and AArch64) - CPU Recompiler/JIT (x86-64, armv7/AArch32 and AArch64).
- Hardware (D3D11, D3D12, OpenGL, Vulkan, Metal) and software rendering - Hardware (D3D11, D3D12, OpenGL, Vulkan, Metal) and software rendering.
- Upscaling, texture filtering, and true colour (24-bit) in hardware renderers - Upscaling, texture filtering, and true colour (24-bit) in hardware renderers.
- PGXP for geometry precision, texture correction, and depth buffer emulation - PGXP for geometry precision, texture correction, and depth buffer emulation.
- Adaptive downsampling filter - Adaptive downsampling filter.
- Post processing shader chains - Post processing shader chains (GLSL and experimental Reshade FX).
- "Fast boot" for skipping BIOS splash/intro - "Fast boot" for skipping BIOS splash/intro.
- Save state support - Save state support.
- Windows, Linux, macOS support - Windows, Linux, macOS support.
- Supports bin/cue images, raw bin/img files, MAME CHD, single-track ECM, MDS/MDF, and unencrypted PBP formats. - Supports bin/cue images, raw bin/img files, MAME CHD, single-track ECM, MDS/MDF, and unencrypted PBP formats.
- Direct booting of homebrew executables - Direct booting of homebrew executables.
- Direct loading of Portable Sound Format (psf) files - Direct loading of Portable Sound Format (psf) files.
- Digital and analog controllers for input (rumble is forwarded to host) - Digital and analog controllers for input (rumble is forwarded to host).
- Namco GunCon lightgun support (simulated with mouse) - Namco GunCon lightgun support (simulated with mouse).
- NeGcon support - NeGcon support.
- Qt and "Big Picture" UI - Qt and "Big Picture" UI.
- Automatic updates for Windows builds - Automatic updates for Windows builds.
- Automatic content scanning - game titles/hashes are provided by redump.org - Automatic content scanning - game titles/hashes are provided by redump.org.
- Optional automatic switching of memory cards for each game - Optional automatic switching of memory cards for each game.
- Supports loading cheats from existing lists - Supports loading cheats from existing lists.
- Memory card editor and save importer - Memory card editor and save importer.
- Emulated CPU overclocking - Emulated CPU overclocking.
- Integrated and remote debugging - Integrated and remote debugging.
- Multitap controllers (up to 8 devices) - Multitap controllers (up to 8 devices).
- RetroAchievements - RetroAchievements.
- Automatic loading/applying of PPF patches - Automatic loading/applying of PPF patches.
## System Requirements ## System Requirements
- A CPU faster than a potato. But it needs to be x86_64, AArch32/armv7, or AArch64/ARMv8, otherwise you won't get a recompiler and it'll be slow. - A CPU faster than a potato. But it needs to be x86_64, AArch32/armv7, or AArch64/ARMv8, otherwise you won't get a recompiler and it'll be slow.
- For the hardware renderers, a GPU capable of OpenGL 3.1/OpenGL ES 3.1/Direct3D 11 Feature Level 10.0 (or Vulkan 1.0) and above. So, basically anything made in the last 10 years or so. - For the hardware renderers, a GPU capable of OpenGL 3.1/OpenGL ES 3.1/Direct3D 11 Feature Level 10.0 (or Vulkan 1.0) and above. So, basically anything made in the last 10 years or so.
- SDL, XInput or DInput compatible game controller (e.g. XB360/XBOne). DualShock 3 users on Windows will need to install the official DualShock 3 drivers included as part of PlayStation Now. - SDL, XInput or DInput compatible game controller (e.g. XB360/XBOne/XBSeries). DualShock 3 users on Windows will need to install the official DualShock 3 drivers included as part of PlayStation Now.
## Downloading and running ## Downloading and running
Binaries of DuckStation for Windows x64/ARM64, Linux x86_64 (in AppImage format), and Android ARMv7/ARMv8 are available via GitHub Releases and are automatically built with every commit/push. Binaries or packages distributed through other sources may be out of date and are not supported by the developer, please speak to them for support, not us. Binaries of DuckStation for Windows x64/ARM64, Linux x86_64 (in AppImage format), and Android ARMv7/ARMv8 are available via GitHub Releases and are automatically built with every commit/push. Binaries or packages distributed through other sources may be out of date and are not supported by the developer, please speak to them for support, not us.

View file

@ -16,6 +16,7 @@ add_subdirectory(soundtouch)
add_subdirectory(googletest) add_subdirectory(googletest)
add_subdirectory(cpuinfo) add_subdirectory(cpuinfo)
add_subdirectory(fast_float) add_subdirectory(fast_float)
add_subdirectory(reshadefx)
if(ENABLE_CUBEB) if(ENABLE_CUBEB)
add_subdirectory(cubeb) add_subdirectory(cubeb)

View file

@ -632,9 +632,9 @@ private:
const unsigned int texture_dimension = info.type.texture_dimension(); const unsigned int texture_dimension = info.type.texture_dimension();
code += "Texture" + std::to_string(texture_dimension) + "D<"; code += "Texture" + std::to_string(texture_dimension) + "D<";
write_texture_format(code, tex_info.format); write_texture_format(code, tex_info.format);
code += "> __" + info.unique_name + "_t : register(t" + std::to_string(info.binding) + "); \n"; code += "> __" + info.unique_name + "_t : register( t0); \n";
code += "SamplerState __" + info.unique_name + "_s : register(s" + std::to_string(info.binding) + ");\n"; code += "SamplerState __" + info.unique_name + "_s : register( s0);\n";
code += "static const "; code += "static const ";
write_type(code, info.type); write_type(code, info.type);

View file

@ -52,6 +52,8 @@ add_library(util
postprocessing.h postprocessing.h
postprocessing_shader.cpp postprocessing_shader.cpp
postprocessing_shader.h postprocessing_shader.h
postprocessing_shader_fx.cpp
postprocessing_shader_fx.h
postprocessing_shader_glsl.cpp postprocessing_shader_glsl.cpp
postprocessing_shader_glsl.h postprocessing_shader_glsl.h
shadergen.cpp shadergen.cpp
@ -69,7 +71,7 @@ add_library(util
target_include_directories(util PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(util PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_include_directories(util PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..") target_include_directories(util PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
target_link_libraries(util PUBLIC common simpleini imgui) target_link_libraries(util PUBLIC common simpleini imgui)
target_link_libraries(util PRIVATE stb libchdr zlib soundtouch Zstd::Zstd) target_link_libraries(util PRIVATE stb libchdr zlib soundtouch Zstd::Zstd reshadefx)
if(ENABLE_CUBEB) if(ENABLE_CUBEB)
target_sources(util PRIVATE target_sources(util PRIVATE

View file

@ -6,6 +6,7 @@
#include "host.h" #include "host.h"
#include "imgui_manager.h" #include "imgui_manager.h"
#include "postprocessing_shader.h" #include "postprocessing_shader.h"
#include "postprocessing_shader_fx.h"
#include "postprocessing_shader_glsl.h" #include "postprocessing_shader_glsl.h"
// TODO: Remove me // TODO: Remove me
@ -175,6 +176,32 @@ std::vector<std::pair<std::string, std::string>> PostProcessing::GetAvailableSha
} }
} }
FileSystem::FindFiles(Path::Combine(EmuFolders::Shaders, "reshade" FS_OSPATH_SEPARATOR_STR "Shaders").c_str(), "*.fx",
FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RELATIVE_PATHS, &results);
FileSystem::FindFiles(
Path::Combine(EmuFolders::Resources, "shaders" FS_OSPATH_SEPARATOR_STR "reshade" FS_OSPATH_SEPARATOR_STR "Shaders")
.c_str(),
"*.fx", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RELATIVE_PATHS | FILESYSTEM_FIND_KEEP_ARRAY, &results);
for (FILESYSTEM_FIND_DATA& fd : results)
{
size_t pos = fd.FileName.rfind('.');
if (pos != std::string::npos && pos > 0)
fd.FileName.erase(pos);
// swap any backslashes for forward slashes so the config is cross-platform
for (size_t i = 0; i < fd.FileName.size(); i++)
{
if (fd.FileName[i] == '\\')
fd.FileName[i] = '/';
}
if (std::none_of(names.begin(), names.end(), [&fd](const auto& other) { return fd.FileName == other.second; }))
{
std::string display_name = fmt::format(TRANSLATE_FS("PostProcessing", "{} [ReShade]"), fd.FileName);
names.emplace_back(std::move(display_name), std::move(fd.FileName));
}
}
return names; return names;
} }
@ -333,7 +360,31 @@ void PostProcessing::SetEnabled(bool enabled)
std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const std::string& shader_name, std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const std::string& shader_name,
bool only_config, Error* error) bool only_config, Error* error)
{ {
std::string filename(Path::Combine(EmuFolders::Shaders, fmt::format("{}.glsl", shader_name))); std::string filename;
std::optional<std::string> resource_str;
// Try reshade first.
filename = Path::Combine(
EmuFolders::Shaders,
fmt::format("reshade" FS_OSPATH_SEPARATOR_STR "Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx", shader_name));
// TODO: Won't work on Android. Who cares? All the homies are tired of demanding Android users.
if (!FileSystem::FileExists(filename.c_str()))
{
filename = Path::Combine(EmuFolders::Resources,
fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "reshade" FS_OSPATH_SEPARATOR_STR
"Shaders" FS_OSPATH_SEPARATOR_STR "{}.fx",
shader_name));
}
if (FileSystem::FileExists(filename.c_str()))
{
std::unique_ptr<ReShadeFXShader> shader = std::make_unique<ReShadeFXShader>();
if (shader->LoadFromFile(std::string(shader_name), filename.c_str(), only_config, error))
return shader;
}
filename = Path::Combine(EmuFolders::Shaders, fmt::format("{}.glsl", shader_name));
if (FileSystem::FileExists(filename.c_str())) if (FileSystem::FileExists(filename.c_str()))
{ {
std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>(); std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>();
@ -341,8 +392,8 @@ std::unique_ptr<PostProcessing::Shader> PostProcessing::TryLoadingShader(const s
return shader; return shader;
} }
std::optional<std::string> resource_str( resource_str =
Host::ReadResourceFileToString(fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name).c_str())); Host::ReadResourceFileToString(fmt::format("shaders" FS_OSPATH_SEPARATOR_STR "{}.glsl", shader_name).c_str());
if (resource_str.has_value()) if (resource_str.has_value())
{ {
std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>(); std::unique_ptr<GLSLShader> shader = std::make_unique<GLSLShader>();

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,122 @@
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
#pragma once
#include "postprocessing_shader.h"
#include "common/timer.h"
// reshadefx
#include "effect_module.hpp"
class Error;
namespace PostProcessing {
class ReShadeFXShader final : public Shader
{
public:
ReShadeFXShader();
~ReShadeFXShader();
bool IsValid() const override;
bool LoadFromFile(std::string name, const char* filename, bool only_config, Error* error);
bool ResizeOutput(GPUTexture::Format format, u32 width, u32 height) override;
bool CompilePipeline(GPUTexture::Format format, u32 width, u32 height) override;
bool Apply(GPUTexture* input, GPUFramebuffer* final_target, s32 final_left, s32 final_top, s32 final_width,
s32 final_height, s32 orig_width, s32 orig_height, u32 target_width, u32 target_height) override;
private:
using TextureID = s32;
static constexpr TextureID INPUT_COLOR_TEXTURE = -1;
static constexpr TextureID INPUT_DEPTH_TEXTURE = -2;
static constexpr TextureID OUTPUT_COLOR_TEXTURE = -3;
enum class SourceOptionType
{
None,
Zero,
Timer,
FrameTime,
FrameCount,
FrameCountF,
PingPong,
MousePoint,
BufferWidth,
BufferHeight,
BufferWidthF,
BufferHeightF,
InternalWidth,
InternalHeight,
InternalWidthF,
InternalHeightF,
MaxCount
};
struct SourceOption
{
SourceOptionType source;
u32 offset;
float min;
float max;
float smoothing;
std::array<float, 2> step;
ShaderOption::ValueVector value;
};
bool CreateModule(s32 buffer_width, s32 buffer_height, reshadefx::module* mod, Error* error);
bool CreateOptions(const reshadefx::module& mod, Error* error);
bool GetSourceOption(const reshadefx::uniform_info& ui, SourceOptionType* si, Error* error);
bool CreatePasses(GPUTexture::Format backbuffer_format, reshadefx::module& mod, Error* error);
const char* GetTextureNameForID(TextureID id) const;
GPUTexture* GetTextureByID(TextureID id, GPUTexture* input, GPUFramebuffer* final_target) const;
GPUFramebuffer* GetFramebufferByID(TextureID id, GPUTexture* input, GPUFramebuffer* final_target) const;
std::string m_filename;
struct Texture
{
std::unique_ptr<GPUTexture> texture;
std::unique_ptr<GPUFramebuffer> framebuffer;
std::string reshade_name; // TODO: we might be able to drop this
GPUTexture::Format format;
float rt_scale;
};
struct Sampler
{
u32 slot;
TextureID texture_id;
std::string reshade_name;
GPUSampler* sampler;
};
struct Pass
{
std::unique_ptr<GPUPipeline> pipeline;
TextureID render_target;
std::vector<Sampler> samplers;
u32 num_vertices;
#ifdef _DEBUG
std::string name;
#endif
};
std::vector<Pass> m_passes;
std::vector<Texture> m_textures;
std::vector<SourceOption> m_source_options;
u32 m_uniforms_size = 0;
bool m_valid = false;
Common::Timer m_frame_timer;
u32 m_frame_count = 0;
};
} // namespace PostProcessing

View file

@ -64,6 +64,7 @@
<ClInclude Include="platform_misc.h" /> <ClInclude Include="platform_misc.h" />
<ClInclude Include="postprocessing.h" /> <ClInclude Include="postprocessing.h" />
<ClInclude Include="postprocessing_shader.h" /> <ClInclude Include="postprocessing_shader.h" />
<ClInclude Include="postprocessing_shader_fx.h" />
<ClInclude Include="postprocessing_shader_glsl.h" /> <ClInclude Include="postprocessing_shader_glsl.h" />
<ClInclude Include="sdl_input_source.h" /> <ClInclude Include="sdl_input_source.h" />
<ClInclude Include="shadergen.h" /> <ClInclude Include="shadergen.h" />
@ -164,6 +165,7 @@
<ClCompile Include="platform_misc_win32.cpp" /> <ClCompile Include="platform_misc_win32.cpp" />
<ClCompile Include="postprocessing.cpp" /> <ClCompile Include="postprocessing.cpp" />
<ClCompile Include="postprocessing_shader.cpp" /> <ClCompile Include="postprocessing_shader.cpp" />
<ClCompile Include="postprocessing_shader_fx.cpp" />
<ClCompile Include="postprocessing_shader_glsl.cpp" /> <ClCompile Include="postprocessing_shader_glsl.cpp" />
<ClCompile Include="sdl_input_source.cpp" /> <ClCompile Include="sdl_input_source.cpp" />
<ClCompile Include="shadergen.cpp" /> <ClCompile Include="shadergen.cpp" />
@ -215,6 +217,9 @@
<ProjectReference Include="..\..\dep\libchdr\libchdr.vcxproj"> <ProjectReference Include="..\..\dep\libchdr\libchdr.vcxproj">
<Project>{425d6c99-d1c8-43c2-b8ac-4d7b1d941017}</Project> <Project>{425d6c99-d1c8-43c2-b8ac-4d7b1d941017}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\dep\reshadefx\reshadefx.vcxproj">
<Project>{27b8d4bb-4f01-4432-bc14-9bf6ca458eee}</Project>
</ProjectReference>
<ProjectReference Include="..\..\dep\soundtouch\soundtouch.vcxproj"> <ProjectReference Include="..\..\dep\soundtouch\soundtouch.vcxproj">
<Project>{751d9f62-881c-454e-bce8-cb9cf5f1d22f}</Project> <Project>{751d9f62-881c-454e-bce8-cb9cf5f1d22f}</Project>
</ProjectReference> </ProjectReference>
@ -238,8 +243,9 @@
<Import Project="util.props" /> <Import Project="util.props" />
<ItemDefinitionGroup> <ItemDefinitionGroup>
<ClCompile> <ClCompile>
<AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(SolutionDir)dep\reshadefx\include</AdditionalIncludeDirectories>
<ObjectFileName>$(IntDir)/%(RelativeDir)/</ObjectFileName> <ObjectFileName>$(IntDir)/%(RelativeDir)/</ObjectFileName>
</ClCompile> </ClCompile>
</ItemDefinitionGroup> </ItemDefinitionGroup>
<Import Project="..\..\dep\msvc\vsprops\Targets.props" /> <Import Project="..\..\dep\msvc\vsprops\Targets.props" />
</Project> </Project>

View file

@ -69,6 +69,7 @@
<ClInclude Include="d3d11_stream_buffer.h" /> <ClInclude Include="d3d11_stream_buffer.h" />
<ClInclude Include="d3d11_texture.h" /> <ClInclude Include="d3d11_texture.h" />
<ClInclude Include="host.h" /> <ClInclude Include="host.h" />
<ClInclude Include="postprocessing_shader_fx.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="jit_code_buffer.cpp" /> <ClCompile Include="jit_code_buffer.cpp" />
@ -144,10 +145,11 @@
<ClCompile Include="d3d11_stream_buffer.cpp" /> <ClCompile Include="d3d11_stream_buffer.cpp" />
<ClCompile Include="d3d11_texture.cpp" /> <ClCompile Include="d3d11_texture.cpp" />
<ClCompile Include="host.cpp" /> <ClCompile Include="host.cpp" />
<ClCompile Include="postprocessing_shader_fx.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Filter Include="gl"> <Filter Include="gl">
<UniqueIdentifier>{e637fc5b-2483-4a31-abc3-89a16d45c223}</UniqueIdentifier> <UniqueIdentifier>{e637fc5b-2483-4a31-abc3-89a16d45c223}</UniqueIdentifier>
</Filter> </Filter>
</ItemGroup> </ItemGroup>
</Project> </Project>