Compare commits

...

32 Commits

Author SHA1 Message Date
Subv
38989bef43 GPU: Perform negation after absolute value in the float shader instructions. 2018-06-18 19:56:29 -05:00
bunnei
0e13d9cb7b Merge pull request #570 from bunnei/astc
gl_rasterizer: Implement texture format ASTC_2D_4X4.
2018-06-18 19:08:49 -04:00
bunnei
c11cfaa705 Merge pull request #562 from DarkLordZach/extracted-ncas-ui
Add UI support for extracted NCA folders
2018-06-18 16:09:46 -04:00
bunnei
4ac4b308e4 Merge pull request #572 from Armada651/user-except-stub
svc: Add a stub for UserExceptionContextAddr.
2018-06-18 11:37:13 -04:00
bunnei
ea080501fb Merge pull request #571 from Armada651/loose-blend
gl_rasterizer: Get loose on independent blending.
2018-06-18 11:36:50 -04:00
Jules Blok
bf4e2b2f0b svc: Add a stub for UserExceptionContextAddr. 2018-06-18 09:29:11 +02:00
Jules Blok
7c7f4a9be2 gl_rasterizer: Get loose on independent blending. 2018-06-18 09:27:06 +02:00
bunnei
61779fa072 gl_rasterizer: Implement texture format ASTC_2D_4X4. 2018-06-18 01:56:59 -04:00
bunnei
d2277b825e Merge pull request #569 from bunnei/fix-cache
gl_rasterizer_cache: Loosen things up a bit.
2018-06-18 01:32:12 -04:00
bunnei
fe906fff36 gl_rasterizer_cache: Loosen things up a bit. 2018-06-18 00:55:59 -04:00
bunnei
f9af74201c Merge pull request #568 from bunnei/lop
gl_shader_decompiler: Implement LOP instructions.
2018-06-17 17:44:38 -04:00
bunnei
afdd657d30 gl_shader_decompiler: Implement LOP instructions. 2018-06-17 15:27:48 -04:00
bunnei
5673ce39c7 gl_shader_decompiler: Refactor LOP32I instruction a bit in support of LOP. 2018-06-17 13:31:39 -04:00
bunnei
3c43ea5c68 Merge pull request #565 from bunnei/shader_conversions
gl_shader_decompiler: Implement register size conversions for I2I and I2F.
2018-06-16 08:50:29 -04:00
bunnei
d383043e07 gl_shader_decompiler: Implement integer size conversions for I2I/I2F/F2I. 2018-06-15 22:42:02 -04:00
bunnei
fb5bd0920d Merge pull request #564 from bunnei/lop32i_passb
gl_shader_decompiler: Implement LOP32I LogicOperation PassB.
2018-06-15 22:04:03 -04:00
bunnei
46cbb6b090 Merge pull request #566 from bunnei/set_pos_w
gl_shader_gen: Set position.w to 1.
2018-06-15 22:03:48 -04:00
bunnei
55c49d5bf4 gl_shader_gen: Set position.w to 1. 2018-06-15 20:47:04 -04:00
bunnei
61f9d9c4ab gl_shader_decompiler: Implement LOP32I LogicOperation PassB. 2018-06-15 20:43:33 -04:00
Zach Hilman
acc8fe5a2a Bug fixes, testing, and review changes 2018-06-14 17:25:40 -04:00
Zach Hilman
f969ddb54e Add 'Load Folder' menu option 2018-06-14 12:27:29 -04:00
Zach Hilman
9f8fbce35b Add support for main files in file picker 2018-06-14 12:16:56 -04:00
Zach Hilman
94d27b1717 Recognize main files in game list 2018-06-14 12:02:32 -04:00
bunnei
17f3590d59 Merge pull request #560 from Subv/crash_widget
Qt: Removed the Registers widget.
2018-06-13 10:15:00 -04:00
Subv
7786f41cc0 Qt: Removed the Registers widget.
It was crashing and nobody actually uses this.
2018-06-12 20:33:32 -05:00
bunnei
019d7208c8 Merge pull request #556 from Subv/dma_engine
GPU: Partially implemented the Maxwell DMA engine.
2018-06-12 14:25:17 -04:00
bunnei
2015a1b180 Merge pull request #558 from Subv/iadd32i
GPU: Implemented the iadd32i shader instruction.
2018-06-12 14:19:25 -04:00
Subv
db0497b808 GPU: Implemented the iadd32i shader instruction. 2018-06-12 11:46:45 -05:00
Subv
987a170665 GPU: Partially implemented the Maxwell DMA engine.
Only tiled->linear and linear->tiled copies that aren't offsetted are supported for now. Queries are not supported. Swizzled copies are not supported.
2018-06-12 11:27:36 -05:00
bunnei
33dbf24b56 Merge pull request #557 from shinyquagsire23/libnx-hid-fix
hid: Update all layouts and only show handheld as connected, fixes libnx input for P1_AUTO
2018-06-12 09:07:38 -04:00
bunnei
2dc8b5c224 Merge pull request #552 from bunnei/sat-fmul
gl_shader_decompiler: Implement saturate for float instructions.
2018-06-11 23:19:37 -04:00
bunnei
5f3d6c85db gl_shader_decompiler: Implement saturate for float instructions. 2018-06-11 21:46:34 -04:00
25 changed files with 2210 additions and 435 deletions

View File

@@ -316,6 +316,11 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
*result = 0;
break;
case GetInfoType::UserExceptionContextAddr:
NGLOG_WARNING(Kernel_SVC,
"(STUBBED) Attempted to query user exception context address, returned 0");
*result = 0;
break;
default:
UNIMPLEMENTED();
}

View File

@@ -9,6 +9,8 @@ add_library(video_core STATIC
engines/maxwell_3d.h
engines/maxwell_compute.cpp
engines/maxwell_compute.h
engines/maxwell_dma.cpp
engines/maxwell_dma.h
engines/shader_bytecode.h
gpu.cpp
gpu.h
@@ -39,6 +41,8 @@ add_library(video_core STATIC
renderer_opengl/maxwell_to_gl.h
renderer_opengl/renderer_opengl.cpp
renderer_opengl/renderer_opengl.h
textures/astc.cpp
textures/astc.h
textures/decoders.cpp
textures/decoders.h
textures/texture.h

View File

@@ -16,6 +16,7 @@
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_compute.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/gpu.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -60,8 +61,11 @@ void GPU::WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params)
case EngineID::MAXWELL_COMPUTE_B:
maxwell_compute->WriteReg(method, value);
break;
case EngineID::MAXWELL_DMA_COPY_A:
maxwell_dma->WriteReg(method, value);
break;
default:
UNIMPLEMENTED();
UNIMPLEMENTED_MSG("Unimplemented engine");
}
}

View File

