diff options
| author | 2014-12-31 14:48:19 +0100 | |
|---|---|---|
| committer | 2015-01-13 14:42:40 -0800 | |
| commit | f2b74b4fb304dc6b80656a730d3ec3d9f3385b87 (patch) | |
| tree | 332920859205a28d2c8a9395edcaff29e33f5e9e /src | |
| parent | Merge pull request #452 from darkf/mingwagain (diff) | |
| download | yuzu-f2b74b4fb304dc6b80656a730d3ec3d9f3385b87.tar.gz yuzu-f2b74b4fb304dc6b80656a730d3ec3d9f3385b87.tar.xz yuzu-f2b74b4fb304dc6b80656a730d3ec3d9f3385b87.zip | |
Pica/Rasterizer: Add ETC1 texture decompression support.
Diffstat (limited to 'src')
| -rw-r--r-- | src/citra_qt/debugger/graphics_cmdlists.cpp | 2 | ||||
| -rw-r--r-- | src/video_core/debug_utils/debug_utils.cpp | 152 | ||||
| -rw-r--r-- | src/video_core/pica.h | 4 |
3 files changed, 144 insertions, 14 deletions
diff --git a/src/citra_qt/debugger/graphics_cmdlists.cpp b/src/citra_qt/debugger/graphics_cmdlists.cpp index 4a6159fdf..bd420f24a 100644 --- a/src/citra_qt/debugger/graphics_cmdlists.cpp +++ b/src/citra_qt/debugger/graphics_cmdlists.cpp | |||
| @@ -76,6 +76,8 @@ TextureInfoDockWidget::TextureInfoDockWidget(const Pica::DebugUtils::TextureInfo | |||
| 76 | format_choice->addItem(tr("IA4")); | 76 | format_choice->addItem(tr("IA4")); |
| 77 | format_choice->addItem(tr("UNK10")); | 77 | format_choice->addItem(tr("UNK10")); |
| 78 | format_choice->addItem(tr("A4")); | 78 | format_choice->addItem(tr("A4")); |
| 79 | format_choice->addItem(tr("ETC1")); | ||
| 80 | format_choice->addItem(tr("ETC1A4")); | ||
| 79 | format_choice->setCurrentIndex(static_cast<int>(info.format)); | 81 | format_choice->setCurrentIndex(static_cast<int>(info.format)); |
| 80 | connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int))); | 82 | connect(format_choice, SIGNAL(currentIndexChanged(int)), this, SLOT(OnFormatChanged(int))); |
| 81 | 83 | ||
diff --git a/src/video_core/debug_utils/debug_utils.cpp b/src/video_core/debug_utils/debug_utils.cpp index a494465b9..12f0009bd 100644 --- a/src/video_core/debug_utils/debug_utils.cpp +++ b/src/video_core/debug_utils/debug_utils.cpp | |||
| @@ -18,6 +18,7 @@ | |||
| 18 | 18 | ||
| 19 | #include "common/log.h" | 19 | #include "common/log.h" |
| 20 | #include "common/file_util.h" | 20 | #include "common/file_util.h" |
| 21 | #include "common/math_util.h" | ||
| 21 | 22 | ||
| 22 | #include "video_core/color.h" | 23 | #include "video_core/color.h" |
| 23 | #include "video_core/math.h" | 24 | #include "video_core/math.h" |
| @@ -337,25 +338,31 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 337 | i = (i ^ (i << 1)) & 0x1515; // ---2 -1-0 | 338 | i = (i ^ (i << 1)) & 0x1515; // ---2 -1-0 |
| 338 | i = (i | (i >> 7)) & 0x3F; | 339 | i = (i | (i >> 7)) & 0x3F; |
| 339 | 340 | ||
| 340 | source += coarse_y * info.stride; | 341 | if (info.format != Regs::TextureFormat::ETC1 && |
| 341 | const unsigned int offset = coarse_x * block_height + i; | 342 | info.format != Regs::TextureFormat::ETC1A4) { |
| 343 | // TODO(neobrain): Fix code design to unify vertical block offsets! | ||
| 344 | source += coarse_y * info.stride; | ||
| 345 | } | ||
| 346 | const unsigned int offset = coarse_x * block_height; | ||
| 347 | |||
| 348 | // TODO: Assert that width/height are multiples of block dimensions | ||
| 342 | 349 | ||
| 343 | switch (info.format) { | 350 | switch (info.format) { |
| 344 | case Regs::TextureFormat::RGBA8: | 351 | case Regs::TextureFormat::RGBA8: |
| 345 | { | 352 | { |
| 346 | const u8* source_ptr = source + offset * 4; | 353 | const u8* source_ptr = source + offset * 4 + i * 4; |
| 347 | return { source_ptr[3], source_ptr[2], source_ptr[1], disable_alpha ? (u8)255 : source_ptr[0] }; | 354 | return { source_ptr[3], source_ptr[2], source_ptr[1], disable_alpha ? (u8)255 : source_ptr[0] }; |
| 348 | } | 355 | } |
| 349 | 356 | ||
| 350 | case Regs::TextureFormat::RGB8: | 357 | case Regs::TextureFormat::RGB8: |
| 351 | { | 358 | { |
| 352 | const u8* source_ptr = source + offset * 3; | 359 | const u8* source_ptr = source + offset * 3 + i * 3; |
| 353 | return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; | 360 | return { source_ptr[2], source_ptr[1], source_ptr[0], 255 }; |
| 354 | } | 361 | } |
| 355 | 362 | ||
| 356 | case Regs::TextureFormat::RGBA5551: | 363 | case Regs::TextureFormat::RGBA5551: |
| 357 | { | 364 | { |
| 358 | const u16 source_ptr = *(const u16*)(source + offset * 2); | 365 | const u16 source_ptr = *(const u16*)(source + offset * 2 + i * 2); |
| 359 | u8 r = (source_ptr >> 11) & 0x1F; | 366 | u8 r = (source_ptr >> 11) & 0x1F; |
| 360 | u8 g = ((source_ptr) >> 6) & 0x1F; | 367 | u8 g = ((source_ptr) >> 6) & 0x1F; |
| 361 | u8 b = (source_ptr >> 1) & 0x1F; | 368 | u8 b = (source_ptr >> 1) & 0x1F; |
| @@ -366,7 +373,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 366 | 373 | ||
| 367 | case Regs::TextureFormat::RGB565: | 374 | case Regs::TextureFormat::RGB565: |
| 368 | { | 375 | { |
| 369 | const u16 source_ptr = *(const u16*)(source + offset * 2); | 376 | const u16 source_ptr = *(const u16*)(source + offset * 2 + i * 2); |
| 370 | u8 r = Color::Convert5To8((source_ptr >> 11) & 0x1F); | 377 | u8 r = Color::Convert5To8((source_ptr >> 11) & 0x1F); |
| 371 | u8 g = Color::Convert6To8(((source_ptr) >> 5) & 0x3F); | 378 | u8 g = Color::Convert6To8(((source_ptr) >> 5) & 0x3F); |
| 372 | u8 b = Color::Convert5To8((source_ptr) & 0x1F); | 379 | u8 b = Color::Convert5To8((source_ptr) & 0x1F); |
| @@ -375,7 +382,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 375 | 382 | ||
| 376 | case Regs::TextureFormat::RGBA4: | 383 | case Regs::TextureFormat::RGBA4: |
| 377 | { | 384 | { |
| 378 | const u8* source_ptr = source + offset * 2; | 385 | const u8* source_ptr = source + offset * 2 + i * 2; |
| 379 | u8 r = Color::Convert4To8(source_ptr[1] >> 4); | 386 | u8 r = Color::Convert4To8(source_ptr[1] >> 4); |
| 380 | u8 g = Color::Convert4To8(source_ptr[1] & 0xF); | 387 | u8 g = Color::Convert4To8(source_ptr[1] & 0xF); |
| 381 | u8 b = Color::Convert4To8(source_ptr[0] >> 4); | 388 | u8 b = Color::Convert4To8(source_ptr[0] >> 4); |
| @@ -385,7 +392,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 385 | 392 | ||
| 386 | case Regs::TextureFormat::IA8: | 393 | case Regs::TextureFormat::IA8: |
| 387 | { | 394 | { |
| 388 | const u8* source_ptr = source + offset * 2; | 395 | const u8* source_ptr = source + offset * 2 + i * 2; |
| 389 | 396 | ||
| 390 | if (disable_alpha) { | 397 | if (disable_alpha) { |
| 391 | // Show intensity as red, alpha as green | 398 | // Show intensity as red, alpha as green |
| @@ -397,13 +404,13 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 397 | 404 | ||
| 398 | case Regs::TextureFormat::I8: | 405 | case Regs::TextureFormat::I8: |
| 399 | { | 406 | { |
| 400 | const u8* source_ptr = source + offset; | 407 | const u8* source_ptr = source + offset + i; |
| 401 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; | 408 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; |
| 402 | } | 409 | } |
| 403 | 410 | ||
| 404 | case Regs::TextureFormat::A8: | 411 | case Regs::TextureFormat::A8: |
| 405 | { | 412 | { |
| 406 | const u8* source_ptr = source + offset; | 413 | const u8* source_ptr = source + offset + i; |
| 407 | 414 | ||
| 408 | if (disable_alpha) { | 415 | if (disable_alpha) { |
| 409 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; | 416 | return { *source_ptr, *source_ptr, *source_ptr, 255 }; |
| @@ -414,7 +421,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 414 | 421 | ||
| 415 | case Regs::TextureFormat::IA4: | 422 | case Regs::TextureFormat::IA4: |
| 416 | { | 423 | { |
| 417 | const u8* source_ptr = source + offset; | 424 | const u8* source_ptr = source + offset + i; |
| 418 | 425 | ||
| 419 | u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4); | 426 | u8 i = Color::Convert4To8(((*source_ptr) & 0xF0) >> 4); |
| 420 | u8 a = Color::Convert4To8((*source_ptr) & 0xF); | 427 | u8 a = Color::Convert4To8((*source_ptr) & 0xF); |
| @@ -429,7 +436,7 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 429 | 436 | ||
| 430 | case Regs::TextureFormat::A4: | 437 | case Regs::TextureFormat::A4: |
| 431 | { | 438 | { |
| 432 | const u8* source_ptr = source + offset / 2; | 439 | const u8* source_ptr = source + offset / 2 + i / 2; |
| 433 | 440 | ||
| 434 | u8 a = (coarse_x % 2) ? ((*source_ptr)&0xF) : (((*source_ptr) & 0xF0) >> 4); | 441 | u8 a = (coarse_x % 2) ? ((*source_ptr)&0xF) : (((*source_ptr) & 0xF0) >> 4); |
| 435 | a = Color::Convert4To8(a); | 442 | a = Color::Convert4To8(a); |
| @@ -441,6 +448,127 @@ const Math::Vec4<u8> LookupTexture(const u8* source, int x, int y, const Texture | |||
| 441 | } | 448 | } |
| 442 | } | 449 | } |
| 443 | 450 | ||
| 451 | case Regs::TextureFormat::ETC1: | ||
| 452 | case Regs::TextureFormat::ETC1A4: | ||
| 453 | { | ||
| 454 | bool has_alpha = (info.format == Regs::TextureFormat::ETC1A4); | ||
| 455 | |||
| 456 | // ETC1 further subdivides each 8x8 tile into four 4x4 subtiles | ||
| 457 | const int subtile_width = 4; | ||
| 458 | const int subtile_height = 4; | ||
| 459 | |||
| 460 | int subtile_index = ((x / subtile_width) & 1) + 2 * ((y / subtile_height) & 1); | ||
| 461 | unsigned subtile_bytes = has_alpha ? 2 : 1; // TODO: Name... | ||
| 462 | |||
| 463 | const u64* source_ptr = (const u64*)(source | ||
| 464 | + coarse_x * subtile_bytes * 4 | ||
| 465 | + coarse_y * subtile_bytes * 4 * (info.width / 8) | ||
| 466 | + subtile_index * subtile_bytes * 8); | ||
| 467 | u64 alpha = 0xFFFFFFFFFFFFFFFF; | ||
| 468 | if (has_alpha) { | ||
| 469 | alpha = *source_ptr; | ||
| 470 | source_ptr++; | ||
| 471 | } | ||
| 472 | |||
| 473 | union ETC1Tile { | ||
| 474 | // Each of these two is a collection of 16 bits (one per lookup value) | ||
| 475 | BitField< 0, 16, u64> table_subindexes; | ||
| 476 | BitField<16, 16, u64> negation_flags; | ||
| 477 | |||
| 478 | unsigned GetTableSubIndex(unsigned index) const { | ||
| 479 | return (table_subindexes >> index) & 1; | ||
| 480 | } | ||
| 481 | |||
| 482 | bool GetNegationFlag(unsigned index) const { | ||
| 483 | return ((negation_flags >> index) & 1) == 1; | ||
| 484 | } | ||
| 485 | |||
| 486 | BitField<32, 1, u64> flip; | ||
| 487 | BitField<33, 1, u64> differential_mode; | ||
| 488 | |||
| 489 | BitField<34, 3, u64> table_index_2; | ||
| 490 | BitField<37, 3, u64> table_index_1; | ||
| 491 | |||
| 492 | union { | ||
| 493 | // delta value + base value | ||
| 494 | BitField<40, 3, s64> db; | ||
| 495 | BitField<43, 5, u64> b; | ||
| 496 | |||
| 497 | BitField<48, 3, s64> dg; | ||
| 498 | BitField<51, 5, u64> g; | ||
| 499 | |||
| 500 | BitField<56, 3, s64> dr; | ||
| 501 | BitField<59, 5, u64> r; | ||
| 502 | } differential; | ||
| 503 | |||
| 504 | union { | ||
| 505 | BitField<40, 4, u64> b2; | ||
| 506 | BitField<44, 4, u64> b1; | ||
| 507 | |||
| 508 | BitField<48, 4, u64> g2; | ||
| 509 | BitField<52, 4, u64> g1; | ||
| 510 | |||
| 511 | BitField<56, 4, u64> r2; | ||
| 512 | BitField<60, 4, u64> r1; | ||
| 513 | } separate; | ||
| 514 | |||
| 515 | const Math::Vec3<u8> GetRGB(int x, int y) const { | ||
| 516 | int texel = 4 * x + y; | ||
| 517 | |||
| 518 | if (flip) | ||
| 519 | std::swap(x, y); | ||
| 520 | |||
| 521 | // Lookup base value | ||
| 522 | Math::Vec3<int> ret; | ||
| 523 | if (differential_mode) { | ||
| 524 | ret.r() = differential.r; | ||
| 525 | ret.g() = differential.g; | ||
| 526 | ret.b() = differential.b; | ||
| 527 | if (x >= 2) { | ||
| 528 | ret.r() += differential.dr; | ||
| 529 | ret.g() += differential.dg; | ||
| 530 | ret.b() += differential.db; | ||
| 531 | } | ||
| 532 | ret.r() = Color::Convert5To8(ret.r()); | ||
| 533 | ret.g() = Color::Convert5To8(ret.g()); | ||
| 534 | ret.b() = Color::Convert5To8(ret.b()); | ||
| 535 | } else { | ||
| 536 | if (x < 2) { | ||
| 537 | ret.r() = Color::Convert4To8(separate.r1); | ||
| 538 | ret.g() = Color::Convert4To8(separate.g1); | ||
| 539 | ret.b() = Color::Convert4To8(separate.b1); | ||
| 540 | } else { | ||
| 541 | ret.r() = Color::Convert4To8(separate.r2); | ||
| 542 | ret.g() = Color::Convert4To8(separate.g2); | ||
| 543 | ret.b() = Color::Convert4To8(separate.b2); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | // Add modifier | ||
| 548 | unsigned table_index = (x < 2) ? table_index_2.Value() : table_index_1.Value(); | ||
| 549 | |||
| 550 | static const auto etc1_modifier_table = std::array<std::array<u8, 2>, 8>{{ | ||
| 551 | { 2, 8 }, { 5, 17 }, { 9, 29 }, { 13, 42 }, | ||
| 552 | { 18, 60 }, { 24, 80 }, { 33, 106 }, { 47, 183 } | ||
| 553 | }}; | ||
| 554 | |||
| 555 | int modifier = etc1_modifier_table.at(table_index).at(GetTableSubIndex(texel)); | ||
| 556 | if (GetNegationFlag(texel)) | ||
| 557 | modifier *= -1; | ||
| 558 | |||
| 559 | ret.r() = MathUtil::Clamp(ret.r() + modifier, 0, 255); | ||
| 560 | ret.g() = MathUtil::Clamp(ret.g() + modifier, 0, 255); | ||
| 561 | ret.b() = MathUtil::Clamp(ret.b() + modifier, 0, 255); | ||
| 562 | |||
| 563 | return ret.Cast<u8>(); | ||
| 564 | } | ||
| 565 | } const *etc1_tile = reinterpret_cast<const ETC1Tile*>(source_ptr); | ||
| 566 | |||
| 567 | alpha >>= 4 * ((x & 3) * 4 + (y & 3)); | ||
| 568 | return Math::MakeVec(etc1_tile->GetRGB(x & 3, y & 3), | ||
| 569 | disable_alpha ? (u8)255 : Color::Convert4To8(alpha & 0xF)); | ||
| 570 | } | ||
| 571 | |||
| 444 | default: | 572 | default: |
| 445 | LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format); | 573 | LOG_ERROR(HW_GPU, "Unknown texture format: %x", (u32)info.format); |
| 446 | _dbg_assert_(HW_GPU, 0); | 574 | _dbg_assert_(HW_GPU, 0); |
diff --git a/src/video_core/pica.h b/src/video_core/pica.h index f5771ed84..de1ce05b6 100644 --- a/src/video_core/pica.h +++ b/src/video_core/pica.h | |||
| @@ -161,8 +161,8 @@ struct Regs { | |||
| 161 | IA4 = 9, | 161 | IA4 = 9, |
| 162 | 162 | ||
| 163 | A4 = 11, | 163 | A4 = 11, |
| 164 | // TODO: Support for the other formats is not implemented, yet. | 164 | ETC1 = 12, // compressed |
| 165 | // Seems like they are luminance formats and compressed textures. | 165 | ETC1A4 = 13, // compressed |
| 166 | }; | 166 | }; |
| 167 | 167 | ||
| 168 | static unsigned NibblesPerPixel(TextureFormat format) { | 168 | static unsigned NibblesPerPixel(TextureFormat format) { |