diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index ca1b150a7..d1edfe09a 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp @@ -196,7 +196,7 @@ void Maxwell3D::DrawArrays() { auto format = tic_entry.format.Value(); - auto texture = Texture::DecodeTexture( + auto texture = Texture::UnswizzleTexture( memory_manager.PhysicalToVirtualAddress(tic_entry.Address()), tic_entry.format.Value(), tic_entry.Width(), tic_entry.Height()); diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 705e2e066..300267209 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -2,13 +2,93 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include +#include "common/assert.h" #include "video_core/textures/decoders.h" +#include "video_core/textures/texture.h" namespace Tegra { namespace Texture { -std::vector DecodeTexture(VAddr address, TextureFormat format, u32 width, u32 height) { - return {}; +/** + * Calculates the offset of an (x, y) position within a swizzled texture. + * Taken from the Tegra X1 TRM. + */ +static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) { + u32 image_width_in_gobs = image_width * bytes_per_pixel / 64; + u32 GOB_address = 0 + (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs + + (x * bytes_per_pixel / 64) * 512 * block_height + + (y % (8 * block_height) / 8) * 512; + x *= bytes_per_pixel; + u32 address = GOB_address + ((x % 64) / 32) * 256 + ((y % 8) / 2) * 64 + ((x % 32) / 16) * 32 + + (y % 2) * 16 + (x % 16); + + return address; } + +static void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_per_pixel, + u8* swizzled_data, u8* unswizzled_data, bool unswizzle, + u32 block_height) { + u8* data_ptrs[2]; + for (unsigned y = 0; y < height; ++y) { + for (unsigned x = 0; x < width; ++x) { + u32 swizzle_offset = GetSwizzleOffset(x, y, width, bytes_per_pixel, block_height); + u32 pixel_index = (x + y * width) * out_bytes_per_pixel; + + data_ptrs[unswizzle] = swizzled_data + swizzle_offset; + data_ptrs[!unswizzle] = &unswizzled_data[pixel_index]; + + std::memcpy(data_ptrs[0], data_ptrs[1], bytes_per_pixel); + } + } } + +u32 BytesPerPixel(TextureFormat format) { + switch (format) { + case TextureFormat::DXT1: + // In this case a 'pixel' actually refers to a 4x4 tile. + return 8; + default: + UNIMPLEMENTED_MSG("Format not implemented"); + break; + } +} + +std::vector UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height) { + u8* data = Memory::GetPointer(address); + u32 bytes_per_pixel = BytesPerPixel(format); + + static constexpr u32 DefaultBlockHeight = 16; + + std::vector unswizzled_data(width * height * bytes_per_pixel); + + switch (format) { + case TextureFormat::DXT1: + // In the DXT1 format, each 4x4 tile is swizzled instead of just individual pixel values. + CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data, + unswizzled_data.data(), true, DefaultBlockHeight); + break; + default: + UNIMPLEMENTED_MSG("Format not implemented"); + break; + } + + return unswizzled_data; +} + +std::vector DecodeTexture(const std::vector& texture_data, TextureFormat format, u32 width, + u32 height) { + std::vector rgba_data; + + // TODO(Subv): Implement. + switch (format) { + default: + UNIMPLEMENTED_MSG("Format not implemented"); + break; + } + + return rgba_data; +} + +} // namespace Texture } // namespace Tegra diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index e0d55600e..0c21694ff 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h @@ -12,9 +12,15 @@ namespace Tegra { namespace Texture { /** - * Decodes a swizzled texture into a RGBA8888 texture. + * Unswizzles a swizzled texture without changing its format. */ -std::vector DecodeTexture(VAddr address, TextureFormat format, u32 width, u32 height); +std::vector UnswizzleTexture(VAddr address, TextureFormat format, u32 width, u32 height); + +/** + * Decodes an unswizzled texture into a A8R8G8B8 texture. + */ +std::vector DecodeTexture(const std::vector& texture_data, TextureFormat format, u32 width, + u32 height); } // namespace Texture } // namespace Tegra diff --git a/src/video_core/textures/texture.h b/src/video_core/textures/texture.h index 3306d2ab2..d969bcdd9 100644 --- a/src/video_core/textures/texture.h +++ b/src/video_core/textures/texture.h @@ -13,6 +13,7 @@ namespace Tegra { namespace Texture { enum class TextureFormat : u32 { + A8R8G8B8 = 8, DXT1 = 0x24, }; @@ -53,5 +54,8 @@ struct TICEntry { }; static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size"); +/// Returns the number of bytes per pixel of the input texture format. +u32 BytesPerPixel(TextureFormat format); + } // namespace Texture } // namespace Tegra