@@ -47,6 +47,7 @@ void Fermi2D::HandleSurfaceCopy() {
if (regs.src.linear == regs.dst.linear) {
// If the input layout and the output layout are the same, just perform a raw copy.
ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
Memory::CopyBlock(dest_cpu, source_cpu,
src_bytes_per_pixel * regs.dst.width * regs.dst.height);
return;

View File

@@ -0,0 +1,69 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/memory.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/textures/decoders.h"
namespace Tegra {
namespace Engines {
MaxwellDMA::MaxwellDMA(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
void MaxwellDMA::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid MaxwellDMA register, increase the size of the Regs structure");
regs.reg_array[method] = value;
#define MAXWELLDMA_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::MaxwellDMA::Regs, field_name) / sizeof(u32))
switch (method) {
case MAXWELLDMA_REG_INDEX(exec): {
HandleCopy();
break;
}
}
#undef MAXWELLDMA_REG_INDEX
}
void MaxwellDMA::HandleCopy() {
NGLOG_WARNING(HW_GPU, "Requested a DMA copy");
const GPUVAddr source = regs.src_address.Address();
const GPUVAddr dest = regs.dst_address.Address();
const VAddr source_cpu = *memory_manager.GpuToCpuAddress(source);
const VAddr dest_cpu = *memory_manager.GpuToCpuAddress(dest);
// TODO(Subv): Perform more research and implement all features of this engine.
ASSERT(regs.exec.enable_swizzle == 0);
ASSERT(regs.exec.enable_2d == 1);
ASSERT(regs.exec.query_mode == Regs::QueryMode::None);
ASSERT(regs.exec.query_intr == Regs::QueryIntr::None);
ASSERT(regs.exec.copy_mode == Regs::CopyMode::Unk2);
ASSERT(regs.src_params.pos_x == 0);
ASSERT(regs.src_params.pos_y == 0);
ASSERT(regs.dst_params.pos_x == 0);
ASSERT(regs.dst_params.pos_y == 0);
ASSERT(regs.exec.is_dst_linear != regs.exec.is_src_linear);
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);
if (regs.exec.is_dst_linear && !regs.exec.is_src_linear) {
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
Texture::CopySwizzledData(regs.src_params.size_x, regs.src_params.size_y, 1, 1, src_buffer,
dst_buffer, true, regs.src_params.BlockHeight());
} else {
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::CopySwizzledData(regs.dst_params.size_x, regs.dst_params.size_y, 1, 1, dst_buffer,
src_buffer, false, regs.dst_params.BlockHeight());
}
}
} // namespace Engines
} // namespace Tegra

View File

@@ -0,0 +1,155 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include "common/assert.h"
#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace Tegra {
namespace Engines {
class MaxwellDMA final {
public:
explicit MaxwellDMA(MemoryManager& memory_manager);
~MaxwellDMA() = default;
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value);
struct Regs {
static constexpr size_t NUM_REGS = 0x1D6;
struct Parameters {
union {
BitField<0, 4, u32> block_depth;
BitField<4, 4, u32> block_height;
BitField<8, 4, u32> block_width;
};
u32 size_x;
u32 size_y;
u32 size_z;
u32 pos_z;
union {
BitField<0, 16, u32> pos_x;
BitField<16, 16, u32> pos_y;
};
u32 BlockHeight() const {
return 1 << block_height;
}
};
static_assert(sizeof(Parameters) == 24, "Parameters has wrong size");
enum class CopyMode : u32 {
None = 0,
Unk1 = 1,
Unk2 = 2,
};
enum class QueryMode : u32 {
None = 0,
Short = 1,
Long = 2,
};
enum class QueryIntr : u32 {
None = 0,
Block = 1,
NonBlock = 2,
};
union {
struct {
INSERT_PADDING_WORDS(0xC0);
struct {
union {
BitField<0, 2, CopyMode> copy_mode;
BitField<2, 1, u32> flush;
BitField<3, 2, QueryMode> query_mode;
BitField<5, 2, QueryIntr> query_intr;
BitField<7, 1, u32> is_src_linear;
BitField<8, 1, u32> is_dst_linear;
BitField<9, 1, u32> enable_2d;
BitField<10, 1, u32> enable_swizzle;
};
} exec;
INSERT_PADDING_WORDS(0x3F);
struct {
u32 address_high;
u32 address_low;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} src_address;
struct {
u32 address_high;
u32 address_low;
GPUVAddr Address() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
} dst_address;
u32 src_pitch;
u32 dst_pitch;
u32 x_count;
u32 y_count;
INSERT_PADDING_WORDS(0xBB);
Parameters dst_params;
INSERT_PADDING_WORDS(1);
Parameters src_params;
INSERT_PADDING_WORDS(0x13);
};
std::array<u32, NUM_REGS> reg_array;
};
} regs{};
MemoryManager& memory_manager;
private:
/// Performs the copy from the source buffer to the destination buffer as configured in the
/// registers.
void HandleCopy();
};
#define ASSERT_REG_POSITION(field_name, position) \
static_assert(offsetof(MaxwellDMA::Regs, field_name) == position * 4, \
"Field " #field_name " has invalid position")
ASSERT_REG_POSITION(exec, 0xC0);
ASSERT_REG_POSITION(src_address, 0x100);
ASSERT_REG_POSITION(dst_address, 0x102);
ASSERT_REG_POSITION(src_pitch, 0x104);
ASSERT_REG_POSITION(dst_pitch, 0x105);
ASSERT_REG_POSITION(x_count, 0x106);
ASSERT_REG_POSITION(y_count, 0x107);
ASSERT_REG_POSITION(dst_params, 0x1C3);
ASSERT_REG_POSITION(src_params, 0x1CA);
#undef ASSERT_REG_POSITION
} // namespace Engines
} // namespace Tegra

View File

