From 71ebc3e90da9882139d2a5695ca1ed59ea77e94f Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 19 Mar 2018 18:00:29 -0500 Subject: GPU: Preliminary work for texture decoding. --- src/video_core/textures/decoders.cpp | 14 +++++++++ src/video_core/textures/decoders.h | 20 +++++++++++++ src/video_core/textures/texture.h | 57 ++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 src/video_core/textures/decoders.cpp create mode 100644 src/video_core/textures/decoders.h create mode 100644 src/video_core/textures/texture.h (limited to 'src/video_core/textures') diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp new file mode 100644 index 000000000..705e2e066 --- /dev/null +++ b/src/video_core/textures/decoders.cpp @@ -0,0 +1,14 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "video_core/textures/decoders.h" + +namespace Tegra { +namespace Texture { + +std::vector DecodeTexture(VAddr address, TextureFormat format, u32 width, u32 height) { + return {}; +} +} +} // namespace Tegra diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h new file mode 100644 index 000000000..e0d55600e --- /dev/null +++ b/src/video_core/textures/decoders.h @@ -0,0 +1,20 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/common_types.h" +#include "video_core/textures/texture.h" + +namespace Tegra { +namespace Texture { + +/** + * Decodes a swizzled texture into a RGBA8888 texture. + */ +std::vector DecodeTexture(VAddr address, 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 new file mode 100644 index 000000000..3306d2ab2 --- /dev/null +++ b/src/video_core/textures/texture.h @@ -0,0 +1,57 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "video_core/memory_manager.h" + +namespace Tegra { +namespace Texture { + +enum class TextureFormat : u32 { + DXT1 = 0x24, +}; + +union TextureHandle { + u32 raw; + BitField<0, 20, u32> tic_id; + BitField<20, 12, u32> tsc_id; +}; + +struct TICEntry { + union { + u32 raw; + BitField<0, 7, TextureFormat> format; + BitField<7, 3, u32> r_type; + BitField<10, 3, u32> g_type; + BitField<13, 3, u32> b_type; + BitField<16, 3, u32> a_type; + }; + u32 address_low; + u16 address_high; + INSERT_PADDING_BYTES(6); + u16 width_minus_1; + INSERT_PADDING_BYTES(2); + u16 height_minus_1; + INSERT_PADDING_BYTES(10); + + GPUVAddr Address() const { + return static_cast((static_cast(address_high) << 32) | address_low); + } + + u32 Width() const { + return width_minus_1 + 1; + } + + u32 Height() const { + return height_minus_1 + 1; + } +}; +static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size"); + +} // namespace Texture +} // namespace Tegra -- cgit v1.2.3 From 1b8d798835c2d39c2867f53d8dcacdc7d0ba0d15 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 22 Mar 2018 15:17:10 -0500 Subject: GPU: Added a method to unswizzle a texture without decoding it. Allow unswizzling of DXT1 textures. --- src/video_core/engines/maxwell_3d.cpp | 2 +- src/video_core/textures/decoders.cpp | 84 ++++++++++++++++++++++++++++++++++- src/video_core/textures/decoders.h | 10 ++++- src/video_core/textures/texture.h | 4 ++ 4 files changed, 95 insertions(+), 5 deletions(-) (limited to 'src/video_core/textures') 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 -- cgit v1.2.3 From 39e60cfeb10ef317521ff1685df3d265d2c9d5ef Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 22 Mar 2018 16:40:11 -0500 Subject: Frontend: Updated the surface view debug widget to work with Maxwell surfaces. --- src/video_core/gpu.h | 4 +++ src/video_core/textures/decoders.cpp | 11 +++++++ src/yuzu/debugger/graphics/graphics_surface.cpp | 42 ++++++++++++++----------- 3 files changed, 38 insertions(+), 19 deletions(-) (limited to 'src/video_core/textures') diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index 778b63218..8183b12e9 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -13,6 +13,10 @@ namespace Tegra { +enum class RenderTargetFormat { + RGBA8_UNORM = 0xD5, +}; + class DebugContext; /** diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index 300267209..2e87281eb 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp @@ -48,6 +48,8 @@ u32 BytesPerPixel(TextureFormat format) { case TextureFormat::DXT1: // In this case a 'pixel' actually refers to a 4x4 tile. return 8; + case TextureFormat::A8R8G8B8: + return 4; default: UNIMPLEMENTED_MSG("Format not implemented"); break; @@ -68,6 +70,10 @@ std::vector UnswizzleTexture(VAddr address, TextureFormat format, u32 width, CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data, unswizzled_data.data(), true, DefaultBlockHeight); break; + case TextureFormat::A8R8G8B8: + CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data, + unswizzled_data.data(), true, DefaultBlockHeight); + break; default: UNIMPLEMENTED_MSG("Format not implemented"); break; @@ -82,6 +88,11 @@ std::vector DecodeTexture(const std::vector& texture_data, TextureFormat // TODO(Subv): Implement. switch (format) { + case TextureFormat::DXT1: + case TextureFormat::A8R8G8B8: + // TODO(Subv): For the time being just forward the same data without any decoding. + rgba_data = texture_data; + break; default: UNIMPLEMENTED_MSG("Format not implemented"); break; diff --git a/src/yuzu/debugger/graphics/graphics_surface.cpp b/src/yuzu/debugger/graphics/graphics_surface.cpp index 54b816054..d061013da 100644 --- a/src/yuzu/debugger/graphics/graphics_surface.cpp +++ b/src/yuzu/debugger/graphics/graphics_surface.cpp @@ -13,12 +13,23 @@ #include #include "core/core.h" #include "video_core/engines/maxwell_3d.h" +#include "video_core/gpu.h" #include "video_core/textures/decoders.h" #include "video_core/textures/texture.h" #include "video_core/utils.h" #include "yuzu/debugger/graphics/graphics_surface.h" #include "yuzu/util/spinbox.h" +static Tegra::Texture::TextureFormat ConvertToTextureFormat( + Tegra::RenderTargetFormat render_target_format) { + switch (render_target_format) { + case Tegra::RenderTargetFormat::RGBA8_UNORM: + return Tegra::Texture::TextureFormat::A8R8G8B8; + default: + UNIMPLEMENTED_MSG("Unimplemented RT format"); + } +} + SurfacePicture::SurfacePicture(QWidget* parent, GraphicsSurfaceWidget* surface_widget_) : QLabel(parent), surface_widget(surface_widget_) {} SurfacePicture::~SurfacePicture() {} @@ -62,7 +73,7 @@ GraphicsSurfaceWidget::GraphicsSurfaceWidget(std::shared_ptrSetBase(16); - surface_address_control->SetRange(0, 0xFFFFFFFF); + surface_address_control->SetRange(0, 0x7FFFFFFFFFFFFFFF); surface_address_control->SetPrefix("0x"); unsigned max_dimension = 16384; // TODO: Find actual maximum @@ -243,7 +254,7 @@ void GraphicsSurfaceWidget::OnSurfaceSourceChanged(int new_value) { void GraphicsSurfaceWidget::OnSurfaceAddressChanged(qint64 new_value) { if (surface_address != new_value) { - surface_address = static_cast(new_value); + surface_address = static_cast(new_value); surface_source_list->setCurrentIndex(static_cast(Source::Custom)); emit Update(); @@ -302,13 +313,6 @@ void GraphicsSurfaceWidget::Pick(int x, int y) { return; } - u8* buffer = Memory::GetPhysicalPointer(surface_address); - if (buffer == nullptr) { - surface_info_label->setText(tr("(unable to access pixel data)")); - surface_info_label->setAlignment(Qt::AlignCenter); - return; - } - surface_info_label->setText(QString("Raw: \n(%1)").arg("")); surface_info_label->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); } @@ -318,8 +322,6 @@ void GraphicsSurfaceWidget::OnUpdate() { QPixmap pixmap; - Tegra::GPUVAddr surface_address = 0; - switch (surface_source) { case Source::RenderTarget0: case Source::RenderTarget1: @@ -333,11 +335,13 @@ void GraphicsSurfaceWidget::OnUpdate() { // directly... auto& registers = gpu.Get3DEngine().regs; + auto& rt = registers.rt[static_cast(surface_source) - + static_cast(Source::RenderTarget0)]; - surface_address = 0; - surface_width = 0; - surface_height = 0; - surface_format = Tegra::Texture::TextureFormat::DXT1; + surface_address = rt.Address(); + surface_width = rt.horiz; + surface_height = rt.vert; + surface_format = ConvertToTextureFormat(static_cast(rt.format)); break; } @@ -378,9 +382,6 @@ void GraphicsSurfaceWidget::OnUpdate() { auto texture_data = Tegra::Texture::DecodeTexture(unswizzled_data, surface_format, surface_width, surface_height); - ASSERT(texture_data.size() == - surface_width * surface_height * - Tegra::Texture::BytesPerPixel(Tegra::Texture::TextureFormat::A8R8G8B8)); surface_picture_label->show(); for (unsigned int y = 0; y < surface_height; ++y) { @@ -431,7 +432,10 @@ void GraphicsSurfaceWidget::SaveSurface() { if (pixmap) pixmap->save(&file, "PNG"); } else if (selectedFilter == bin_filter) { - const u8* buffer = Memory::GetPhysicalPointer(surface_address); + auto& gpu = Core::System::GetInstance().GPU(); + VAddr address = gpu.memory_manager->PhysicalToVirtualAddress(surface_address); + + const u8* buffer = Memory::GetPointer(address); ASSERT_MSG(buffer != nullptr, "Memory not accessible"); QFile file(filename); -- cgit v1.2.3