GPU: Add texture hashing and dumping support
This commit is contained in:
parent
cf2599b6c7
commit
aeba30c24a
|
@ -100,7 +100,7 @@ set(RECOMPILER_SRCS
|
|||
|
||||
target_include_directories(core PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_include_directories(core PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/..")
|
||||
target_link_libraries(core PUBLIC Threads::Threads common zlib vulkan-loader)
|
||||
target_link_libraries(core PUBLIC Threads::Threads common zlib vulkan-loader xxhash)
|
||||
target_link_libraries(core PRIVATE glad stb)
|
||||
|
||||
if(WIN32)
|
||||
|
|
|
@ -158,6 +158,9 @@
|
|||
<ProjectReference Include="..\..\dep\zlib\zlib.vcxproj">
|
||||
<Project>{7ff9fdb9-d504-47db-a16a-b08071999620}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\dep\xxhash\xxhash.vcxproj">
|
||||
<Project>{09553c96-9f39-49bf-8ae6-7acbd07c410c}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{ee054e08-3799-4a59-a422-18259c105ffd}</Project>
|
||||
</ProjectReference>
|
||||
|
@ -306,10 +309,10 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_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\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -332,10 +335,10 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_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\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -358,10 +361,10 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_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\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -387,10 +390,10 @@
|
|||
</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_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\xbyak\xbyak;$(SolutionDir)dep\zlib\include;$(SolutionDir)dep\vulkan-loader\include;$(SolutionDir)dep\xxhash\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
|
@ -417,8 +420,8 @@
|
|||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=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>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_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)dep\xxhash\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -444,8 +447,8 @@
|
|||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=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>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_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)dep\xxhash\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -472,8 +475,8 @@
|
|||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=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>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_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)dep\xxhash\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>false</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -499,8 +502,8 @@
|
|||
</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=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>
|
||||
<PreprocessorDefinitions>WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_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)dep\xxhash\include;$(SolutionDir)src;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
|
@ -583,4 +586,4 @@
|
|||
<IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename>
|
||||
<ProjectName>core</ProjectName>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
212
src/core/gpu.cpp
212
src/core/gpu.cpp
|
@ -1,8 +1,10 @@
|
|||
#include "gpu.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/hash_combine.h"
|
||||
#include "common/heap_array.h"
|
||||
#include "common/log.h"
|
||||
#include "common/state_wrapper.h"
|
||||
#include "common/string.h"
|
||||
#include "dma.h"
|
||||
#include "host_display.h"
|
||||
#include "host_interface.h"
|
||||
|
@ -10,10 +12,12 @@
|
|||
#include "stb_image_write.h"
|
||||
#include "system.h"
|
||||
#include "timers.h"
|
||||
#include <cinttypes>
|
||||
#include <cmath>
|
||||
#ifdef WITH_IMGUI
|
||||
#include "imgui.h"
|
||||
#endif
|
||||
#include <xxhash.h>
|
||||
Log_SetChannel(GPU);
|
||||
|
||||
std::unique_ptr<GPU> g_gpu;
|
||||
|
@ -1184,6 +1188,8 @@ void GPU::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
DumpDirectVRAMWrite(data, x, y, width, height);
|
||||
}
|
||||
|
||||
void GPU::CopyVRAM(u32 src_x, u32 src_y, u32 dst_x, u32 dst_y, u32 width, u32 height)
|
||||
|
@ -1319,6 +1325,212 @@ void GPU::SetTextureWindow(u32 value)
|
|||
m_draw_mode.texture_window_changed = true;
|
||||
}
|
||||
|
||||
GPU::VRAMHashType GPU::GetVRAMHash(u32 x, u32 y, u32 width, u32 height) const
|
||||
{
|
||||
static_assert(sizeof(VRAMHashType) == sizeof(XXH64_hash_t));
|
||||
|
||||
const u32 max_y = y + height;
|
||||
DebugAssert(max_y <= VRAM_HEIGHT);
|
||||
|
||||
XXH64_state_t hash_state;
|
||||
XXH64_reset(&hash_state, UINT64_C(0x4AF073341EF24663));
|
||||
|
||||
for (u32 current_y = y; current_y < max_y; current_y++)
|
||||
XXH64_update(&hash_state, &m_vram_ptr[current_y * VRAM_WIDTH + x], sizeof(u16) * width);
|
||||
|
||||
return XXH64_digest(&hash_state);
|
||||
}
|
||||
|
||||
std::size_t GPU::TextureHashHasher::operator()(const TextureHash& th) const
|
||||
{
|
||||
std::size_t h = 0;
|
||||
hash_combine(h, th.texture_hash, th.palette_hash);
|
||||
return h;
|
||||
}
|
||||
|
||||
GPU::TextureHash GPU::GetCurrentTextureHash() const
|
||||
{
|
||||
switch (m_draw_mode.GetTextureMode())
|
||||
{
|
||||
case TextureMode::Palette4Bit:
|
||||
{
|
||||
return TextureHash{TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT,
|
||||
GetVRAMHash(m_draw_mode.texture_page_x, m_draw_mode.texture_page_y, TEXTURE_PAGE_WIDTH / 4,
|
||||
TEXTURE_PAGE_HEIGHT),
|
||||
GetVRAMHash(m_draw_mode.texture_palette_x, m_draw_mode.texture_palette_y, 16, 1)};
|
||||
}
|
||||
|
||||
case TextureMode::Palette8Bit:
|
||||
{
|
||||
return TextureHash{TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT,
|
||||
GetVRAMHash(m_draw_mode.texture_page_x, m_draw_mode.texture_page_y, TEXTURE_PAGE_WIDTH / 2,
|
||||
TEXTURE_PAGE_HEIGHT),
|
||||
GetVRAMHash(m_draw_mode.texture_palette_x, m_draw_mode.texture_palette_y, 256, 1)};
|
||||
}
|
||||
|
||||
case TextureMode::Direct16Bit:
|
||||
case TextureMode::Reserved_Direct16Bit:
|
||||
{
|
||||
return TextureHash{
|
||||
TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT,
|
||||
GetVRAMHash(m_draw_mode.texture_page_x, m_draw_mode.texture_page_y, TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT),
|
||||
static_cast<VRAMHashType>(0)};
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return TextureHash{};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<GPU::TextureHash, GPU::TextureHashHasher> GPU::dumped_textures;
|
||||
|
||||
void GPU::DumpCurrentTexture() const
|
||||
{
|
||||
const TextureHash hash = GetCurrentTextureHash();
|
||||
if (dumped_textures.find(hash) != dumped_textures.end())
|
||||
return;
|
||||
|
||||
dumped_textures.insert(hash);
|
||||
|
||||
const TextureMode mode = m_draw_mode.GetTextureMode();
|
||||
const u32 clut_size = (mode == TextureMode::Palette4Bit ? 16 : (mode == TextureMode::Palette8Bit ? 256 : 0));
|
||||
std::vector<u32> clut;
|
||||
clut.reserve(clut_size);
|
||||
const u16* clut_ptr = &m_vram_ptr[m_draw_mode.texture_palette_y * VRAM_WIDTH + m_draw_mode.texture_palette_x];
|
||||
for (u32 i = 0; i < clut_size; i++)
|
||||
clut.push_back(RGBA5551ToRGBA8888ForExport(*clut_ptr++));
|
||||
|
||||
std::vector<u32> texture_data;
|
||||
texture_data.reserve(TEXTURE_PAGE_WIDTH * TEXTURE_PAGE_HEIGHT);
|
||||
switch (mode)
|
||||
{
|
||||
case TextureMode::Palette4Bit:
|
||||
{
|
||||
for (u32 row = 0; row < TEXTURE_PAGE_HEIGHT; row++)
|
||||
{
|
||||
const u16* row_ptr = &m_vram_ptr[(m_draw_mode.texture_page_y + row) * VRAM_WIDTH + m_draw_mode.texture_page_x];
|
||||
for (u32 col = 0; col < TEXTURE_PAGE_WIDTH / 4; col++)
|
||||
{
|
||||
u16 packed_pixels = *(row_ptr++);
|
||||
texture_data.push_back(clut[packed_pixels & 0x0F]);
|
||||
packed_pixels >>= 4;
|
||||
texture_data.push_back(clut[packed_pixels & 0x0F]);
|
||||
packed_pixels >>= 4;
|
||||
texture_data.push_back(clut[packed_pixels & 0x0F]);
|
||||
packed_pixels >>= 4;
|
||||
texture_data.push_back(clut[packed_pixels & 0x0F]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TextureMode::Palette8Bit:
|
||||
{
|
||||
for (u32 row = 0; row < TEXTURE_PAGE_HEIGHT; row++)
|
||||
{
|
||||
const u16* row_ptr = &m_vram_ptr[(m_draw_mode.texture_page_y + row) * VRAM_WIDTH + m_draw_mode.texture_page_x];
|
||||
for (u32 col = 0; col < TEXTURE_PAGE_WIDTH / 2; col++)
|
||||
{
|
||||
u16 packed_pixels = *(row_ptr++);
|
||||
texture_data.push_back(clut[packed_pixels & 0xFF]);
|
||||
packed_pixels >>= 8;
|
||||
texture_data.push_back(clut[packed_pixels & 0xFF]);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TextureMode::Direct16Bit:
|
||||
case TextureMode::Reserved_Direct16Bit:
|
||||
{
|
||||
for (u32 row = 0; row < TEXTURE_PAGE_HEIGHT; row++)
|
||||
{
|
||||
const u16* row_ptr = &m_vram_ptr[(m_draw_mode.texture_page_y + row) * VRAM_WIDTH + m_draw_mode.texture_page_x];
|
||||
for (u32 col = 0; col < TEXTURE_PAGE_WIDTH; col++)
|
||||
texture_data.push_back(RGBA5551ToRGBA8888ForExport(*(row_ptr++)));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
texture_data.resize(TEXTURE_PAGE_WIDTH * TEXTURE_PAGE_HEIGHT);
|
||||
break;
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!System::GetRunningCode().empty())
|
||||
path = g_host_interface->GetUserDirectoryRelativePath("dump/textures/%s", System::GetRunningCode().c_str());
|
||||
else
|
||||
path = g_host_interface->GetUserDirectoryRelativePath("dump/textures");
|
||||
|
||||
if (!FileSystem::DirectoryExists(path.c_str()) && !FileSystem::CreateDirectory(path.c_str(), true))
|
||||
{
|
||||
Log_WarningPrintf("Failed to create texture dump directory '%s'", path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (hash.palette_hash != 0)
|
||||
path += TinyString::FromFormat("/%" PRIx64 "_%" PRIx64 ".png", hash.texture_hash, hash.palette_hash);
|
||||
else
|
||||
path += TinyString::FromFormat("/%" PRIx64 ".png", hash.texture_hash);
|
||||
|
||||
if (!stbi_write_png(path.c_str(), TEXTURE_PAGE_WIDTH, TEXTURE_PAGE_HEIGHT, 4, texture_data.data(),
|
||||
TEXTURE_PAGE_WIDTH * sizeof(u32)))
|
||||
{
|
||||
Log_WarningPrintf("Failed to dump texture to '%s'", path.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void GPU::DumpDirectVRAMWrite(const void* pixels, u32 x, u32 y, u32 width, u32 height)
|
||||
{
|
||||
const u32 WIDTH_THRESHOLD = 128;
|
||||
const u32 HEIGHT_THRESHOLD = 128;
|
||||
if (width < WIDTH_THRESHOLD || height < HEIGHT_THRESHOLD)
|
||||
return;
|
||||
|
||||
TextureHash hash{width, height, GetVRAMHash(x, y, width, height), 0};
|
||||
if (dumped_textures.find(hash) != dumped_textures.end())
|
||||
return;
|
||||
|
||||
dumped_textures.insert(hash);
|
||||
|
||||
std::vector<u32> texture_data;
|
||||
texture_data.reserve(width * height);
|
||||
|
||||
for (u32 row = 0; row < height; row++)
|
||||
{
|
||||
const u16* row_ptr = &m_vram_ptr[(y + row) * VRAM_WIDTH + x];
|
||||
for (u32 col = 0; col < width; col++)
|
||||
texture_data.push_back(RGBA5551ToRGBA8888ForExport(*(row_ptr++)));
|
||||
}
|
||||
|
||||
std::string path;
|
||||
if (!System::GetRunningCode().empty())
|
||||
path = g_host_interface->GetUserDirectoryRelativePath("dump/textures/vram_%s", System::GetRunningCode().c_str());
|
||||
else
|
||||
path = g_host_interface->GetUserDirectoryRelativePath("dump/textures");
|
||||
|
||||
if (!FileSystem::DirectoryExists(path.c_str()) && !FileSystem::CreateDirectory(path.c_str(), true))
|
||||
{
|
||||
Log_WarningPrintf("Failed to create texture dump directory '%s'", path.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (hash.palette_hash != 0)
|
||||
path += TinyString::FromFormat("/%" PRIx64 "_%" PRIx64 ".png", hash.texture_hash, hash.palette_hash);
|
||||
else
|
||||
path += TinyString::FromFormat("/%" PRIx64 ".png", hash.texture_hash);
|
||||
|
||||
if (!stbi_write_png(path.c_str(), width, height, 4, texture_data.data(), width * sizeof(u32)))
|
||||
{
|
||||
Log_WarningPrintf("Failed to dump texture to '%s'", path.c_str());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool GPU::DumpVRAMToFile(const char* filename, u32 width, u32 height, u32 stride, const void* buffer, bool remove_alpha)
|
||||
{
|
||||
auto fp = FileSystem::OpenManagedCFile(filename, "wb");
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
class StateWrapper;
|
||||
|
||||
|
@ -109,6 +110,31 @@ public:
|
|||
PAL_TOTAL_LINES = 314,
|
||||
};
|
||||
|
||||
using VRAMHashType = u64;
|
||||
|
||||
struct TextureHash
|
||||
{
|
||||
u32 width, height;
|
||||
VRAMHashType texture_hash;
|
||||
VRAMHashType palette_hash;
|
||||
|
||||
ALWAYS_INLINE bool operator==(const TextureHash& h) const
|
||||
{
|
||||
return (texture_hash == h.texture_hash && palette_hash == h.palette_hash);
|
||||
}
|
||||
ALWAYS_INLINE bool operator!=(const TextureHash& h) const
|
||||
{
|
||||
return (texture_hash != h.texture_hash || palette_hash != h.palette_hash);
|
||||
}
|
||||
};
|
||||
|
||||
struct TextureHashHasher
|
||||
{
|
||||
std::size_t operator()(const TextureHash& th) const;
|
||||
};
|
||||
|
||||
static std::unordered_set<TextureHash, TextureHashHasher> dumped_textures;
|
||||
|
||||
// 4x4 dither matrix.
|
||||
static constexpr s32 DITHER_MATRIX[DITHER_MATRIX_SIZE][DITHER_MATRIX_SIZE] = {{-4, +0, -3, +1}, // row 0
|
||||
{+2, -2, +3, -1}, // row 1
|
||||
|
@ -235,6 +261,22 @@ protected:
|
|||
return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24);
|
||||
}
|
||||
|
||||
static constexpr u32 RGBA5551ToRGBA8888ForExport(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 ? 127 : 255;
|
||||
|
||||
return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24);
|
||||
}
|
||||
|
||||
static constexpr u16 RGBA8888ToRGBA5551(u32 color)
|
||||
{
|
||||
const u16 r = Truncate16((color >> 3) & 0x1Fu);
|
||||
|
@ -405,6 +447,17 @@ protected:
|
|||
/// Sets/decodes texture window bits.
|
||||
void SetTextureWindow(u32 value);
|
||||
|
||||
/// Returns the hash for a VRAM region.
|
||||
VRAMHashType GetVRAMHash(u32 x, u32 y, u32 width, u32 height) const;
|
||||
|
||||
/// Returns the hash for the currently-bound texture.
|
||||
TextureHash GetCurrentTextureHash() const;
|
||||
|
||||
/// Dumps any currently-bound texture if the files do not exist.
|
||||
void DumpCurrentTexture() const;
|
||||
|
||||
void DumpDirectVRAMWrite(const void* pixels, u32 x, u32 y, u32 width, u32 height);
|
||||
|
||||
u32 ReadGPUREAD();
|
||||
void FinishVRAMWrite();
|
||||
|
||||
|
|
|
@ -348,6 +348,8 @@ bool GPU::HandleRenderPolygonCommand()
|
|||
SetDrawMode((texpage_attribute & DrawMode::Reg::POLYGON_TEXPAGE_MASK) |
|
||||
(m_draw_mode.mode_reg.bits & ~DrawMode::Reg::POLYGON_TEXPAGE_MASK));
|
||||
SetTexturePalette(Truncate16(FifoPeek(2) >> 16));
|
||||
if (m_draw_mode.IsUsingPalette())
|
||||
DumpCurrentTexture();
|
||||
}
|
||||
|
||||
m_stats.num_vertices += num_vertices;
|
||||
|
@ -372,7 +374,11 @@ bool GPU::HandleRenderRectangleCommand()
|
|||
SynchronizeCRTC();
|
||||
|
||||
if (rc.texture_enable)
|
||||
{
|
||||
SetTexturePalette(Truncate16(FifoPeek(2) >> 16));
|
||||
if (m_draw_mode.IsUsingPalette())
|
||||
DumpCurrentTexture();
|
||||
}
|
||||
|
||||
const TickCount setup_ticks = 16;
|
||||
AddCommandTicks(setup_ticks);
|
||||
|
|
Loading…
Reference in New Issue