@@ -213,16 +213,15 @@ union Instruction {
BitField<28, 8, Register> gpr28;
BitField<39, 8, Register> gpr39;
BitField<48, 16, u64> opcode;
BitField<50, 1, u64> saturate_a;
union {
BitField<20, 19, u64> imm20_19;
BitField<20, 32, u64> imm20_32;
BitField<20, 32, s64> imm20_32;
BitField<45, 1, u64> negate_b;
BitField<46, 1, u64> abs_a;
BitField<48, 1, u64> negate_a;
BitField<49, 1, u64> abs_b;
BitField<50, 1, u64> abs_d;
BitField<50, 1, u64> saturate_d;
BitField<56, 1, u64> negate_imm;
union {
@@ -230,11 +229,19 @@ union Instruction {
BitField<42, 1, u64> negate_pred;
} fmnmx;
union {
BitField<39, 1, u64> invert_a;
BitField<40, 1, u64> invert_b;
BitField<41, 2, LogicOperation> operation;
BitField<44, 2, u64> unk44;
BitField<48, 3, Pred> pred48;
} lop;
union {
BitField<53, 2, LogicOperation> operation;
BitField<55, 1, u64> invert_a;
BitField<56, 1, u64> invert_b;
} lop;
} lop32i;
float GetImm20_19() const {
float result{};
@@ -247,7 +254,7 @@ union Instruction {
float GetImm20_32() const {
float result{};
u32 imm{static_cast<u32>(imm20_32)};
s32 imm{static_cast<s32>(imm20_32)};
std::memcpy(&result, &imm, sizeof(imm));
return result;
}
@@ -270,6 +277,11 @@ union Instruction {
BitField<49, 1, u64> negate_a;
} alu_integer;
union {
BitField<54, 1, u64> saturate;
BitField<56, 1, u64> negate_a;
} iadd32i;
union {
BitField<20, 8, u64> shift_position;
BitField<28, 8, u64> shift_length;
@@ -339,7 +351,8 @@ union Instruction {
} iset;
union {
BitField<10, 2, Register::Size> size;
BitField<8, 2, Register::Size> dest_size;
BitField<10, 2, Register::Size> src_size;
BitField<12, 1, u64> is_output_signed;
BitField<13, 1, u64> is_input_signed;
BitField<41, 2, u64> selector;
@@ -451,6 +464,7 @@ public:
IADD_C,
IADD_R,
IADD_IMM,
IADD32I,
ISCADD_C, // Scale and Add
ISCADD_R,
ISCADD_IMM,
@@ -470,6 +484,9 @@ public:
I2I_C,
I2I_R,
I2I_IMM,
LOP_C,
LOP_R,
LOP_IMM,
LOP32I,
MOV_C,
MOV_R,
@@ -510,8 +527,8 @@ public:
Trivial,
Arithmetic,
ArithmeticInteger,
ArithmeticIntegerImmediate,
Bfe,
Logic,
Shift,
Ffma,
Flow,
@@ -642,6 +659,7 @@ private:
INST("0100110000010---", Id::IADD_C, Type::ArithmeticInteger, "IADD_C"),
INST("0101110000010---", Id::IADD_R, Type::ArithmeticInteger, "IADD_R"),
INST("0011100-00010---", Id::IADD_IMM, Type::ArithmeticInteger, "IADD_IMM"),
INST("0001110---------", Id::IADD32I, Type::ArithmeticIntegerImmediate, "IADD32I"),
INST("0100110000011---", Id::ISCADD_C, Type::ArithmeticInteger, "ISCADD_C"),
INST("0101110000011---", Id::ISCADD_R, Type::ArithmeticInteger, "ISCADD_R"),
INST("0011100-00011---", Id::ISCADD_IMM, Type::ArithmeticInteger, "ISCADD_IMM"),
@@ -668,7 +686,10 @@ private:
INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"),
INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"),
INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"),
INST("000001----------", Id::LOP32I, Type::Logic, "LOP32I"),
INST("0100110001000---", Id::LOP_C, Type::ArithmeticInteger, "LOP_C"),
INST("0101110001000---", Id::LOP_R, Type::ArithmeticInteger, "LOP_R"),
INST("0011100001000---", Id::LOP_IMM, Type::ArithmeticInteger, "LOP_IMM"),
INST("000001----------", Id::LOP32I, Type::ArithmeticIntegerImmediate, "LOP32I"),
INST("0100110001001---", Id::SHL_C, Type::Shift, "SHL_C"),
INST("0101110001001---", Id::SHL_R, Type::Shift, "SHL_R"),
INST("0011100-01001---", Id::SHL_IMM, Type::Shift, "SHL_IMM"),

View File

@@ -5,6 +5,7 @@
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_compute.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/gpu.h"
namespace Tegra {
@@ -14,6 +15,7 @@ GPU::GPU() {
maxwell_3d = std::make_unique<Engines::Maxwell3D>(*memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
}
GPU::~GPU() = default;

View File

@@ -63,6 +63,7 @@ namespace Engines {
class Fermi2D;
class Maxwell3D;
class MaxwellCompute;
class MaxwellDMA;
} // namespace Engines
enum class EngineID {
@@ -103,6 +104,8 @@ private:
std::unique_ptr<Engines::Fermi2D> fermi_2d;
/// Compute engine
std::unique_ptr<Engines::MaxwellCompute> maxwell_compute;
/// DMA engine
std::unique_ptr<Engines::MaxwellDMA> maxwell_dma;
};
} // namespace Tegra

View File

@@ -740,7 +740,6 @@ void RasterizerOpenGL::SyncDepthOffset() {
void RasterizerOpenGL::SyncBlendState() {
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented");
// TODO(Subv): Support more than just render target 0.
state.blend.enabled = regs.blend.enable[0] != 0;
@@ -748,6 +747,7 @@ void RasterizerOpenGL::SyncBlendState() {
if (!state.blend.enabled)
return;
ASSERT_MSG(regs.independent_blend_enable == 1, "Only independent blending is implemented");
ASSERT_MSG(!regs.independent_blend[0].separate_alpha, "Unimplemented");
state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_rgb);
state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_rgb);

View File

@@ -28,6 +28,7 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/textures/astc.h"
#include "video_core/textures/decoders.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
@@ -55,6 +56,7 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form
{GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT23
{GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, true}, // DXT45
{GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, true}, // DXN1
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, false}, // ASTC_2D_4X4
}};
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format, ComponentType component_type) {
@@ -86,6 +88,23 @@ static u16 GetResolutionScaleFactor() {
: Settings::values.resolution_factor);
}
static void ConvertASTCToRGBA8(std::vector<u8>& data, PixelFormat format, u32 width, u32 height) {
u32 block_width{};
u32 block_height{};
switch (format) {
case PixelFormat::ASTC_2D_4X4:
block_width = 4;
block_height = 4;
break;
default:
NGLOG_CRITICAL(HW_GPU, "Unhandled format: {}", static_cast<u32>(format));
UNREACHABLE();
}
data = Tegra::Texture::ASTC::Decompress(data, width, height, block_width, block_height);
}
template <bool morton_to_gl, PixelFormat format>
void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::GPUVAddr base,
Tegra::GPUVAddr start, Tegra::GPUVAddr end) {
@@ -97,6 +116,12 @@ void MortonCopy(u32 stride, u32 block_height, u32 height, u8* gl_buffer, Tegra::
auto data = Tegra::Texture::UnswizzleTexture(
*gpu.memory_manager->GpuToCpuAddress(base),
SurfaceParams::TextureFormatFromPixelFormat(format), stride, height, block_height);
if (SurfaceParams::IsFormatASTC(format)) {
// ASTC formats are converted to RGBA8 in software, as most PC GPUs do not support this
ConvertASTCToRGBA8(data, format, stride, height);
}
std::memcpy(gl_buffer, data.data(), data.size());
} else {
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check
@@ -118,7 +143,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra:
MortonCopy<true, PixelFormat::R8>, MortonCopy<true, PixelFormat::RGBA16F>,
MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::DXT1>,
MortonCopy<true, PixelFormat::DXT23>, MortonCopy<true, PixelFormat::DXT45>,
MortonCopy<true, PixelFormat::DXN1>,
MortonCopy<true, PixelFormat::DXN1>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>,
};
static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra::GPUVAddr,
@@ -137,6 +162,7 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr, Tegra:
nullptr,
nullptr,
nullptr,
MortonCopy<false, PixelFormat::ABGR8>,
};
// Allocate an uninitialized texture of appropriate size and format for the surface
@@ -888,9 +914,6 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
// Use GetSurfaceSubRect instead
ASSERT(params.width == params.stride);
ASSERT(!params.is_tiled ||
(params.GetActualWidth() % 8 == 0 && params.GetActualHeight() % 8 == 0));
// Check for an exact match in existing surfaces
Surface surface =
FindMatch<MatchFlags::Exact | MatchFlags::Invalid>(surface_cache, params, match_res_scale);
@@ -1048,8 +1071,13 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
if (config.tic.IsTiled()) {
params.block_height = config.tic.BlockHeight();
params.width = Common::AlignUp(params.width, params.block_height);
params.height = Common::AlignUp(params.height, params.block_height);
// TODO(bunnei): The below align up is a hack. This is here because some compressed textures
// are not a multiple of their own compression factor, and so this accounts for that. This
// could potentially result in an extra row of 4px being decoded if a texture is not a
// multiple of 4.
params.width = Common::AlignUp(params.width, 4);
params.height = Common::AlignUp(params.height, 4);
} else {
// Use the texture-provided stride value if the texture isn't tiled.
params.stride = static_cast<u32>(params.PixelsInBytes(config.tic.Pitch()));
@@ -1057,26 +1085,6 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
params.UpdateParams();
if (params.GetActualWidth() % 8 != 0 || params.GetActualHeight() % 8 != 0 ||
params.stride != params.width) {
Surface src_surface;
MathUtil::Rectangle<u32> rect;
std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true);
rect = rect.Scale(params.GetCompresssionFactor());
params.res_scale = src_surface->res_scale;
Surface tmp_surface = CreateSurface(params);
auto dst_rect = tmp_surface->GetScaledRect().Scale(params.GetCompresssionFactor());
BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle, dst_rect,
SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
draw_framebuffer.handle);
remove_surfaces.emplace(tmp_surface);
return tmp_surface;
}
return GetSurface(params, ScaleMatch::Ignore, true);
}
@@ -1251,7 +1259,7 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, Tegra::GPUVA
const auto interval = *it & validate_interval;
// Look for a valid surface to copy from
SurfaceParams params = surface->FromInterval(interval);
SurfaceParams params = *surface;
Surface copy_surface =
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);

View File

@@ -65,6 +65,7 @@ struct SurfaceParams {
DXT23 = 8,
DXT45 = 9,
DXN1 = 10, // This is also known as BC4
ASTC_2D_4X4 = 11,
Max,
Invalid = 255,
@@ -111,6 +112,7 @@ struct SurfaceParams {
4, // DXT23
4, // DXT45
4, // DXN1
1, // ASTC_2D_4X4
}};
ASSERT(static_cast<size_t>(format) < compression_factor_table.size());
@@ -136,6 +138,7 @@ struct SurfaceParams {
128, // DXT23
128, // DXT45
64, // DXN1
32, // ASTC_2D_4X4
}};
ASSERT(static_cast<size_t>(format) < bpp_table.size());
@@ -162,6 +165,15 @@ struct SurfaceParams {
}
}
static bool IsFormatASTC(PixelFormat format) {
switch (format) {
case PixelFormat::ASTC_2D_4X4:
return true;
default:
return false;
}
}
static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
switch (format) {
case Tegra::FramebufferConfig::PixelFormat::ABGR8:
@@ -197,6 +209,8 @@ struct SurfaceParams {
return PixelFormat::DXT45;
case Tegra::Texture::TextureFormat::DXN1:
return PixelFormat::DXN1;
case Tegra::Texture::TextureFormat::ASTC_2D_4X4:
return PixelFormat::ASTC_2D_4X4;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
UNREACHABLE();
@@ -228,6 +242,8 @@ struct SurfaceParams {
return Tegra::Texture::TextureFormat::DXT45;
case PixelFormat::DXN1:
return Tegra::Texture::TextureFormat::DXN1;
case PixelFormat::ASTC_2D_4X4:
return Tegra::Texture::TextureFormat::ASTC_2D_4X4;
default:
UNREACHABLE();
}

View File

@@ -16,6 +16,7 @@ namespace Decompiler {
using Tegra::Shader::Attribute;
using Tegra::Shader::Instruction;
using Tegra::Shader::LogicOperation;
using Tegra::Shader::OpCode;
using Tegra::Shader::Register;
using Tegra::Shader::Sampler;
@@ -265,6 +266,27 @@ public:
BuildRegisterList();
}
/**
* Returns code that does an integer size conversion for the specified size.
* @param value Value to perform integer size conversion on.
* @param size Register size to use for conversion instructions.
* @returns GLSL string corresponding to the value converted to the specified size.
*/
static std::string ConvertIntegerSize(const std::string& value, Register::Size size) {
switch (size) {
case Register::Size::Byte:
return "((" + value + " << 24) >> 24)";
case Register::Size::Short:
return "((" + value + " << 16) >> 16)";
case Register::Size::Word:
// Default - do nothing
return value;
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented conversion size {}", static_cast<u32>(size));
UNREACHABLE();
}
}
/**
* Gets a register as an float.
* @param reg The register to get.
@@ -281,15 +303,18 @@ public:
* @param reg The register to get.
* @param elem The element to use for the operation.
* @param is_signed Whether to get the register as a signed (or unsigned) integer.
* @param size Register size to use for conversion instructions.
* @returns GLSL string corresponding to the register as an integer.
*/
std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0,
bool is_signed = true) {
std::string GetRegisterAsInteger(const Register& reg, unsigned elem = 0, bool is_signed = true,
Register::Size size = Register::Size::Word) {
const std::string func = GetGLSLConversionFunc(
GLSLRegister::Type::Float,
is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger);
return func + '(' + GetRegister(reg, elem) + ')';
std::string value = func + '(' + GetRegister(reg, elem) + ')';
return ConvertIntegerSize(value, size);
}
/**
@@ -299,13 +324,15 @@ public:
* @param value The code representing the value to assign.
* @param dest_num_components Number of components in the destination.
* @param value_num_components Number of components in the value.
* @param is_abs Optional, when True, applies absolute value to output.
* @param is_saturated Optional, when True, saturates the provided value.
* @param dest_elem Optional, the destination element to use for the operation.
*/
void SetRegisterToFloat(const Register& reg, u64 elem, const std::string& value,
u64 dest_num_components, u64 value_num_components, bool is_abs = false,
u64 dest_elem = 0) {
SetRegister(reg, elem, value, dest_num_components, value_num_components, is_abs, dest_elem);
u64 dest_num_components, u64 value_num_components,
bool is_saturated = false, u64 dest_elem = 0) {
SetRegister(reg, elem, is_saturated ? "clamp(" + value + ", 0.0, 1.0)" : value,
dest_num_components, value_num_components, dest_elem);
}
/**
@@ -315,18 +342,22 @@ public:
* @param value The code representing the value to assign.
* @param dest_num_components Number of components in the destination.
* @param value_num_components Number of components in the value.
* @param is_abs Optional, when True, applies absolute value to output.
* @param is_saturated Optional, when True, saturates the provided value.
* @param dest_elem Optional, the destination element to use for the operation.
* @param size Register size to use for conversion instructions.
*/
void SetRegisterToInteger(const Register& reg, bool is_signed, u64 elem,
const std::string& value, u64 dest_num_components,
u64 value_num_components, bool is_abs = false, u64 dest_elem = 0) {
u64 value_num_components, bool is_saturated = false,
u64 dest_elem = 0, Register::Size size = Register::Size::Word) {
ASSERT_MSG(!is_saturated, "Unimplemented");
const std::string func = GetGLSLConversionFunc(
is_signed ? GLSLRegister::Type::Integer : GLSLRegister::Type::UnsignedInteger,
GLSLRegister::Type::Float);
SetRegister(reg, elem, func + '(' + value + ')', dest_num_components, value_num_components,
is_abs, dest_elem);
SetRegister(reg, elem, func + '(' + ConvertIntegerSize(value, size) + ')',
dest_num_components, value_num_components, dest_elem);
}
/**
@@ -500,12 +531,10 @@ private:
* @param value The code representing the value to assign.
* @param dest_num_components Number of components in the destination.
* @param value_num_components Number of components in the value.
* @param is_abs Optional, when True, applies absolute value to output.
* @param dest_elem Optional, the destination element to use for the operation.
*/
void SetRegister(const Register& reg, u64 elem, const std::string& value,
u64 dest_num_components, u64 value_num_components, bool is_abs,
u64 dest_elem) {
u64 dest_num_components, u64 value_num_components, u64 dest_elem) {
std::string dest = GetRegister(reg, dest_elem);
if (dest_num_components > 1) {
dest += GetSwizzle(elem);
@@ -516,8 +545,6 @@ private:
src += GetSwizzle(elem);
}
src = is_abs ? "abs(" + src + ')' : src;
shader.AddLine(dest + " = " + src + ';');
}
@@ -733,6 +760,31 @@ private:
return (absolute_offset % SchedPeriod) == 0;
}
void WriteLogicOperation(Register dest, LogicOperation logic_op, const std::string& op_a,
const std::string& op_b) {
switch (logic_op) {
case LogicOperation::And: {
regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " & " + op_b + ')', 1, 1);
break;
}
case LogicOperation::Or: {
regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " | " + op_b + ')', 1, 1);
break;
}
case LogicOperation::Xor: {
regs.SetRegisterToInteger(dest, true, 0, '(' + op_a + " ^ " + op_b + ')', 1, 1);
break;
}
case LogicOperation::PassB: {
regs.SetRegisterToInteger(dest, true, 0, op_b, 1, 1);
break;
}
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented logic operation: {}", static_cast<u32>(logic_op));
UNREACHABLE();
}
}
/**
* Compiles a single instruction from Tegra to GLSL.
* @param offset the offset of the Tegra shader instruction.
@@ -770,22 +822,25 @@ private:
switch (opcode->GetType()) {
case OpCode::Type::Arithmetic: {
std::string op_a = instr.alu.negate_a ? "-" : "";
op_a += regs.GetRegisterAsFloat(instr.gpr8);
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
if (instr.alu.abs_a) {
op_a = "abs(" + op_a + ')';
}
std::string op_b = instr.alu.negate_b ? "-" : "";
if (instr.alu.negate_a) {
op_a = "-(" + op_a + ')';
}
std::string op_b;
if (instr.is_b_imm) {
op_b += GetImmediate19(instr);
op_b = GetImmediate19(instr);
} else {
if (instr.is_b_gpr) {
op_b += regs.GetRegisterAsFloat(instr.gpr20);
op_b = regs.GetRegisterAsFloat(instr.gpr20);
} else {
op_b += regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
GLSLRegister::Type::Float);
op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
GLSLRegister::Type::Float);
}
}
@@ -793,6 +848,10 @@ private:
op_b = "abs(" + op_b + ')';
}
if (instr.alu.negate_b) {
op_b = "-(" + op_b + ')';
}
switch (opcode->GetId()) {
case OpCode::Id::MOV_C:
case OpCode::Id::MOV_R: {
@@ -808,9 +867,8 @@ private:
case OpCode::Id::FMUL_C:
case OpCode::Id::FMUL_R:
case OpCode::Id::FMUL_IMM: {
ASSERT_MSG(!instr.saturate_a, "Unimplemented");
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, instr.alu.abs_d);
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
instr.alu.saturate_d);
break;
}
case OpCode::Id::FMUL32_IMM: {
@@ -823,41 +881,39 @@ private:
case OpCode::Id::FADD_C:
case OpCode::Id::FADD_R:
case OpCode::Id::FADD_IMM: {
ASSERT_MSG(!instr.saturate_a, "Unimplemented");
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1, instr.alu.abs_d);
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " + " + op_b, 1, 1,
instr.alu.saturate_d);
break;
}
case OpCode::Id::MUFU: {
ASSERT_MSG(!instr.saturate_a, "Unimplemented");
switch (instr.sub_op) {
case SubOp::Cos:
regs.SetRegisterToFloat(instr.gpr0, 0, "cos(" + op_a + ')', 1, 1,
instr.alu.abs_d);
instr.alu.saturate_d);
break;
case SubOp::Sin:
regs.SetRegisterToFloat(instr.gpr0, 0, "sin(" + op_a + ')', 1, 1,
instr.alu.abs_d);
instr.alu.saturate_d);
break;
case SubOp::Ex2:
regs.SetRegisterToFloat(instr.gpr0, 0, "exp2(" + op_a + ')', 1, 1,
instr.alu.abs_d);
instr.alu.saturate_d);
break;
case SubOp::Lg2:
regs.SetRegisterToFloat(instr.gpr0, 0, "log2(" + op_a + ')', 1, 1,
instr.alu.abs_d);
instr.alu.saturate_d);
break;
case SubOp::Rcp:
regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1, instr.alu.abs_d);
regs.SetRegisterToFloat(instr.gpr0, 0, "1.0 / " + op_a, 1, 1,
instr.alu.saturate_d);
break;
case SubOp::Rsq:
regs.SetRegisterToFloat(instr.gpr0, 0, "inversesqrt(" + op_a + ')', 1, 1,
instr.alu.abs_d);
instr.alu.saturate_d);
break;
case SubOp::Min:
regs.SetRegisterToFloat(instr.gpr0, 0, "min(" + op_a + "," + op_b + ')', 1, 1,
instr.alu.abs_d);
instr.alu.saturate_d);
break;
default:
NGLOG_CRITICAL(HW_GPU, "Unhandled MUFU sub op: {0:x}",
@@ -919,49 +975,6 @@ private:
break;
}
case OpCode::Type::Logic: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
if (instr.alu.lop.invert_a)
op_a = "~(" + op_a + ')';
switch (opcode->GetId()) {
case OpCode::Id::LOP32I: {
u32 imm = static_cast<u32>(instr.alu.imm20_32.Value());
if (instr.alu.lop.invert_b)
imm = ~imm;
switch (instr.alu.lop.operation) {
case Tegra::Shader::LogicOperation::And: {
regs.SetRegisterToInteger(instr.gpr0, true, 0,
'(' + op_a + " & " + std::to_string(imm) + ')', 1, 1);
break;
}
case Tegra::Shader::LogicOperation::Or: {
regs.SetRegisterToInteger(instr.gpr0, true, 0,
'(' + op_a + " | " + std::to_string(imm) + ')', 1, 1);
break;
}
case Tegra::Shader::LogicOperation::Xor: {
regs.SetRegisterToInteger(instr.gpr0, true, 0,
'(' + op_a + " ^ " + std::to_string(imm) + ')', 1, 1);
break;
}
default:
NGLOG_CRITICAL(HW_GPU, "Unimplemented lop32i operation: {}",
static_cast<u32>(instr.alu.lop.operation.Value()));
UNREACHABLE();
}
break;
}
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled logic instruction: {}", opcode->GetName());
UNREACHABLE();
}
}
break;
}
case OpCode::Type::Shift: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, true);
@@ -1005,14 +1018,39 @@ private:
break;
}
case OpCode::Type::ArithmeticIntegerImmediate: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
std::string op_b = std::to_string(instr.alu.imm20_32.Value());
switch (opcode->GetId()) {
case OpCode::Id::IADD32I:
if (instr.iadd32i.negate_a)
op_a = "-(" + op_a + ')';
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
instr.iadd32i.saturate != 0);
break;
case OpCode::Id::LOP32I: {
if (instr.alu.lop32i.invert_a)
op_a = "~(" + op_a + ')';
if (instr.alu.lop32i.invert_b)
op_b = "~(" + op_b + ')';
WriteLogicOperation(instr.gpr0, instr.alu.lop32i.operation, op_a, op_b);
break;
}
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled ArithmeticIntegerImmediate instruction: {}",
opcode->GetName());
UNREACHABLE();
}
}
break;
}
case OpCode::Type::ArithmeticInteger: {
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
if (instr.alu_integer.negate_a)
op_a = '-' + op_a;
std::string op_b = instr.alu_integer.negate_b ? "-" : "";
std::string op_b;
if (instr.is_b_imm) {
op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')';
} else {
@@ -1028,19 +1066,46 @@ private:
case OpCode::Id::IADD_C:
case OpCode::Id::IADD_R:
case OpCode::Id::IADD_IMM: {
ASSERT_MSG(!instr.saturate_a, "Unimplemented");
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1);
if (instr.alu_integer.negate_a)
op_a = "-(" + op_a + ')';
if (instr.alu_integer.negate_b)
op_b = "-(" + op_b + ')';
regs.SetRegisterToInteger(instr.gpr0, true, 0, op_a + " + " + op_b, 1, 1,
instr.alu.saturate_d);
break;
}
case OpCode::Id::ISCADD_C:
case OpCode::Id::ISCADD_R:
case OpCode::Id::ISCADD_IMM: {
if (instr.alu_integer.negate_a)
op_a = "-(" + op_a + ')';
if (instr.alu_integer.negate_b)
op_b = "-(" + op_b + ')';
std::string shift = std::to_string(instr.alu_integer.shift_amount.Value());
regs.SetRegisterToInteger(instr.gpr0, true, 0,
"((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
break;
}
case OpCode::Id::LOP_C:
case OpCode::Id::LOP_R:
case OpCode::Id::LOP_IMM: {
ASSERT_MSG(!instr.alu.lop.unk44, "Unimplemented");
ASSERT_MSG(instr.alu.lop.pred48 == Pred::UnusedIndex, "Unimplemented");
if (instr.alu.lop.invert_a)
op_a = "~(" + op_a + ')';
if (instr.alu.lop.invert_b)
op_b = "~(" + op_b + ')';
WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b);
break;
}
default: {
NGLOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}",
opcode->GetName());
@@ -1051,8 +1116,6 @@ private:
break;
}
case OpCode::Type::Ffma: {
ASSERT_MSG(!instr.saturate_a, "Unimplemented");
std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
std::string op_b = instr.ffma.negate_b ? "-" : "";
std::string op_c = instr.ffma.negate_c ? "-" : "";
@@ -1086,33 +1149,33 @@ private:
}
}
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1);
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b + " + " + op_c, 1, 1,
instr.alu.saturate_d);
break;
}
case OpCode::Type::Conversion: {
ASSERT_MSG(instr.conversion.size == Register::Size::Word, "Unimplemented");
ASSERT_MSG(!instr.conversion.negate_a, "Unimplemented");
ASSERT_MSG(!instr.saturate_a, "Unimplemented");
switch (opcode->GetId()) {
case OpCode::Id::I2I_R: {
ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
std::string op_a =
regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed);
std::string op_a = regs.GetRegisterAsInteger(
instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
if (instr.conversion.abs_a) {
op_a = "abs(" + op_a + ')';
}
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
1);
1, instr.alu.saturate_d, 0, instr.conversion.dest_size);
break;
}
case OpCode::Id::I2F_R: {
ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented");
ASSERT_MSG(!instr.conversion.selector, "Unimplemented");
std::string op_a =
regs.GetRegisterAsInteger(instr.gpr20, 0, instr.conversion.is_input_signed);
std::string op_a = regs.GetRegisterAsInteger(
instr.gpr20, 0, instr.conversion.is_input_signed, instr.conversion.src_size);
if (instr.conversion.abs_a) {
op_a = "abs(" + op_a + ')';
@@ -1122,8 +1185,8 @@ private:
break;
}
case OpCode::Id::F2F_R: {
ASSERT_MSG(!instr.saturate_a, "Unimplemented");
ASSERT_MSG(instr.conversion.dest_size == Register::Size::Word, "Unimplemented");
ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented");
std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
switch (instr.conversion.f2f.rounding) {
@@ -1149,10 +1212,11 @@ private:
op_a = "abs(" + op_a + ')';
}
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1);
regs.SetRegisterToFloat(instr.gpr0, 0, op_a, 1, 1, instr.alu.saturate_d);
break;
}
case OpCode::Id::F2I_R: {
ASSERT_MSG(instr.conversion.src_size == Register::Size::Word, "Unimplemented");
std::string op_a = regs.GetRegisterAsFloat(instr.gpr20);
if (instr.conversion.abs_a) {
@@ -1185,7 +1249,7 @@ private:
}
regs.SetRegisterToInteger(instr.gpr0, instr.conversion.is_output_signed, 0, op_a, 1,
1);
1, false, 0, instr.conversion.dest_size);
break;
}
default: {

View File

@@ -39,6 +39,10 @@ void main() {
// Viewport can be flipped, which is unsupported by glViewport
position.xy *= viewport_flip.xy;
gl_Position = position;
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
// For now, this is here to bring order in lieu of proper emulation
position.w = 1.0;
}
)";
out += program.first;

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,15 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstdint>
#include <vector>
namespace Tegra::Texture::ASTC {
std::vector<uint8_t> Decompress(std::vector<uint8_t>& data, uint32_t width, uint32_t height,
uint32_t block_width, uint32_t block_height);
} // namespace Tegra::Texture::ASTC

View File

@@ -53,6 +53,7 @@ u32 BytesPerPixel(TextureFormat format) {
case TextureFormat::DXT45:
// In this case a 'pixel' actually refers to a 4x4 tile.
return 16;
case TextureFormat::ASTC_2D_4X4:
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
case TextureFormat::BF10GF11RF11:
@@ -94,6 +95,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width,
case TextureFormat::R8:
case TextureFormat::R16_G16_B16_A16:
case TextureFormat::BF10GF11RF11:
case TextureFormat::ASTC_2D_4X4:
CopySwizzledData(width, height, bytes_per_pixel, bytes_per_pixel, data,
unswizzled_data.data(), true, block_height);
break;
@@ -115,6 +117,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
case TextureFormat::DXT23:
case TextureFormat::DXT45:
case TextureFormat::DXN1:
case TextureFormat::ASTC_2D_4X4:
case TextureFormat::A8R8G8B8:
case TextureFormat::A2B10G10R10:
case TextureFormat::A1B5G5R5:

View File

@@ -32,8 +32,6 @@ add_executable(yuzu
debugger/graphics/graphics_surface.h
debugger/profiler.cpp
debugger/profiler.h
debugger/registers.cpp
debugger/registers.h
debugger/wait_tree.cpp
debugger/wait_tree.h
game_list.cpp
@@ -60,7 +58,6 @@ set(UIS
configuration/configure_graphics.ui
configuration/configure_input.ui
configuration/configure_system.ui
debugger/registers.ui
hotkeys.ui
main.ui
)

View File

@@ -1,190 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <QTreeWidgetItem>
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "yuzu/debugger/registers.h"
#include "yuzu/util/util.h"
RegistersWidget::RegistersWidget(QWidget* parent) : QDockWidget(parent) {
cpu_regs_ui.setupUi(this);
tree = cpu_regs_ui.treeWidget;
tree->addTopLevelItem(core_registers = new QTreeWidgetItem(QStringList(tr("Registers"))));
tree->addTopLevelItem(vfp_registers = new QTreeWidgetItem(QStringList(tr("VFP Registers"))));
tree->addTopLevelItem(vfp_system_registers =
new QTreeWidgetItem(QStringList(tr("VFP System Registers"))));
tree->addTopLevelItem(cpsr = new QTreeWidgetItem(QStringList("CPSR")));
for (int i = 0; i < 16; ++i) {
QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("R[%1]").arg(i)));
core_registers->addChild(child);
}
for (int i = 0; i < 32; ++i) {
QTreeWidgetItem* child = new QTreeWidgetItem(QStringList(QString("S[%1]").arg(i)));
vfp_registers->addChild(child);
}
QFont font = GetMonospaceFont();
CreateCPSRChildren();
CreateVFPSystemRegisterChildren();
// Set Registers to display in monospace font
for (int i = 0; i < core_registers->childCount(); ++i)
core_registers->child(i)->setFont(1, font);
for (int i = 0; i < vfp_registers->childCount(); ++i)
vfp_registers->child(i)->setFont(1, font);
for (int i = 0; i < vfp_system_registers->childCount(); ++i) {
vfp_system_registers->child(i)->setFont(1, font);
for (int x = 0; x < vfp_system_registers->child(i)->childCount(); ++x) {
vfp_system_registers->child(i)->child(x)->setFont(1, font);
}
}
// Set CSPR to display in monospace font
cpsr->setFont(1, font);
for (int i = 0; i < cpsr->childCount(); ++i) {
cpsr->child(i)->setFont(1, font);
for (int x = 0; x < cpsr->child(i)->childCount(); ++x) {
cpsr->child(i)->child(x)->setFont(1, font);
}
}
setEnabled(false);
}
void RegistersWidget::OnDebugModeEntered() {
if (!Core::System::GetInstance().IsPoweredOn())
return;
for (int i = 0; i < core_registers->childCount(); ++i)
core_registers->child(i)->setText(
1, QString("0x%1").arg(Core::CurrentArmInterface().GetReg(i), 8, 16, QLatin1Char('0')));
UpdateCPSRValues();
}
void RegistersWidget::OnDebugModeLeft() {}
void RegistersWidget::OnEmulationStarting(EmuThread* emu_thread) {
setEnabled(true);
}
void RegistersWidget::OnEmulationStopping() {
// Reset widget text
for (int i = 0; i < core_registers->childCount(); ++i)
core_registers->child(i)->setText(1, QString(""));
for (int i = 0; i < vfp_registers->childCount(); ++i)
vfp_registers->child(i)->setText(1, QString(""));
for (int i = 0; i < cpsr->childCount(); ++i)
cpsr->child(i)->setText(1, QString(""));
cpsr->setText(1, QString(""));
// FPSCR
for (int i = 0; i < vfp_system_registers->child(0)->childCount(); ++i)
vfp_system_registers->child(0)->child(i)->setText(1, QString(""));
// FPEXC
for (int i = 0; i < vfp_system_registers->child(1)->childCount(); ++i)
vfp_system_registers->child(1)->child(i)->setText(1, QString(""));
vfp_system_registers->child(0)->setText(1, QString(""));
vfp_system_registers->child(1)->setText(1, QString(""));
vfp_system_registers->child(2)->setText(1, QString(""));
vfp_system_registers->child(3)->setText(1, QString(""));
setEnabled(false);
}
void RegistersWidget::CreateCPSRChildren() {
cpsr->addChild(new QTreeWidgetItem(QStringList("M")));
cpsr->addChild(new QTreeWidgetItem(QStringList("T")));
cpsr->addChild(new QTreeWidgetItem(QStringList("F")));
cpsr->addChild(new QTreeWidgetItem(QStringList("I")));
cpsr->addChild(new QTreeWidgetItem(QStringList("A")));
cpsr->addChild(new QTreeWidgetItem(QStringList("E")));
cpsr->addChild(new QTreeWidgetItem(QStringList("IT")));
cpsr->addChild(new QTreeWidgetItem(QStringList("GE")));
cpsr->addChild(new QTreeWidgetItem(QStringList("DNM")));
cpsr->addChild(new QTreeWidgetItem(QStringList("J")));
cpsr->addChild(new QTreeWidgetItem(QStringList("Q")));
cpsr->addChild(new QTreeWidgetItem(QStringList("V")));
cpsr->addChild(new QTreeWidgetItem(QStringList("C")));
cpsr->addChild(new QTreeWidgetItem(QStringList("Z")));
cpsr->addChild(new QTreeWidgetItem(QStringList("N")));
}
void RegistersWidget::UpdateCPSRValues() {
const u32 cpsr_val = Core::CurrentArmInterface().GetCPSR();
cpsr->setText(1, QString("0x%1").arg(cpsr_val, 8, 16, QLatin1Char('0')));
cpsr->child(0)->setText(
1, QString("b%1").arg(cpsr_val & 0x1F, 5, 2, QLatin1Char('0'))); // M - Mode
cpsr->child(1)->setText(1, QString::number((cpsr_val >> 5) & 1)); // T - State
cpsr->child(2)->setText(1, QString::number((cpsr_val >> 6) & 1)); // F - FIQ disable
cpsr->child(3)->setText(1, QString::number((cpsr_val >> 7) & 1)); // I - IRQ disable
cpsr->child(4)->setText(1, QString::number((cpsr_val >> 8) & 1)); // A - Imprecise abort
cpsr->child(5)->setText(1, QString::number((cpsr_val >> 9) & 1)); // E - Data endianness
cpsr->child(6)->setText(1,
QString::number((cpsr_val >> 10) & 0x3F)); // IT - If-Then state (DNM)
cpsr->child(7)->setText(1,
QString::number((cpsr_val >> 16) & 0xF)); // GE - Greater-than-or-Equal
cpsr->child(8)->setText(1, QString::number((cpsr_val >> 20) & 0xF)); // DNM - Do not modify
cpsr->child(9)->setText(1, QString::number((cpsr_val >> 24) & 1)); // J - Jazelle
cpsr->child(10)->setText(1, QString::number((cpsr_val >> 27) & 1)); // Q - Saturation
cpsr->child(11)->setText(1, QString::number((cpsr_val >> 28) & 1)); // V - Overflow
cpsr->child(12)->setText(1, QString::number((cpsr_val >> 29) & 1)); // C - Carry/Borrow/Extend
cpsr->child(13)->setText(1, QString::number((cpsr_val >> 30) & 1)); // Z - Zero
cpsr->child(14)->setText(1, QString::number((cpsr_val >> 31) & 1)); // N - Negative/Less than
}
void RegistersWidget::CreateVFPSystemRegisterChildren() {
QTreeWidgetItem* const fpscr = new QTreeWidgetItem(QStringList("FPSCR"));
fpscr->addChild(new QTreeWidgetItem(QStringList("IOC")));
fpscr->addChild(new QTreeWidgetItem(QStringList("DZC")));
fpscr->addChild(new QTreeWidgetItem(QStringList("OFC")));
fpscr->addChild(new QTreeWidgetItem(QStringList("UFC")));
fpscr->addChild(new QTreeWidgetItem(QStringList("IXC")));
fpscr->addChild(new QTreeWidgetItem(QStringList("IDC")));
fpscr->addChild(new QTreeWidgetItem(QStringList("IOE")));
fpscr->addChild(new QTreeWidgetItem(QStringList("DZE")));
fpscr->addChild(new QTreeWidgetItem(QStringList("OFE")));
fpscr->addChild(new QTreeWidgetItem(QStringList("UFE")));
fpscr->addChild(new QTreeWidgetItem(QStringList("IXE")));
fpscr->addChild(new QTreeWidgetItem(QStringList("IDE")));
fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Vector Length"))));
fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Vector Stride"))));
fpscr->addChild(new QTreeWidgetItem(QStringList(tr("Rounding Mode"))));
fpscr->addChild(new QTreeWidgetItem(QStringList("FZ")));
fpscr->addChild(new QTreeWidgetItem(QStringList("DN")));
fpscr->addChild(new QTreeWidgetItem(QStringList("V")));
fpscr->addChild(new QTreeWidgetItem(QStringList("C")));
fpscr->addChild(new QTreeWidgetItem(QStringList("Z")));
fpscr->addChild(new QTreeWidgetItem(QStringList("N")));
QTreeWidgetItem* const fpexc = new QTreeWidgetItem(QStringList("FPEXC"));
fpexc->addChild(new QTreeWidgetItem(QStringList("IOC")));
fpexc->addChild(new QTreeWidgetItem(QStringList("OFC")));
fpexc->addChild(new QTreeWidgetItem(QStringList("UFC")));
fpexc->addChild(new QTreeWidgetItem(QStringList("INV")));
fpexc->addChild(new QTreeWidgetItem(QStringList(tr("Vector Iteration Count"))));
fpexc->addChild(new QTreeWidgetItem(QStringList("FP2V")));
fpexc->addChild(new QTreeWidgetItem(QStringList("EN")));
fpexc->addChild(new QTreeWidgetItem(QStringList("EX")));
vfp_system_registers->addChild(fpscr);
vfp_system_registers->addChild(fpexc);
vfp_system_registers->addChild(new QTreeWidgetItem(QStringList("FPINST")));
vfp_system_registers->addChild(new QTreeWidgetItem(QStringList("FPINST2")));
}
void RegistersWidget::UpdateVFPSystemRegisterValues() {
UNIMPLEMENTED();
}

View File

@@ -1,42 +0,0 @@
// Copyright 2014 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <QDockWidget>
#include "ui_registers.h"
class QTreeWidget;
class QTreeWidgetItem;
class EmuThread;
class RegistersWidget : public QDockWidget {
Q_OBJECT
public:
explicit RegistersWidget(QWidget* parent = nullptr);
public slots:
void OnDebugModeEntered();
void OnDebugModeLeft();
void OnEmulationStarting(EmuThread* emu_thread);
void OnEmulationStopping();
private:
void CreateCPSRChildren();
void UpdateCPSRValues();
void CreateVFPSystemRegisterChildren();
void UpdateVFPSystemRegisterValues();
Ui::ARMRegisters cpu_regs_ui;
QTreeWidget* tree;
QTreeWidgetItem* core_registers;
QTreeWidgetItem* vfp_registers;
QTreeWidgetItem* vfp_system_registers;
QTreeWidgetItem* cpsr;
};

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ARMRegisters</class>
<widget class="QDockWidget" name="ARMRegisters">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle">
<string>ARM Registers</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QTreeWidget" name="treeWidget">
<property name="alternatingRowColors">
<bool>true</bool>
</property>
<column>
<property name="text">
<string>Register</string>
</property>
</column>
<column>
<property name="text">
<string>Value</string>
</property>
</column>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <QApplication>
#include <QDir>
#include <QFileInfo>
#include <QHeaderView>
#include <QKeyEvent>
@@ -264,8 +265,17 @@ void GameList::ValidateEntry(const QModelIndex& item) {
if (file_path.isEmpty())
return;
std::string std_file_path(file_path.toStdString());
if (!FileUtil::Exists(std_file_path) || FileUtil::IsDirectory(std_file_path))
if (!FileUtil::Exists(std_file_path))
return;
if (FileUtil::IsDirectory(std_file_path)) {
QDir dir(std_file_path.c_str());
QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
emit GameChosen(dir.path() + DIR_SEP + matching_main[0]);
}
return;
}
// Users usually want to run a diffrent game after closing one
search_field->clear();
emit GameChosen(file_path);
@@ -363,6 +373,19 @@ static bool HasSupportedFileExtension(const std::string& file_name) {
return GameList::supported_file_extensions.contains(file.suffix(), Qt::CaseInsensitive);
}
static bool IsExtractedNCAMain(const std::string& file_name) {
return QFileInfo(file_name.c_str()).fileName() == "main";
}
static QString FormatGameName(const std::string& physical_name) {
QFileInfo file_info(physical_name.c_str());
if (IsExtractedNCAMain(physical_name)) {
return file_info.dir().path();
} else {
return QString::fromStdString(physical_name);
}
}
void GameList::RefreshGameDirectory() {
if (!UISettings::values.gamedir.isEmpty() && current_worker != nullptr) {
NGLOG_INFO(Frontend, "Change detected in the games directory. Reloading game list.");
@@ -380,7 +403,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
return false; // Breaks the callback loop.
bool is_dir = FileUtil::IsDirectory(physical_name);
if (!is_dir && HasSupportedFileExtension(physical_name)) {
if (!is_dir &&
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name);
if (!loader)
return true;
@@ -392,7 +416,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
loader->ReadProgramId(program_id);
emit EntryReady({
new GameListItemPath(QString::fromStdString(physical_name), smdh, program_id),
new GameListItemPath(FormatGameName(physical_name), smdh, program_id),
new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)),

View File

@@ -13,6 +13,7 @@
#include <QMessageBox>
#include <QtGui>
#include <QtWidgets>
#include "common/common_paths.h"
#include "common/logging/backend.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
@@ -33,7 +34,6 @@
#include "yuzu/debugger/graphics/graphics_breakpoints.h"
#include "yuzu/debugger/graphics/graphics_surface.h"
#include "yuzu/debugger/profiler.h"
#include "yuzu/debugger/registers.h"
#include "yuzu/debugger/wait_tree.h"
#include "yuzu/game_list.h"
#include "yuzu/hotkeys.h"
@@ -169,15 +169,6 @@ void GMainWindow::InitializeDebugWidgets() {
debug_menu->addAction(microProfileDialog->toggleViewAction());
#endif
registersWidget = new RegistersWidget(this);
addDockWidget(Qt::RightDockWidgetArea, registersWidget);
registersWidget->hide();
debug_menu->addAction(registersWidget->toggleViewAction());
connect(this, &GMainWindow::EmulationStarting, registersWidget,
&RegistersWidget::OnEmulationStarting);
connect(this, &GMainWindow::EmulationStopping, registersWidget,
&RegistersWidget::OnEmulationStopping);
graphicsBreakpointsWidget = new GraphicsBreakPointsWidget(debug_context, this);
addDockWidget(Qt::RightDockWidgetArea, graphicsBreakpointsWidget);
graphicsBreakpointsWidget->hide();
@@ -288,6 +279,7 @@ void GMainWindow::ConnectWidgetEvents() {
void GMainWindow::ConnectMenuEvents() {
// File
connect(ui.action_Load_File, &QAction::triggered, this, &GMainWindow::OnMenuLoadFile);
connect(ui.action_Load_Folder, &QAction::triggered, this, &GMainWindow::OnMenuLoadFolder);
connect(ui.action_Select_Game_List_Root, &QAction::triggered, this,
&GMainWindow::OnMenuSelectGameListRoot);
connect(ui.action_Exit, &QAction::triggered, this, &QMainWindow::close);
@@ -460,17 +452,12 @@ void GMainWindow::BootGame(const QString& filename) {
connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
// BlockingQueuedConnection is important here, it makes sure we've finished refreshing our views
// before the CPU continues
connect(emu_thread.get(), &EmuThread::DebugModeEntered, registersWidget,
&RegistersWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
connect(emu_thread.get(), &EmuThread::DebugModeEntered, waitTreeWidget,
&WaitTreeWidget::OnDebugModeEntered, Qt::BlockingQueuedConnection);
connect(emu_thread.get(), &EmuThread::DebugModeLeft, registersWidget,
&RegistersWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection);
connect(emu_thread.get(), &EmuThread::DebugModeLeft, waitTreeWidget,
&WaitTreeWidget::OnDebugModeLeft, Qt::BlockingQueuedConnection);
// Update the GUI
registersWidget->OnDebugModeEntered();
if (ui.action_Single_Window_Mode->isChecked()) {
game_list->hide();
}
@@ -565,6 +552,8 @@ void GMainWindow::OnMenuLoadFile() {
for (const auto& piece : game_list->supported_file_extensions)
extensions += "*." + piece + " ";
extensions += "main ";
QString file_filter = tr("Switch Executable") + " (" + extensions + ")";
file_filter += ";;" + tr("All Files (*.*)");
@@ -577,6 +566,18 @@ void GMainWindow::OnMenuLoadFile() {
}
}
void GMainWindow::OnMenuLoadFolder() {
QDir dir = QFileDialog::getExistingDirectory(this, tr("Open Extracted ROM Directory"));
QStringList matching_main = dir.entryList(QStringList("main"), QDir::Files);
if (matching_main.size() == 1) {
BootGame(dir.path() + DIR_SEP + matching_main[0]);
} else {
QMessageBox::warning(this, tr("Invalid Directory Selected"),
tr("The directory you have selected does not contain a 'main' file."));
}
}
void GMainWindow::OnMenuSelectGameListRoot() {
QString dir_path = QFileDialog::getExistingDirectory(this, tr("Select Directory"));
if (!dir_path.isEmpty()) {

View File

@@ -19,7 +19,6 @@ class GraphicsSurfaceWidget;
class GRenderWindow;
class MicroProfileDialog;
class ProfilerWidget;
class RegistersWidget;
class WaitTreeWidget;
namespace Tegra {
@@ -124,6 +123,7 @@ private slots:
void OnGameListLoadFile(QString game_path);
void OnGameListOpenSaveFolder(u64 program_id);
void OnMenuLoadFile();
void OnMenuLoadFolder();
/// Called whenever a user selects the "File->Select Game List Root" menu item
void OnMenuSelectGameListRoot();
void OnMenuRecentFile();
@@ -163,7 +163,6 @@ private:
// Debugger panes
ProfilerWidget* profilerWidget;
MicroProfileDialog* microProfileDialog;
RegistersWidget* registersWidget;
GraphicsBreakPointsWidget* graphicsBreakpointsWidget;
GraphicsSurfaceWidget* graphicsSurfaceWidget;
WaitTreeWidget* waitTreeWidget;

View File

@@ -58,6 +58,7 @@
</property>
</widget>
<addaction name="action_Load_File"/>
<addaction name="action_Load_Folder"/>
<addaction name="separator"/>
<addaction name="action_Select_Game_List_Root"/>
<addaction name="menu_recent_files"/>
@@ -106,6 +107,11 @@
<string>Load File...</string>
</property>
</action>
<action name="action_Load_Folder">
<property name="text">
<string>Load Folder...</string>
</property>
</action>
<action name="action_Load_Symbol_Map">
<property name="text">
<string>Load Symbol Map...</string>