diff options
| author | 2014-08-16 14:06:40 +0200 | |
|---|---|---|
| committer | 2014-08-25 22:03:18 +0200 | |
| commit | c4691b784bd7746c5845df00a82ea0909b37ec0f (patch) | |
| tree | 635fa4cea0fa7c487217c8799cb90c45ebc12841 /src | |
| parent | Pica/Math: Improved the design of the Vec2/Vec3/Vec4 classes and simplified r... (diff) | |
| download | yuzu-c4691b784bd7746c5845df00a82ea0909b37ec0f.tar.gz yuzu-c4691b784bd7746c5845df00a82ea0909b37ec0f.tar.xz yuzu-c4691b784bd7746c5845df00a82ea0909b37ec0f.zip | |
Pica: Add support for dumping textures.
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/citra_qt/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/debug_utils/debug_utils.cpp | 131 | ||||
| -rw-r--r-- | src/video_core/debug_utils/debug_utils.h | 2 | ||||
| -rw-r--r-- | src/video_core/pica.h | 45 |
5 files changed, 179 insertions, 3 deletions
diff --git a/src/citra/CMakeLists.txt b/src/citra/CMakeLists.txt index b1ab67598..1cbe22cc0 100644 --- a/src/citra/CMakeLists.txt +++ b/src/citra/CMakeLists.txt | |||
| @@ -12,7 +12,7 @@ add_executable(citra ${SRCS} ${HEADERS}) | |||
| 12 | if (APPLE) | 12 | if (APPLE) |
| 13 | target_link_libraries(citra core common video_core iconv pthread ${COREFOUNDATION_LIBRARY} ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} ${GLFW_LIBRARIES}) | 13 | target_link_libraries(citra core common video_core iconv pthread ${COREFOUNDATION_LIBRARY} ${OPENGL_LIBRARIES} ${GLEW_LIBRARY} ${GLFW_LIBRARIES}) |
| 14 | else() | 14 | else() |
| 15 | target_link_libraries(citra core common video_core GLEW pthread X11 Xxf86vm Xi Xcursor ${OPENGL_LIBRARIES} ${GLFW_LIBRARIES} rt ${X11_Xrandr_LIB} ${X11_xv86vmode_LIB}) | 15 | target_link_libraries(citra core common video_core GLEW pthread X11 Xxf86vm Xi Xcursor ${OPENGL_LIBRARIES} ${GLFW_LIBRARIES} rt ${X11_Xrandr_LIB} ${X11_xv86vmode_LIB} ${PNG_LIBRARIES}) |
| 16 | endif() | 16 | endif() |
| 17 | 17 | ||
| 18 | #install(TARGETS citra RUNTIME DESTINATION ${bindir}) | 18 | #install(TARGETS citra RUNTIME DESTINATION ${bindir}) |
diff --git a/src/citra_qt/CMakeLists.txt b/src/citra_qt/CMakeLists.txt index ff1fbc460..8ad6759b7 100644 --- a/src/citra_qt/CMakeLists.txt +++ b/src/citra_qt/CMakeLists.txt | |||
| @@ -47,7 +47,7 @@ else() | |||
| 47 | set(RT_LIBRARY rt) | 47 | set(RT_LIBRARY rt) |
| 48 | endif() | 48 | endif() |
| 49 | 49 | ||
| 50 | target_link_libraries(citra-qt core common video_core qhexedit ${ICONV_LIBRARY} ${COREFOUNDATION_LIBRARY} ${QT_LIBRARIES} ${OPENGL_LIBRARIES} ${RT_LIBRARY} ${GLEW_LIBRARY}) | 50 | target_link_libraries(citra-qt core common video_core qhexedit ${ICONV_LIBRARY} ${COREFOUNDATION_LIBRARY} ${QT_LIBRARIES} ${OPENGL_LIBRARIES} ${RT_LIBRARY} ${GLEW_LIBRARY} ${PNG_LIBRARIES}) |
| 51 | if(USE_QT5) | 51 | if(USE_QT5) |
| 52 | target_link_libraries(citra-qt Qt5::Gui Qt5::Widgets Qt5::OpenGL) | 52 | target_link_libraries(citra-qt Qt5::Gui Qt5::Widgets Qt5::OpenGL) |
| 53 | endif() | 53 | endif() |
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index 1bbc0330c..753de41e8 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -3,10 +3,17 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <map> | ||
| 6 | #include <fstream> | 7 | #include <fstream> |
| 7 | #include <mutex> | 8 | #include <mutex> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | 10 | ||
| 11 | #ifdef HAVE_PNG | ||
| 12 | #include <png.h> | ||
| 13 | #endif | ||
| 14 | |||
| 15 | #include "common/file_util.h" | ||
| 16 | |||
| 10 | #include "video_core/pica.h" | 17 | #include "video_core/pica.h" |
| 11 | 18 | ||
| 12 | #include "debug_utils.h" | 19 | #include "debug_utils.h" |
| @@ -315,6 +322,130 @@ std::unique_ptr<PicaTrace> FinishPicaTracing() | |||
| 315 | return std::move(ret); | 322 | return std::move(ret); |
| 316 | } | 323 | } |
| 317 | 324 | ||
| 325 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data) { | ||
| 326 | // NOTE: Permanently enabling this just trashes hard disks for no reason. | ||
| 327 | // Hence, this is currently disabled. | ||
| 328 | return; | ||
| 329 | |||
| 330 | #ifndef HAVE_PNG | ||
| 331 | return; | ||
| 332 | #else | ||
| 333 | if (!data) | ||
| 334 | return; | ||
| 335 | |||
| 336 | // Write data to file | ||
| 337 | static int dump_index = 0; | ||
| 338 | std::string filename = std::string("texture_dump") + std::to_string(++dump_index) + std::string(".png"); | ||
| 339 | u32 row_stride = texture_config.width * 3; | ||
| 340 | |||
| 341 | u8* buf; | ||
| 342 | |||
| 343 | char title[] = "Citra texture dump"; | ||
| 344 | char title_key[] = "Title"; | ||
| 345 | png_structp png_ptr = nullptr; | ||
| 346 | png_infop info_ptr = nullptr; | ||
| 347 | |||
| 348 | // Open file for writing (binary mode) | ||
| 349 | File::IOFile fp(filename, "wb"); | ||
| 350 | |||
| 351 | // Initialize write structure | ||
| 352 | png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); | ||
| 353 | if (png_ptr == nullptr) { | ||
| 354 | ERROR_LOG(GPU, "Could not allocate write struct\n"); | ||
| 355 | goto finalise; | ||
| 356 | |||
| 357 | } | ||
| 358 | |||
| 359 | // Initialize info structure | ||
| 360 | info_ptr = png_create_info_struct(png_ptr); | ||
| 361 | if (info_ptr == nullptr) { | ||
| 362 | ERROR_LOG(GPU, "Could not allocate info struct\n"); | ||
| 363 | goto finalise; | ||
| 364 | } | ||
| 365 | |||
| 366 | // Setup Exception handling | ||
| 367 | if (setjmp(png_jmpbuf(png_ptr))) { | ||
| 368 | ERROR_LOG(GPU, "Error during png creation\n"); | ||
| 369 | goto finalise; | ||
| 370 | } | ||
| 371 | |||
| 372 | png_init_io(png_ptr, fp.GetHandle()); | ||
| 373 | |||
| 374 | // Write header (8 bit colour depth) | ||
| 375 | png_set_IHDR(png_ptr, info_ptr, texture_config.width, texture_config.height, | ||
| 376 | 8, PNG_COLOR_TYPE_RGB /*_ALPHA*/, PNG_INTERLACE_NONE, | ||
| 377 | PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); | ||
| 378 | |||
| 379 | png_text title_text; | ||
| 380 | title_text.compression = PNG_TEXT_COMPRESSION_NONE; | ||
| 381 | title_text.key = title_key; | ||
| 382 | title_text.text = title; | ||
| 383 | png_set_text(png_ptr, info_ptr, &title_text, 1); | ||
| 384 | |||
| 385 | png_write_info(png_ptr, info_ptr); | ||
| 386 | |||
| 387 | buf = new u8[row_stride * texture_config.height]; | ||
| 388 | for (int y = 0; y < texture_config.height; ++y) { | ||
| 389 | for (int x = 0; x < texture_config.width; ++x) { | ||
| 390 | // Images are split into 8x8 tiles. Each tile is composed of four 4x4 subtiles each | ||
| 391 | // of which is composed of four 2x2 subtiles each of which is composed of four texels. | ||
| 392 | // Each structure is embedded into the next-bigger one in a diagonal pattern, e.g. | ||
| 393 | // texels are laid out in a 2x2 subtile like this: | ||
| 394 | // 2 3 | ||
| 395 | // 0 1 | ||
| 396 | // | ||
| 397 | // The full 8x8 tile has the texels arranged like this: | ||
| 398 | // | ||
| 399 | // 42 43 46 47 58 59 62 63 | ||
| 400 | // 40 41 44 45 56 57 60 61 | ||
| 401 | // 34 35 38 39 50 51 54 55 | ||
| 402 | // 32 33 36 37 48 49 52 53 | ||
| 403 | // 10 11 14 15 26 27 30 31 | ||
| 404 | // 08 09 12 13 24 25 28 29 | ||
| 405 | // 02 03 06 07 18 19 22 23 | ||
| 406 | // 00 01 04 05 16 17 20 21 | ||
| 407 | int texel_index_within_tile = 0; | ||
| 408 | for (int block_size_index = 0; block_size_index < 3; ++block_size_index) { | ||
| 409 | int sub_tile_width = 1 << block_size_index; | ||
| 410 | int sub_tile_height = 1 << block_size_index; | ||
| 411 | |||
| 412 | int sub_tile_index = (x & sub_tile_width) << block_size_index; | ||
| 413 | sub_tile_index += 2 * ((y & sub_tile_height) << block_size_index); | ||
| 414 | texel_index_within_tile += sub_tile_index; | ||
| 415 | } | ||
| 416 | |||
| 417 | const int block_width = 8; | ||
| 418 | const int block_height = 8; | ||
| 419 | |||
| 420 | int coarse_x = (x / block_width) * block_width; | ||
| 421 | int coarse_y = (y / block_height) * block_height; | ||
| 422 | |||
| 423 | u8* source_ptr = (u8*)data + coarse_x * block_height * 3 + coarse_y * row_stride + texel_index_within_tile * 3; | ||
| 424 | buf[3 * x + y * row_stride ] = source_ptr[2]; | ||
| 425 | buf[3 * x + y * row_stride + 1] = source_ptr[1]; | ||
| 426 | buf[3 * x + y * row_stride + 2] = source_ptr[0]; | ||
| 427 | } | ||
| 428 | } | ||
| 429 | |||
| 430 | // Write image data | ||
| 431 | for (auto y = 0; y < texture_config.height; ++y) | ||
| 432 | { | ||
| 433 | u8* row_ptr = (u8*)buf + y * row_stride; | ||
| 434 | u8* ptr = row_ptr; | ||
| 435 | png_write_row(png_ptr, row_ptr); | ||
| 436 | } | ||
| 437 | |||
| 438 | delete[] buf; | ||
| 439 | |||
| 440 | // End write | ||
| 441 | png_write_end(png_ptr, nullptr); | ||
| 442 | |||
| 443 | finalise: | ||
| 444 | if (info_ptr != nullptr) png_free_data(png_ptr, info_ptr, PNG_FREE_ALL, -1); | ||
| 445 | if (png_ptr != nullptr) png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); | ||
| 446 | #endif | ||
| 447 | } | ||
| 448 | |||
| 318 | } // namespace | 449 | } // namespace |
| 319 | 450 | ||
| 320 | } // namespace | 451 | } // namespace |
diff --git a/src/video_core/debug_utils/debug_utils.h b/src/video_core/debug_utils/debug_utils.h index 023500066..49b19b28a 100644 --- a/src/video_core/debug_utils/debug_utils.h +++ b/src/video_core/debug_utils/debug_utils.h | |||
| @@ -59,6 +59,8 @@ bool IsPicaTracing(); | |||
| 59 | void OnPicaRegWrite(u32 id, u32 value); | 59 | void OnPicaRegWrite(u32 id, u32 value); |
| 60 | std::unique_ptr<PicaTrace> FinishPicaTracing(); | 60 | std::unique_ptr<PicaTrace> FinishPicaTracing(); |
| 61 | 61 | ||
| 62 | void DumpTexture(const Pica::Regs::TextureConfig& texture_config, u8* data); | ||
| 63 | |||
| 62 | } // namespace | 64 | } // namespace |
| 63 | 65 | ||
| 64 | } // namespace | 66 | } // namespace |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index fe886c16f..f288615b8 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -94,7 +94,46 @@ struct Regs { | |||
| 94 | BitField<16, 16, u32> y; | 94 | BitField<16, 16, u32> y; |
| 95 | } viewport_corner; | 95 | } viewport_corner; |
| 96 | 96 | ||
| 97 | INSERT_PADDING_WORDS(0xa7); | 97 | INSERT_PADDING_WORDS(0x18); |
| 98 | |||
| 99 | struct TextureConfig { | ||
| 100 | INSERT_PADDING_WORDS(0x1); | ||
| 101 | |||
| 102 | union { | ||
| 103 | BitField< 0, 16, u32> height; | ||
| 104 | BitField<16, 16, u32> width; | ||
| 105 | }; | ||
| 106 | |||
| 107 | INSERT_PADDING_WORDS(0x2); | ||
| 108 | |||
| 109 | u32 address; | ||
| 110 | |||
| 111 | u32 GetPhysicalAddress() { | ||
| 112 | return DecodeAddressRegister(address) - Memory::FCRAM_PADDR + Memory::HEAP_GSP_VADDR; | ||
| 113 | } | ||
| 114 | |||
| 115 | // texture1 and texture2 store the texture format directly after the address | ||
| 116 | // whereas texture0 inserts some additional flags inbetween. | ||
| 117 | // Hence, we store the format separately so that all other parameters can be described | ||
| 118 | // in a single structure. | ||
| 119 | }; | ||
| 120 | |||
| 121 | enum class TextureFormat : u32 { | ||
| 122 | RGBA8 = 0, | ||
| 123 | RGB8 = 1, | ||
| 124 | RGBA5551 = 2, | ||
| 125 | RGB565 = 3, | ||
| 126 | RGBA4 = 4, | ||
| 127 | |||
| 128 | // TODO: Support for the other formats is not implemented, yet. | ||
| 129 | // Seems like they are luminance formats and compressed textures. | ||
| 130 | }; | ||
| 131 | |||
| 132 | TextureConfig texture0; | ||
| 133 | INSERT_PADDING_WORDS(0x8); | ||
| 134 | BitField<0, 4, TextureFormat> texture0_format; | ||
| 135 | |||
| 136 | INSERT_PADDING_WORDS(0x81); | ||
| 98 | 137 | ||
| 99 | struct { | 138 | struct { |
| 100 | enum ColorFormat : u32 { | 139 | enum ColorFormat : u32 { |
| @@ -403,6 +442,8 @@ struct Regs { | |||
| 403 | ADD_FIELD(viewport_depth_range); | 442 | ADD_FIELD(viewport_depth_range); |
| 404 | ADD_FIELD(viewport_depth_far_plane); | 443 | ADD_FIELD(viewport_depth_far_plane); |
| 405 | ADD_FIELD(viewport_corner); | 444 | ADD_FIELD(viewport_corner); |
| 445 | ADD_FIELD(texture0); | ||
| 446 | ADD_FIELD(texture0_format); | ||
| 406 | ADD_FIELD(framebuffer); | 447 | ADD_FIELD(framebuffer); |
| 407 | ADD_FIELD(vertex_attributes); | 448 | ADD_FIELD(vertex_attributes); |
| 408 | ADD_FIELD(index_array); | 449 | ADD_FIELD(index_array); |
| @@ -460,6 +501,8 @@ ASSERT_REG_POSITION(viewport_depth_far_plane, 0x4e); | |||
| 460 | ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); | 501 | ASSERT_REG_POSITION(vs_output_attributes[0], 0x50); |
| 461 | ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); | 502 | ASSERT_REG_POSITION(vs_output_attributes[1], 0x51); |
| 462 | ASSERT_REG_POSITION(viewport_corner, 0x68); | 503 | ASSERT_REG_POSITION(viewport_corner, 0x68); |
| 504 | ASSERT_REG_POSITION(texture0, 0x81); | ||
| 505 | ASSERT_REG_POSITION(texture0_format, 0x8e); | ||
| 463 | ASSERT_REG_POSITION(framebuffer, 0x110); | 506 | ASSERT_REG_POSITION(framebuffer, 0x110); |
| 464 | ASSERT_REG_POSITION(vertex_attributes, 0x200); | 507 | ASSERT_REG_POSITION(vertex_attributes, 0x200); |
| 465 | ASSERT_REG_POSITION(index_array, 0x227); | 508 | ASSERT_REG_POSITION(index_array, 0x227); |