GPU: Support replacing VRAM writes with new textures
This commit is contained in:
parent
a66f14b5c3
commit
8db961042a
|
@ -92,6 +92,8 @@ add_library(core
|
|||
spu.h
|
||||
system.cpp
|
||||
system.h
|
||||
texture_replacements.cpp
|
||||
texture_replacements.h
|
||||
timers.cpp
|
||||
timers.h
|
||||
timing_event.cpp
|
||||
|
|
|
@ -149,6 +149,7 @@
|
|||
<ClCompile Include="sio.cpp" />
|
||||
<ClCompile Include="spu.cpp" />
|
||||
<ClCompile Include="system.cpp" />
|
||||
<ClCompile Include="texture_replacements.cpp" />
|
||||
<ClCompile Include="timers.cpp" />
|
||||
<ClCompile Include="timing_event.cpp" />
|
||||
</ItemGroup>
|
||||
|
@ -226,6 +227,7 @@
|
|||
<ClInclude Include="sio.h" />
|
||||
<ClInclude Include="spu.h" />
|
||||
<ClInclude Include="system.h" />
|
||||
<ClInclude Include="texture_replacements.h" />
|
||||
<ClInclude Include="timers.h" />
|
||||
<ClInclude Include="timing_event.h" />
|
||||
<ClInclude Include="types.h" />
|
||||
|
@ -246,6 +248,9 @@
|
|||
<ProjectReference Include="..\..\dep\vulkan-loader\vulkan-loader.vcxproj">
|
||||
<Project>{9c8ddeb0-2b8f-4f5f-ba86-127cdf27f035}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\xxhash\xxhash.vcxproj">
|
||||
<Project>{09553c96-9f39-49bf-8ae6-7acbd07c410c}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\zlib\zlib.vcxproj">
|
||||
<Project>{7ff9fdb9-d504-47db-a16a-b08071999620}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -464,7 +469,7 @@
|
|||
<PreprocessorDefinitions>WITH_IMGUI=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -490,7 +495,7 @@
|
|||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;WITH_MMAP_FASTMEM=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -516,7 +521,7 @@
|
|||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;WITH_FASTMEM=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\vixl\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\vixl\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -542,7 +547,7 @@
|
|||
<PreprocessorDefinitions>WITH_IMGUI=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -571,7 +576,7 @@
|
|||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;WITH_MMAP_FASTMEM=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -600,7 +605,7 @@
|
|||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;WITH_FASTMEM=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\vixl\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\vixl\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -628,7 +633,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -655,7 +660,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -683,7 +688,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;WITH_MMAP_FASTMEM=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -710,7 +715,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;WITH_FASTMEM=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\vixl\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\vixl\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -737,7 +742,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;WITH_MMAP_FASTMEM=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -765,7 +770,7 @@
|
|||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;WITH_FASTMEM=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\vixl\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)dep\msvc\include;$(SolutionDir)dep\glad\include;$(SolutionDir)dep\stb\include;$(SolutionDir)dep\imgui\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)dep\vixl\include;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
|
|
@ -55,6 +55,7 @@
|
|||
<ClCompile Include="gpu_backend.cpp" />
|
||||
<ClCompile Include="gpu_sw_backend.cpp" />
|
||||
<ClCompile Include="libcrypt_game_codes.cpp" />
|
||||
<ClCompile Include="texture_replacements.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="types.h" />
|
||||
|
@ -113,5 +114,6 @@
|
|||
<ClInclude Include="gpu_backend.h" />
|
||||
<ClInclude Include="gpu_sw_backend.h" />
|
||||
<ClInclude Include="libcrypt_game_codes.h" />
|
||||
<ClInclude Include="texture_replacements.h" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -4,6 +4,7 @@
|
|||
#include "gpu.h"
|
||||
#include "interrupt_controller.h"
|
||||
#include "system.h"
|
||||
#include "texture_replacements.h"
|
||||
Log_SetChannel(GPU);
|
||||
|
||||
#define CHECK_COMMAND_SIZE(num_words) \
|
||||
|
@ -497,13 +498,6 @@ bool GPU::HandleCopyRectangleCPUToVRAMCommand()
|
|||
|
||||
void GPU::FinishVRAMWrite()
|
||||
{
|
||||
if (g_settings.debugging.dump_cpu_to_vram_copies && m_blit_remaining_words == 0)
|
||||
{
|
||||
DumpVRAMToFile(StringUtil::StdStringFromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++).c_str(),
|
||||
m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * m_vram_transfer.width,
|
||||
m_blit_buffer.data(), true);
|
||||
}
|
||||
|
||||
if (IsInterlacedRenderingEnabled() && IsCRTCScanlinePending())
|
||||
SynchronizeCRTC();
|
||||
|
||||
|
@ -511,6 +505,19 @@ void GPU::FinishVRAMWrite()
|
|||
|
||||
if (m_blit_remaining_words == 0)
|
||||
{
|
||||
if (g_settings.debugging.dump_cpu_to_vram_copies)
|
||||
{
|
||||
DumpVRAMToFile(StringUtil::StdStringFromFormat("cpu_to_vram_copy_%u.png", s_cpu_to_vram_dump_id++).c_str(),
|
||||
m_vram_transfer.width, m_vram_transfer.height, sizeof(u16) * m_vram_transfer.width,
|
||||
m_blit_buffer.data(), true);
|
||||
}
|
||||
|
||||
if (g_settings.texture_replacements.ShouldDumpVRAMWrite(m_vram_transfer.width, m_vram_transfer.height))
|
||||
{
|
||||
g_texture_replacements.DumpVRAMWrite(m_vram_transfer.width, m_vram_transfer.height,
|
||||
reinterpret_cast<const u16*>(m_blit_buffer.data()));
|
||||
}
|
||||
|
||||
UpdateVRAM(m_vram_transfer.x, m_vram_transfer.y, m_vram_transfer.width, m_vram_transfer.height,
|
||||
m_blit_buffer.data(), m_GPUSTAT.set_mask_while_drawing, m_GPUSTAT.check_mask_before_draw);
|
||||
}
|
||||
|
|
|
@ -616,6 +616,52 @@ void GPU_HW_D3D11::DrawUtilityShader(ID3D11PixelShader* shader, const void* unif
|
|||
m_context->Draw(3, 0);
|
||||
}
|
||||
|
||||
bool GPU_HW_D3D11::BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width,
|
||||
u32 height)
|
||||
{
|
||||
if (m_vram_replacement_texture.GetWidth() < tex->GetWidth() ||
|
||||
m_vram_replacement_texture.GetHeight() < tex->GetHeight())
|
||||
{
|
||||
if (!m_vram_replacement_texture.Create(m_device.Get(), tex->GetWidth(), tex->GetHeight(), 1,
|
||||
DXGI_FORMAT_R8G8B8A8_UNORM, D3D11_BIND_SHADER_RESOURCE, tex->GetPixels(),
|
||||
tex->GetByteStride(), true))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
D3D11_MAPPED_SUBRESOURCE sr;
|
||||
HRESULT hr = m_context->Map(m_vram_replacement_texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &sr);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
Log_ErrorPrintf("Texture map failed: %08X", hr);
|
||||
return false;
|
||||
}
|
||||
|
||||
const u32 copy_size = std::min(tex->GetByteStride(), sr.RowPitch);
|
||||
const u8* src_ptr = reinterpret_cast<const u8*>(tex->GetPixels());
|
||||
u8* dst_ptr = static_cast<u8*>(sr.pData);
|
||||
for (u32 i = 0; i < tex->GetHeight(); i++)
|
||||
{
|
||||
std::memcpy(dst_ptr, src_ptr, copy_size);
|
||||
src_ptr += tex->GetByteStride();
|
||||
dst_ptr += sr.RowPitch;
|
||||
}
|
||||
|
||||
m_context->Unmap(m_vram_replacement_texture, 0);
|
||||
}
|
||||
|
||||
m_context->OMSetDepthStencilState(m_depth_disabled_state.Get(), 0);
|
||||
m_context->PSSetShaderResources(0, 1, m_vram_replacement_texture.GetD3DSRVArray());
|
||||
SetViewportAndScissor(dst_x, dst_y, width, height);
|
||||
|
||||
const float uniforms[] = {0.0f, 0.0f, 1.0f, 1.0f};
|
||||
DrawUtilityShader(m_copy_pixel_shader.Get(), uniforms, sizeof(uniforms));
|
||||
RestoreGraphicsAPIState();
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_D3D11::DrawBatchVertices(BatchRenderMode render_mode, u32 base_vertex, u32 num_vertices)
|
||||
{
|
||||
const bool textured = (m_batch.texture_mode != GPUTextureMode::Disabled);
|
||||
|
@ -803,6 +849,16 @@ void GPU_HW_D3D11::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* d
|
|||
const Common::Rectangle<u32> bounds = GetVRAMTransferBounds(x, y, width, height);
|
||||
GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask);
|
||||
|
||||
if (!check_mask)
|
||||
{
|
||||
const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data);
|
||||
if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale,
|
||||
width * m_resolution_scale, height * m_resolution_scale))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 num_pixels = width * height;
|
||||
const auto map_result = m_texture_stream_buffer.Map(m_context.Get(), sizeof(u16), num_pixels * sizeof(u16));
|
||||
std::memcpy(map_result.pointer, data, num_pixels * sizeof(u16));
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "common/d3d11/stream_buffer.h"
|
||||
#include "common/d3d11/texture.h"
|
||||
#include "gpu_hw.h"
|
||||
#include "texture_replacements.h"
|
||||
#include <array>
|
||||
#include <d3d11.h>
|
||||
#include <memory>
|
||||
|
@ -68,6 +69,8 @@ private:
|
|||
|
||||
void DrawUtilityShader(ID3D11PixelShader* shader, const void* uniforms, u32 uniforms_size);
|
||||
|
||||
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||
|
||||
ComPtr<ID3D11Device> m_device;
|
||||
ComPtr<ID3D11DeviceContext> m_context;
|
||||
|
||||
|
@ -118,4 +121,6 @@ private:
|
|||
ComPtr<ID3D11PixelShader> m_vram_copy_pixel_shader;
|
||||
ComPtr<ID3D11PixelShader> m_vram_update_depth_pixel_shader;
|
||||
std::array<std::array<ComPtr<ID3D11PixelShader>, 3>, 2> m_display_pixel_shaders; // [depth_24][interlaced]
|
||||
|
||||
D3D11::Texture m_vram_replacement_texture;
|
||||
};
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "gpu_hw_shadergen.h"
|
||||
#include "host_display.h"
|
||||
#include "system.h"
|
||||
#include "texture_replacements.h"
|
||||
Log_SetChannel(GPU_HW_OpenGL);
|
||||
|
||||
GPU_HW_OpenGL::GPU_HW_OpenGL() : GPU_HW() {}
|
||||
|
@ -618,6 +619,37 @@ void GPU_HW_OpenGL::SetBlendMode()
|
|||
}
|
||||
}
|
||||
|
||||
bool GPU_HW_OpenGL::BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width,
|
||||
u32 height)
|
||||
{
|
||||
if (!m_vram_write_replacement_texture.IsValid())
|
||||
{
|
||||
if (!m_vram_write_replacement_texture.Create(tex->GetWidth(), tex->GetHeight(), 1, GL_RGBA, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, tex->GetPixels()) ||
|
||||
!m_vram_write_replacement_texture.CreateFramebuffer())
|
||||
{
|
||||
m_vram_write_replacement_texture.Destroy();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_vram_write_replacement_texture.Replace(tex->GetWidth(), tex->GetHeight(), GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE,
|
||||
tex->GetPixels());
|
||||
}
|
||||
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
m_vram_write_replacement_texture.BindFramebuffer(GL_READ_FRAMEBUFFER);
|
||||
|
||||
dst_y = m_vram_texture.GetHeight() - dst_y - height;
|
||||
glBlitFramebuffer(0, tex->GetHeight(), tex->GetWidth(), 0, dst_x, dst_y, dst_x + width, dst_y + height,
|
||||
GL_COLOR_BUFFER_BIT, GL_LINEAR);
|
||||
|
||||
m_vram_read_texture.Bind();
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
return true;
|
||||
}
|
||||
|
||||
void GPU_HW_OpenGL::SetDepthFunc()
|
||||
{
|
||||
SetDepthFunc(m_batch.use_depth_buffer ? GL_LEQUAL : (m_batch.check_mask_before_draw ? GL_GEQUAL : GL_ALWAYS));
|
||||
|
@ -849,12 +881,22 @@ void GPU_HW_OpenGL::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
|
|||
|
||||
void GPU_HW_OpenGL::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data, bool set_mask, bool check_mask)
|
||||
{
|
||||
const Common::Rectangle<u32> bounds = GetVRAMTransferBounds(x, y, width, height);
|
||||
GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask);
|
||||
|
||||
if (!check_mask)
|
||||
{
|
||||
const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data);
|
||||
if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale,
|
||||
width * m_resolution_scale, height * m_resolution_scale))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 num_pixels = width * height;
|
||||
if (num_pixels < m_max_texture_buffer_size || m_use_ssbo_for_vram_writes)
|
||||
{
|
||||
const Common::Rectangle<u32> bounds = GetVRAMTransferBounds(x, y, width, height);
|
||||
GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask);
|
||||
|
||||
const auto map_result = m_texture_stream_buffer->Map(sizeof(u16), num_pixels * sizeof(u16));
|
||||
std::memcpy(map_result.pointer, data, num_pixels * sizeof(u16));
|
||||
m_texture_stream_buffer->Unmap(num_pixels * sizeof(u16));
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "common/gl/texture.h"
|
||||
#include "glad.h"
|
||||
#include "gpu_hw.h"
|
||||
#include "texture_replacements.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
@ -67,12 +68,15 @@ private:
|
|||
void SetDepthFunc(GLenum func);
|
||||
void SetBlendMode();
|
||||
|
||||
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||
|
||||
// downsample texture - used for readbacks at >1xIR.
|
||||
GL::Texture m_vram_texture;
|
||||
GL::Texture m_vram_depth_texture;
|
||||
GL::Texture m_vram_read_texture;
|
||||
GL::Texture m_vram_encoding_texture;
|
||||
GL::Texture m_display_texture;
|
||||
GL::Texture m_vram_write_replacement_texture;
|
||||
|
||||
std::unique_ptr<GL::StreamBuffer> m_vertex_stream_buffer;
|
||||
GLuint m_vram_fbo_id = 0;
|
||||
|
|
|
@ -1148,6 +1148,16 @@ void GPU_HW_Vulkan::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void*
|
|||
const Common::Rectangle<u32> bounds = GetVRAMTransferBounds(x, y, width, height);
|
||||
GPU_HW::UpdateVRAM(bounds.left, bounds.top, bounds.GetWidth(), bounds.GetHeight(), data, set_mask, check_mask);
|
||||
|
||||
if (!check_mask)
|
||||
{
|
||||
const TextureReplacementTexture* rtex = g_texture_replacements.GetVRAMWriteReplacement(width, height, data);
|
||||
if (rtex && BlitVRAMReplacementTexture(rtex, x * m_resolution_scale, y * m_resolution_scale,
|
||||
width * m_resolution_scale, height * m_resolution_scale))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 data_size = width * height * sizeof(u16);
|
||||
const u32 alignment = std::max<u32>(sizeof(u16), static_cast<u32>(g_vulkan_context->GetTexelBufferAlignment()));
|
||||
if (!m_texture_stream_buffer.ReserveMemory(data_size, alignment))
|
||||
|
@ -1326,6 +1336,80 @@ void GPU_HW_Vulkan::ClearDepthBuffer()
|
|||
m_last_depth_z = 1.0f;
|
||||
}
|
||||
|
||||
bool GPU_HW_Vulkan::CreateTextureReplacementStreamBuffer()
|
||||
{
|
||||
if (m_texture_replacment_stream_buffer.IsValid())
|
||||
return true;
|
||||
|
||||
if (!m_texture_replacment_stream_buffer.Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, TEXTURE_REPLACEMENT_BUFFER_SIZE))
|
||||
{
|
||||
Log_ErrorPrint("Failed to allocate texture replacement streaming buffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPU_HW_Vulkan::BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width,
|
||||
u32 height)
|
||||
{
|
||||
if (!CreateTextureReplacementStreamBuffer())
|
||||
return false;
|
||||
|
||||
if (m_vram_write_replacement_texture.GetWidth() < tex->GetWidth() ||
|
||||
m_vram_write_replacement_texture.GetHeight() < tex->GetHeight())
|
||||
{
|
||||
if (!m_vram_write_replacement_texture.Create(tex->GetWidth(), tex->GetHeight(), 1, 1, VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VK_SAMPLE_COUNT_1_BIT, VK_IMAGE_VIEW_TYPE_2D, VK_IMAGE_TILING_OPTIMAL,
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT))
|
||||
{
|
||||
Log_ErrorPrint("Failed to create VRAM write replacement texture");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const u32 required_size = tex->GetWidth() * tex->GetHeight() * sizeof(u32);
|
||||
const u32 alignment = static_cast<u32>(g_vulkan_context->GetBufferImageGranularity());
|
||||
if (!m_texture_replacment_stream_buffer.ReserveMemory(required_size, alignment))
|
||||
{
|
||||
Log_PerfPrint("Executing command buffer while waiting for texture replacement buffer space");
|
||||
g_vulkan_context->ExecuteCommandBuffer(false);
|
||||
if (!m_texture_replacment_stream_buffer.ReserveMemory(required_size, alignment))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to allocate %u bytes from texture replacement streaming buffer", required_size);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// upload to buffer
|
||||
const u32 buffer_offset = m_texture_replacment_stream_buffer.GetCurrentOffset();
|
||||
std::memcpy(m_texture_replacment_stream_buffer.GetCurrentHostPointer(), tex->GetPixels(), required_size);
|
||||
m_texture_replacment_stream_buffer.CommitMemory(required_size);
|
||||
|
||||
// buffer -> texture
|
||||
VkCommandBuffer cmdbuf = g_vulkan_context->GetCurrentCommandBuffer();
|
||||
m_vram_write_replacement_texture.UpdateFromBuffer(cmdbuf, 0, 0, 0, 0, tex->GetWidth(), tex->GetHeight(),
|
||||
m_texture_replacment_stream_buffer.GetBuffer(), buffer_offset);
|
||||
|
||||
// texture -> vram
|
||||
const VkImageBlit blit = {
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u},
|
||||
{
|
||||
{0, 0, 0},
|
||||
{static_cast<int32_t>(tex->GetWidth()), static_cast<int32_t>(tex->GetHeight()), 1},
|
||||
},
|
||||
{VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u},
|
||||
{{static_cast<int32_t>(dst_x), static_cast<int32_t>(dst_y), 0},
|
||||
{static_cast<int32_t>(dst_x + width), static_cast<int32_t>(dst_y + height), 1}},
|
||||
};
|
||||
m_vram_write_replacement_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL);
|
||||
m_vram_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
|
||||
vkCmdBlitImage(cmdbuf, m_vram_write_replacement_texture.GetImage(), m_vram_write_replacement_texture.GetLayout(),
|
||||
m_vram_texture.GetImage(), m_vram_texture.GetLayout(), 1, &blit, VK_FILTER_LINEAR);
|
||||
m_vram_texture.TransitionToLayout(cmdbuf, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<GPU> GPU::CreateHardwareVulkanRenderer()
|
||||
{
|
||||
return std::make_unique<GPU_HW_Vulkan>();
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "common/vulkan/stream_buffer.h"
|
||||
#include "common/vulkan/texture.h"
|
||||
#include "gpu_hw.h"
|
||||
#include "texture_replacements.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
@ -41,6 +42,7 @@ private:
|
|||
enum : u32
|
||||
{
|
||||
MAX_PUSH_CONSTANTS_SIZE = 64,
|
||||
TEXTURE_REPLACEMENT_BUFFER_SIZE = 64 * 1024 * 1024
|
||||
};
|
||||
void SetCapabilities();
|
||||
void DestroyResources();
|
||||
|
@ -64,6 +66,10 @@ private:
|
|||
bool CompilePipelines();
|
||||
void DestroyPipelines();
|
||||
|
||||
bool CreateTextureReplacementStreamBuffer();
|
||||
|
||||
bool BlitVRAMReplacementTexture(const TextureReplacementTexture* tex, u32 dst_x, u32 dst_y, u32 width, u32 height);
|
||||
|
||||
VkRenderPass m_current_render_pass = VK_NULL_HANDLE;
|
||||
|
||||
VkRenderPass m_vram_render_pass = VK_NULL_HANDLE;
|
||||
|
@ -86,6 +92,7 @@ private:
|
|||
Vulkan::Texture m_vram_readback_texture;
|
||||
Vulkan::StagingTexture m_vram_readback_staging_texture;
|
||||
Vulkan::Texture m_display_texture;
|
||||
bool m_use_ssbos_for_vram_writes = false;
|
||||
|
||||
VkFramebuffer m_vram_framebuffer = VK_NULL_HANDLE;
|
||||
VkFramebuffer m_vram_update_depth_framebuffer = VK_NULL_HANDLE;
|
||||
|
@ -123,5 +130,7 @@ private:
|
|||
// [depth_24][interlace_mode]
|
||||
DimensionalArray<VkPipeline, 3, 2> m_display_pipelines{};
|
||||
|
||||
bool m_use_ssbos_for_vram_writes = false;
|
||||
// texture replacements
|
||||
Vulkan::Texture m_vram_write_replacement_texture;
|
||||
Vulkan::StreamBuffer m_texture_replacment_stream_buffer;
|
||||
};
|
||||
|
|
|
@ -255,6 +255,17 @@ void Settings::Load(SettingsInterface& si)
|
|||
debugging.show_timers_state = si.GetBoolValue("Debug", "ShowTimersState");
|
||||
debugging.show_mdec_state = si.GetBoolValue("Debug", "ShowMDECState");
|
||||
debugging.show_dma_state = si.GetBoolValue("Debug", "ShowDMAState");
|
||||
|
||||
texture_replacements.enable_vram_write_replacements =
|
||||
si.GetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements", false);
|
||||
texture_replacements.preload_textures = si.GetBoolValue("TextureReplacements", "PreloadTextures", false);
|
||||
texture_replacements.dump_vram_writes = si.GetBoolValue("TextureReplacements", "DumpVRAMWrites", false);
|
||||
texture_replacements.dump_vram_write_force_alpha_channel =
|
||||
si.GetBoolValue("TextureReplacements", "DumpVRAMWriteForceAlphaChannel", true);
|
||||
texture_replacements.dump_vram_write_width_threshold =
|
||||
si.GetIntValue("TextureReplacements", "DumpVRAMWriteWidthThreshold", 128);
|
||||
texture_replacements.dump_vram_write_height_threshold =
|
||||
si.GetIntValue("TextureReplacements", "DumpVRAMWriteHeightThreshold", 128);
|
||||
}
|
||||
|
||||
void Settings::Save(SettingsInterface& si) const
|
||||
|
@ -381,6 +392,17 @@ void Settings::Save(SettingsInterface& si) const
|
|||
si.SetBoolValue("Debug", "ShowTimersState", debugging.show_timers_state);
|
||||
si.SetBoolValue("Debug", "ShowMDECState", debugging.show_mdec_state);
|
||||
si.SetBoolValue("Debug", "ShowDMAState", debugging.show_dma_state);
|
||||
|
||||
si.SetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements",
|
||||
texture_replacements.enable_vram_write_replacements);
|
||||
si.SetBoolValue("TextureReplacements", "PreloadTextures", texture_replacements.preload_textures);
|
||||
si.SetBoolValue("TextureReplacements", "DumpVRAMWrites", texture_replacements.dump_vram_writes);
|
||||
si.SetBoolValue("TextureReplacements", "DumpVRAMWriteForceAlphaChannel",
|
||||
texture_replacements.dump_vram_write_force_alpha_channel);
|
||||
si.SetIntValue("TextureReplacements", "DumpVRAMWriteWidthThreshold",
|
||||
texture_replacements.dump_vram_write_width_threshold);
|
||||
si.SetIntValue("TextureReplacements", "DumpVRAMWriteHeightThreshold",
|
||||
texture_replacements.dump_vram_write_height_threshold);
|
||||
}
|
||||
|
||||
static std::array<const char*, LOGLEVEL_COUNT> s_log_level_names = {
|
||||
|
@ -635,8 +657,8 @@ static std::array<const char*, 13> s_display_aspect_ratio_names = {{"Auto (Game
|
|||
"19:9", "21:9", "32:9", "8:7", "5:4", "3:2",
|
||||
"2:1 (VRAM 1:1)", "1:1", "PAR 1:1"}};
|
||||
static constexpr std::array<float, 13> s_display_aspect_ratio_values = {
|
||||
{-1.0f, 4.0f / 3.0f, 16.0f / 9.0f, 16.0f / 10.0f, 19.0f / 9.0f, 64.0f / 27.0f, 32.0f / 9.0f, 8.0f / 7.0f, 5.0f / 4.0f, 3.0f / 2.0f,
|
||||
2.0f / 1.0f, 1.0f, -1.0f}};
|
||||
{-1.0f, 4.0f / 3.0f, 16.0f / 9.0f, 16.0f / 10.0f, 19.0f / 9.0f, 64.0f / 27.0f, 32.0f / 9.0f, 8.0f / 7.0f, 5.0f / 4.0f,
|
||||
3.0f / 2.0f, 2.0f / 1.0f, 1.0f, -1.0f}};
|
||||
|
||||
std::optional<DisplayAspectRatio> Settings::ParseDisplayAspectRatio(const char* str)
|
||||
{
|
||||
|
|
|
@ -171,6 +171,25 @@ struct Settings
|
|||
mutable bool show_dma_state = false;
|
||||
} debugging;
|
||||
|
||||
// texture replacements
|
||||
struct TextureReplacementSettings
|
||||
{
|
||||
bool enable_vram_write_replacements = false;
|
||||
bool preload_textures = false;
|
||||
|
||||
bool dump_vram_writes = false;
|
||||
bool dump_vram_write_force_alpha_channel = true;
|
||||
u32 dump_vram_write_width_threshold = 128;
|
||||
u32 dump_vram_write_height_threshold = 128;
|
||||
|
||||
ALWAYS_INLINE bool AnyReplacementsEnabled() const { return enable_vram_write_replacements; }
|
||||
|
||||
ALWAYS_INLINE bool ShouldDumpVRAMWrite(u32 width, u32 height)
|
||||
{
|
||||
return dump_vram_writes && width >= dump_vram_write_width_threshold && height >= dump_vram_write_height_threshold;
|
||||
}
|
||||
} texture_replacements;
|
||||
|
||||
// TODO: Controllers, memory cards, etc.
|
||||
|
||||
bool bios_patch_tty_enable = false;
|
||||
|
@ -228,7 +247,9 @@ struct Settings
|
|||
DEFAULT_DMA_MAX_SLICE_TICKS = 1000,
|
||||
DEFAULT_DMA_HALT_TICKS = 100,
|
||||
DEFAULT_GPU_FIFO_SIZE = 16,
|
||||
DEFAULT_GPU_MAX_RUN_AHEAD = 128
|
||||
DEFAULT_GPU_MAX_RUN_AHEAD = 128,
|
||||
DEFAULT_VRAM_WRITE_DUMP_WIDTH_THRESHOLD = 128,
|
||||
DEFAULT_VRAM_WRITE_DUMP_HEIGHT_THRESHOLD = 128,
|
||||
};
|
||||
|
||||
void Load(SettingsInterface& si);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "save_state_version.h"
|
||||
#include "sio.h"
|
||||
#include "spu.h"
|
||||
#include "texture_replacements.h"
|
||||
#include "timers.h"
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
|
@ -770,6 +771,8 @@ void Shutdown()
|
|||
if (s_state == State::Shutdown)
|
||||
return;
|
||||
|
||||
g_texture_replacements.Shutdown();
|
||||
|
||||
g_sio.Shutdown();
|
||||
g_mdec.Shutdown();
|
||||
g_spu.Shutdown();
|
||||
|
@ -1691,6 +1694,8 @@ void UpdateRunningGame(const char* path, CDImage* image)
|
|||
s_running_game_code.c_str(), s_running_game_title.c_str());
|
||||
}
|
||||
|
||||
g_texture_replacements.SetGameID(s_running_game_code);
|
||||
|
||||
g_host_interface->OnRunningGameChanged();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
#include "texture_replacements.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/timer.h"
|
||||
#include "host_interface.h"
|
||||
#include "settings.h"
|
||||
#include "xxhash.h"
|
||||
#include <cinttypes>
|
||||
Log_SetChannel(TextureReplacements);
|
||||
|
||||
TextureReplacements g_texture_replacements;
|
||||
|
||||
static constexpr u32 RGBA5551ToRGBA8888(u16 color)
|
||||
{
|
||||
u8 r = Truncate8(color & 31);
|
||||
u8 g = Truncate8((color >> 5) & 31);
|
||||
u8 b = Truncate8((color >> 10) & 31);
|
||||
u8 a = Truncate8((color >> 15) & 1);
|
||||
|
||||
// 00012345 -> 1234545
|
||||
b = (b << 3) | (b & 0b111);
|
||||
g = (g << 3) | (g & 0b111);
|
||||
r = (r << 3) | (r & 0b111);
|
||||
a = a ? 255 : 0;
|
||||
|
||||
return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24);
|
||||
}
|
||||
|
||||
std::string TextureReplacementHash::ToString() const
|
||||
{
|
||||
return StringUtil::StdStringFromFormat("%" PRIx64 "%" PRIx64, high, low);
|
||||
}
|
||||
|
||||
bool TextureReplacementHash::ParseString(const std::string_view& sv)
|
||||
{
|
||||
if (sv.length() != 32)
|
||||
return false;
|
||||
|
||||
std::optional<u64> high_value = StringUtil::FromChars<u64>(sv.substr(0, 16), 16);
|
||||
std::optional<u64> low_value = StringUtil::FromChars<u64>(sv.substr(16), 16);
|
||||
if (!high_value.has_value() || !low_value.has_value())
|
||||
return false;
|
||||
|
||||
low = low_value.value();
|
||||
high = high_value.value();
|
||||
return true;
|
||||
}
|
||||
|
||||
TextureReplacements::TextureReplacements() = default;
|
||||
|
||||
TextureReplacements::~TextureReplacements() = default;
|
||||
|
||||
void TextureReplacements::SetGameID(std::string game_id)
|
||||
{
|
||||
if (m_game_id == game_id)
|
||||
return;
|
||||
|
||||
m_game_id = game_id;
|
||||
Reload();
|
||||
}
|
||||
|
||||
const TextureReplacementTexture* TextureReplacements::GetVRAMWriteReplacement(u32 width, u32 height, const void* pixels)
|
||||
{
|
||||
const TextureReplacementHash hash = GetVRAMWriteHash(width, height, pixels);
|
||||
|
||||
const auto it = m_vram_write_replacements.find(hash);
|
||||
if (it == m_vram_write_replacements.end())
|
||||
return nullptr;
|
||||
|
||||
return LoadTexture(it->second);
|
||||
}
|
||||
|
||||
void TextureReplacements::DumpVRAMWrite(u32 width, u32 height, const void* pixels)
|
||||
{
|
||||
std::string filename = GetVRAMWriteDumpFilename(width, height, pixels);
|
||||
if (filename.empty())
|
||||
return;
|
||||
|
||||
Common::RGBA8Image image;
|
||||
image.SetSize(width, height);
|
||||
|
||||
const u16* src_pixels = reinterpret_cast<const u16*>(pixels);
|
||||
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
for (u32 x = 0; x < width; x++)
|
||||
{
|
||||
image.SetPixel(x, y, RGBA5551ToRGBA8888(*src_pixels));
|
||||
src_pixels++;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_settings.texture_replacements.dump_vram_write_force_alpha_channel)
|
||||
{
|
||||
for (u32 y = 0; y < height; y++)
|
||||
{
|
||||
for (u32 x = 0; x < width; x++)
|
||||
image.SetPixel(x, y, image.GetPixel(x, y) | 0xFF000000u);
|
||||
}
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Dumping %ux%u VRAM write to '%s'", width, height, filename.c_str());
|
||||
if (!Common::WriteImageToFile(image, filename.c_str()))
|
||||
Log_ErrorPrintf("Failed to dump %ux%u VRAM write to '%s'", width, height, filename.c_str());
|
||||
}
|
||||
|
||||
void TextureReplacements::Shutdown()
|
||||
{
|
||||
m_texture_cache.clear();
|
||||
m_vram_write_replacements.clear();
|
||||
m_game_id.clear();
|
||||
}
|
||||
|
||||
std::string TextureReplacements::GetSourceDirectory() const
|
||||
{
|
||||
return g_host_interface->GetUserDirectoryRelativePath("textures/%s", m_game_id.c_str());
|
||||
}
|
||||
|
||||
TextureReplacementHash TextureReplacements::GetVRAMWriteHash(u32 width, u32 height, const void* pixels) const
|
||||
{
|
||||
XXH128_hash_t hash = XXH3_128bits(pixels, width * height * sizeof(u16));
|
||||
return {hash.low64, hash.high64};
|
||||
}
|
||||
|
||||
std::string TextureReplacements::GetVRAMWriteDumpFilename(u32 width, u32 height, const void* pixels) const
|
||||
{
|
||||
if (m_game_id.empty())
|
||||
return {};
|
||||
|
||||
const TextureReplacementHash hash = GetVRAMWriteHash(width, height, pixels);
|
||||
std::string filename = g_host_interface->GetUserDirectoryRelativePath("dump/textures/%s/vram-write-%s.png",
|
||||
m_game_id.c_str(), hash.ToString().c_str());
|
||||
|
||||
if (FileSystem::FileExists(filename.c_str()))
|
||||
return {};
|
||||
|
||||
const std::string dump_directory =
|
||||
g_host_interface->GetUserDirectoryRelativePath("dump/textures/%s", m_game_id.c_str());
|
||||
if (!FileSystem::DirectoryExists(dump_directory.c_str()) &&
|
||||
!FileSystem::CreateDirectory(dump_directory.c_str(), false))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
void TextureReplacements::Reload()
|
||||
{
|
||||
m_vram_write_replacements.clear();
|
||||
|
||||
if (g_settings.texture_replacements.AnyReplacementsEnabled())
|
||||
FindTextures(GetSourceDirectory());
|
||||
|
||||
if (g_settings.texture_replacements.preload_textures)
|
||||
PreloadTextures();
|
||||
|
||||
PurgeUnreferencedTexturesFromCache();
|
||||
}
|
||||
|
||||
void TextureReplacements::PurgeUnreferencedTexturesFromCache()
|
||||
{
|
||||
TextureCache old_map = std::move(m_texture_cache);
|
||||
for (const auto& it : m_vram_write_replacements)
|
||||
{
|
||||
auto it2 = old_map.find(it.second);
|
||||
if (it2 != old_map.end())
|
||||
{
|
||||
m_texture_cache[it.second] = std::move(it2->second);
|
||||
old_map.erase(it2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool TextureReplacements::ParseReplacementFilename(const std::string& filename,
|
||||
TextureReplacementHash* replacement_hash,
|
||||
ReplacmentType* replacement_type)
|
||||
{
|
||||
const char* extension = std::strrchr(filename.c_str(), '.');
|
||||
const char* title = std::strrchr(filename.c_str(), '/');
|
||||
#ifdef WIN32
|
||||
const char* title2 = std::strrchr(filename.c_str(), '\\');
|
||||
if (title2 && (!title || title2 > title))
|
||||
title = title2;
|
||||
#endif
|
||||
|
||||
if (!title || !extension)
|
||||
return false;
|
||||
|
||||
title++;
|
||||
|
||||
const char* hashpart;
|
||||
|
||||
if (StringUtil::Strncasecmp(title, "vram-write-", 11) == 0)
|
||||
{
|
||||
hashpart = title + 11;
|
||||
*replacement_type = ReplacmentType::VRAMWrite;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!replacement_hash->ParseString(std::string_view(hashpart, static_cast<size_t>(extension - hashpart))))
|
||||
return false;
|
||||
|
||||
extension++;
|
||||
|
||||
bool valid_extension = false;
|
||||
for (const char* test_extension : {"png", "jpg", "tga", "bmp"})
|
||||
{
|
||||
if (StringUtil::Strcasecmp(extension, test_extension) == 0)
|
||||
{
|
||||
valid_extension = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return valid_extension;
|
||||
}
|
||||
|
||||
void TextureReplacements::FindTextures(const std::string& dir)
|
||||
{
|
||||
FileSystem::FindResultsArray files;
|
||||
FileSystem::FindFiles(dir.c_str(), "*", FILESYSTEM_FIND_FILES | FILESYSTEM_FIND_RECURSIVE, &files);
|
||||
|
||||
for (FILESYSTEM_FIND_DATA& fd : files)
|
||||
{
|
||||
if (fd.Attributes & FILESYSTEM_FILE_ATTRIBUTE_DIRECTORY)
|
||||
continue;
|
||||
|
||||
TextureReplacementHash hash;
|
||||
ReplacmentType type;
|
||||
if (!ParseReplacementFilename(fd.FileName, &hash, &type))
|
||||
continue;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case ReplacmentType::VRAMWrite:
|
||||
{
|
||||
auto it = m_vram_write_replacements.find(hash);
|
||||
if (it != m_vram_write_replacements.end())
|
||||
{
|
||||
Log_WarningPrintf("Duplicate VRAM write replacement: '%s' and '%s'", it->second.c_str(), fd.FileName.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
m_vram_write_replacements.emplace(hash, std::move(fd.FileName));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Found %zu replacement VRAM writes for '%s'", m_vram_write_replacements.size(), m_game_id.c_str());
|
||||
}
|
||||
|
||||
const TextureReplacementTexture* TextureReplacements::LoadTexture(const std::string& filename)
|
||||
{
|
||||
auto it = m_texture_cache.find(filename);
|
||||
if (it != m_texture_cache.end())
|
||||
return &it->second;
|
||||
|
||||
Common::RGBA8Image image;
|
||||
if (!Common::LoadImageFromFile(&image, filename.c_str()))
|
||||
{
|
||||
Log_ErrorPrintf("Failed to load '%s'", filename.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Log_InfoPrintf("Loaded '%s': %ux%u", filename.c_str(), image.GetWidth(), image.GetHeight());
|
||||
it = m_texture_cache.emplace(filename, std::move(image)).first;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
void TextureReplacements::PreloadTextures()
|
||||
{
|
||||
static constexpr float UPDATE_INTERVAL = 1.0f;
|
||||
|
||||
Common::Timer last_update_time;
|
||||
u32 num_textures_loaded = 0;
|
||||
const u32 total_textures = static_cast<u32>(m_vram_write_replacements.size());
|
||||
|
||||
#define UPDATE_PROGRESS() \
|
||||
if (last_update_time.GetTimeSeconds() >= UPDATE_INTERVAL) \
|
||||
{ \
|
||||
g_host_interface->DisplayLoadingScreen("Preloading replacement textures...", 0, static_cast<int>(total_textures), \
|
||||
static_cast<int>(num_textures_loaded)); \
|
||||
last_update_time.Reset(); \
|
||||
}
|
||||
|
||||
for (const auto& it : m_vram_write_replacements)
|
||||
{
|
||||
UPDATE_PROGRESS();
|
||||
|
||||
LoadTexture(it.second);
|
||||
num_textures_loaded++;
|
||||
}
|
||||
|
||||
#undef UPDATE_PROGRESS
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
#pragma once
|
||||
#include "common/hash_combine.h"
|
||||
#include "common/image.h"
|
||||
#include "types.h"
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct TextureReplacementHash
|
||||
{
|
||||
u64 low;
|
||||
u64 high;
|
||||
|
||||
std::string ToString() const;
|
||||
bool ParseString(const std::string_view& sv);
|
||||
|
||||
bool operator<(const TextureReplacementHash& rhs) const { return std::tie(low, high) < std::tie(rhs.low, rhs.high); }
|
||||
bool operator==(const TextureReplacementHash& rhs) const { return low == rhs.low && high == rhs.high; }
|
||||
bool operator!=(const TextureReplacementHash& rhs) const { return low != rhs.low || high != rhs.high; }
|
||||
};
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<TextureReplacementHash>
|
||||
{
|
||||
size_t operator()(const TextureReplacementHash& h) const
|
||||
{
|
||||
size_t hash_hash = std::hash<u64>{}(h.low);
|
||||
hash_combine(hash_hash, h.high);
|
||||
return hash_hash;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
using TextureReplacementTexture = Common::RGBA8Image;
|
||||
|
||||
class TextureReplacements
|
||||
{
|
||||
public:
|
||||
enum class ReplacmentType
|
||||
{
|
||||
VRAMWrite
|
||||
};
|
||||
|
||||
TextureReplacements();
|
||||
~TextureReplacements();
|
||||
|
||||
const std::string GetGameID() const { return m_game_id; }
|
||||
void SetGameID(std::string game_id);
|
||||
|
||||
void Reload();
|
||||
|
||||
const TextureReplacementTexture* GetVRAMWriteReplacement(u32 width, u32 height, const void* pixels);
|
||||
void DumpVRAMWrite(u32 width, u32 height, const void* pixels);
|
||||
|
||||
void Shutdown();
|
||||
|
||||
private:
|
||||
struct ReplacementHashMapHash
|
||||
{
|
||||
size_t operator()(const TextureReplacementHash& hash);
|
||||
};
|
||||
|
||||
using VRAMWriteReplacementMap = std::unordered_map<TextureReplacementHash, std::string>;
|
||||
using TextureCache = std::unordered_map<std::string, TextureReplacementTexture>;
|
||||
|
||||
static bool ParseReplacementFilename(const std::string& filename, TextureReplacementHash* replacement_hash,
|
||||
ReplacmentType* replacement_type);
|
||||
|
||||
std::string GetSourceDirectory() const;
|
||||
|
||||
TextureReplacementHash GetVRAMWriteHash(u32 width, u32 height, const void* pixels) const;
|
||||
std::string GetVRAMWriteDumpFilename(u32 width, u32 height, const void* pixels) const;
|
||||
|
||||
void FindTextures(const std::string& dir);
|
||||
|
||||
const TextureReplacementTexture* LoadTexture(const std::string& filename);
|
||||
void PreloadTextures();
|
||||
void PurgeUnreferencedTexturesFromCache();
|
||||
|
||||
std::string m_game_id;
|
||||
|
||||
TextureCache m_texture_cache;
|
||||
|
||||
VRAMWriteReplacementMap m_vram_write_replacements;
|
||||
};
|
||||
|
||||
extern TextureReplacements g_texture_replacements;
|
Loading…
Reference in New Issue