diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index bc2fc30bc..b6b8d9b40 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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) diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index e0121765a..c76a5106c 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -158,6 +158,9 @@ {7ff9fdb9-d504-47db-a16a-b08071999620} + + {09553c96-9f39-49bf-8ae6-7acbd07c410c} + {ee054e08-3799-4a59-a422-18259c105ffd} @@ -306,10 +309,10 @@ Level4 Disabled - WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(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) + $(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) true false stdcpp17 @@ -332,10 +335,10 @@ Level4 Disabled - WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) true ProgramDatabase - $(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) + $(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) true false stdcpp17 @@ -358,10 +361,10 @@ Level4 Disabled - WITH_IMGUI=1;WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(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) true ProgramDatabase - $(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) + $(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) Default true false @@ -387,10 +390,10 @@ Level4 Disabled - WITH_IMGUI=1;WITH_RECOMPILER=1;_ITERATOR_DEBUG_LEVEL=1;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUGFAST;_DEBUG;_CONSOLE;_LIB;%(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) true ProgramDatabase - $(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) + $(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) Default true false @@ -417,8 +420,8 @@ MaxSpeed true - WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(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) + WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(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) true false stdcpp17 @@ -444,8 +447,8 @@ MaxSpeed true - WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(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) + WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(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) true true stdcpp17 @@ -472,8 +475,8 @@ MaxSpeed true - WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(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) + WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(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) true false stdcpp17 @@ -499,8 +502,8 @@ MaxSpeed true - WITH_IMGUI=1;WITH_RECOMPILER=1;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - $(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) + WITH_IMGUI=1;WITH_RECOMPILER=1;XXH_STATIC_LINKING_ONLY;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) + $(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) true true stdcpp17 @@ -583,4 +586,4 @@ true core - \ No newline at end of file + diff --git a/src/core/gpu.cpp b/src/core/gpu.cpp index 086865c83..cb8210305 100644 --- a/src/core/gpu.cpp +++ b/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 #include #ifdef WITH_IMGUI #include "imgui.h" #endif +#include Log_SetChannel(GPU); std::unique_ptr 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(0)}; + } + + default: + { + return TextureHash{}; + } + } +} + +std::unordered_set 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 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 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 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"); diff --git a/src/core/gpu.h b/src/core/gpu.h index 54078114b..5c61b6bea 100644 --- a/src/core/gpu.h +++ b/src/core/gpu.h @@ -10,6 +10,7 @@ #include #include #include +#include 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 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(); diff --git a/src/core/gpu_commands.cpp b/src/core/gpu_commands.cpp index 1f379af3f..6f161c2d3 100644 --- a/src/core/gpu_commands.cpp +++ b/src/core/gpu_commands.cpp @@ -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);