Compare commits
70 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c06d6b27f3 | ||
|
|
c3e95086b6 | ||
|
|
a3d82ef5d9 | ||
|
|
be1f5dedfb | ||
|
|
7a0bb406d5 | ||
|
|
0d8ae773f1 | ||
|
|
1ab133d7fa | ||
|
|
38989bef43 | ||
|
|
eab7457c00 | ||
|
|
0e13d9cb7b | ||
|
|
c11cfaa705 | ||
|
|
4ac4b308e4 | ||
|
|
ea080501fb | ||
|
|
bf4e2b2f0b | ||
|
|
7c7f4a9be2 | ||
|
|
61779fa072 | ||
|
|
d2277b825e | ||
|
|
fe906fff36 | ||
|
|
f9af74201c | ||
|
|
afdd657d30 | ||
|
|
5673ce39c7 | ||
|
|
3c43ea5c68 | ||
|
|
d383043e07 | ||
|
|
fb5bd0920d | ||
|
|
46cbb6b090 | ||
|
|
55c49d5bf4 | ||
|
|
61f9d9c4ab | ||
|
|
acc8fe5a2a | ||
|
|
f969ddb54e | ||
|
|
9f8fbce35b | ||
|
|
94d27b1717 | ||
|
|
ac88d3e89f | ||
|
|
a353322b58 | ||
|
|
50153a1cb2 | ||
|
|
17f3590d59 | ||
|
|
7786f41cc0 | ||
|
|
019d7208c8 | ||
|
|
2015a1b180 | ||
|
|
db0497b808 | ||
|
|
987a170665 | ||
|
|
33dbf24b56 | ||
|
|
2dc8b5c224 | ||
|
|
5f3d6c85db | ||
|
|
2f9c0e7c7e | ||
|
|
09b8a16414 | ||
|
|
004b1b3830 | ||
|
|
281fd881a0 | ||
|
|
b366b885a1 | ||
|
|
3cb753eeb1 | ||
|
|
d81aaa3ed3 | ||
|
|
e2176dc7ce | ||
|
|
174c22e5f6 | ||
|
|
5440b9c634 | ||
|
|
abec5f82e2 | ||
|
|
bbc4f369ed | ||
|
|
79e9c2e237 | ||
|
|
83517cb53a | ||
|
|
9949e4d508 | ||
|
|
c116b220e9 | ||
|
|
c011b6f67e | ||
|
|
c712dafaee | ||
|
|
a931cf9e8b | ||
|
|
a941a94148 | ||
|
|
8d9534d830 | ||
|
|
47dc5e0dab | ||
|
|
f3885845fc | ||
|
|
c0d3e2da4e | ||
|
|
517112f549 | ||
|
|
6324d86c71 | ||
|
|
a2efb1dd48 |
@@ -42,3 +42,7 @@ notifications:
|
||||
webhooks:
|
||||
urls:
|
||||
- https://api.yuzu-emu.org/code/travis/notify
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.ccache
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
docker run -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
docker run -e CCACHE_DIR=/ccache -v $HOME/.ccache:/ccache -v $(pwd):/yuzu ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
#!/bin/bash -ex
|
||||
|
||||
apt-get update
|
||||
apt-get install -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget ninja-build
|
||||
|
||||
# Get a recent version of CMake
|
||||
wget https://cmake.org/files/v3.10/cmake-3.10.1-Linux-x86_64.sh
|
||||
sh cmake-3.10.1-Linux-x86_64.sh --exclude-subdir --prefix=/ --skip-license
|
||||
apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev libsdl2-dev libssl-dev python qtbase5-dev wget cmake ninja-build ccache
|
||||
|
||||
cd /yuzu
|
||||
|
||||
export PATH=/usr/lib/ccache:$PATH
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/cc
|
||||
ln -sf /usr/bin/ccache /usr/lib/ccache/c++
|
||||
mkdir build && cd build
|
||||
ccache --show-stats > ccache_before
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
ninja
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -7,8 +7,12 @@ export Qt5_DIR=$(brew --prefix)/opt/qt5
|
||||
export UNICORNDIR=$(pwd)/externals/unicorn
|
||||
|
||||
mkdir build && cd build
|
||||
export PATH=/usr/local/opt/ccache/libexec:$PATH
|
||||
ccache --show-stats > ccache_before
|
||||
cmake --version
|
||||
cmake .. -DYUZU_BUILD_UNICORN=ON -DCMAKE_BUILD_TYPE=Release
|
||||
make -j4
|
||||
ccache --show-stats > ccache_after
|
||||
diff -U100 ccache_before ccache_after || true
|
||||
|
||||
ctest -VV -C Release
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh -ex
|
||||
|
||||
brew update
|
||||
brew install dylibbundler p7zip qt5 sdl2
|
||||
brew install dylibbundler p7zip qt5 sdl2 ccache
|
||||
brew outdated cmake || brew upgrade cmake
|
||||
|
||||
@@ -64,6 +64,10 @@ std::string ArrayToString(const u8* data, size_t size, int line_len, bool spaces
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data) {
|
||||
return std::string(data.begin(), std::find(data.begin(), data.end(), '\0'));
|
||||
}
|
||||
|
||||
// Turns " hej " into "hej". Also handles tabs.
|
||||
std::string StripSpaces(const std::string& str) {
|
||||
const size_t s = str.find_first_not_of(" \t\r\n");
|
||||
|
||||
@@ -21,6 +21,8 @@ std::string ToUpper(std::string str);
|
||||
|
||||
std::string ArrayToString(const u8* data, size_t size, int line_len = 20, bool spaces = true);
|
||||
|
||||
std::string StringFromBuffer(const std::vector<u8>& data);
|
||||
|
||||
std::string StripSpaces(const std::string& s);
|
||||
std::string StripQuotes(const std::string& s);
|
||||
|
||||
|
||||
@@ -44,14 +44,14 @@ Cpu& System::CurrentCpuCore() {
|
||||
}
|
||||
|
||||
// Otherwise, use single-threaded mode active_core variable
|
||||
return *cpu_cores[active_core = 4];
|
||||
return *cpu_cores[active_core];
|
||||
}
|
||||
|
||||
System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
status = ResultStatus::Success;
|
||||
|
||||
// Update thread_to_cpu in case Core 0 is run from a different host thread
|
||||
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[4];
|
||||
thread_to_cpu[std::this_thread::get_id()] = cpu_cores[0];
|
||||
|
||||
if (GDBStub::IsServerEnabled()) {
|
||||
GDBStub::HandlePacket();
|
||||
@@ -68,7 +68,7 @@ System::ResultStatus System::RunLoop(bool tight_loop) {
|
||||
}
|
||||
}
|
||||
|
||||
for (active_core = 4; active_core < NUM_CPU_CORES; ++active_core) {
|
||||
for (active_core = 0; active_core < NUM_CPU_CORES; ++active_core) {
|
||||
cpu_cores[active_core]->RunLoop(tight_loop);
|
||||
if (Settings::values.use_multi_core) {
|
||||
// Cores 1-3 are run on other threads in this mode
|
||||
|
||||
@@ -73,21 +73,21 @@ u32 PartitionFilesystem::GetNumEntries() const {
|
||||
return pfs_header.num_entries;
|
||||
}
|
||||
|
||||
u64 PartitionFilesystem::GetEntryOffset(int index) const {
|
||||
u64 PartitionFilesystem::GetEntryOffset(u32 index) const {
|
||||
if (index > GetNumEntries())
|
||||
return 0;
|
||||
|
||||
return content_offset + pfs_entries[index].fs_entry.offset;
|
||||
}
|
||||
|
||||
u64 PartitionFilesystem::GetEntrySize(int index) const {
|
||||
u64 PartitionFilesystem::GetEntrySize(u32 index) const {
|
||||
if (index > GetNumEntries())
|
||||
return 0;
|
||||
|
||||
return pfs_entries[index].fs_entry.size;
|
||||
}
|
||||
|
||||
std::string PartitionFilesystem::GetEntryName(int index) const {
|
||||
std::string PartitionFilesystem::GetEntryName(u32 index) const {
|
||||
if (index > GetNumEntries())
|
||||
return "";
|
||||
|
||||
|
||||
@@ -27,9 +27,9 @@ public:
|
||||
Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0);
|
||||
|
||||
u32 GetNumEntries() const;
|
||||
u64 GetEntryOffset(int index) const;
|
||||
u64 GetEntrySize(int index) const;
|
||||
std::string GetEntryName(int index) const;
|
||||
u64 GetEntryOffset(u32 index) const;
|
||||
u64 GetEntrySize(u32 index) const;
|
||||
std::string GetEntryName(u32 index) const;
|
||||
u64 GetFileOffset(const std::string& name) const;
|
||||
u64 GetFileSize(const std::string& name) const;
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -744,7 +749,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
ASSERT(thread->owner_process->ideal_processor != THREADPROCESSORID_DEFAULT);
|
||||
// Set the target CPU to the one specified in the process' exheader.
|
||||
core = thread->owner_process->ideal_processor;
|
||||
mask = 1 << core;
|
||||
mask = 1ull << core;
|
||||
}
|
||||
|
||||
if (mask == 0) {
|
||||
@@ -761,7 +766,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
|
||||
}
|
||||
|
||||
// Error out if the input core isn't enabled in the input mask.
|
||||
if (core < Core::NUM_CPU_CORES && (mask & (1 << core)) == 0) {
|
||||
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
|
||||
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,27 +57,26 @@ private:
|
||||
}
|
||||
|
||||
void RequestUpdateAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
NGLOG_DEBUG(Service_Audio, "{}", ctx.Description());
|
||||
AudioRendererResponseData response_data{};
|
||||
AudioRendererConfig config;
|
||||
auto buf = ctx.ReadBuffer();
|
||||
std::memcpy(&config, buf.data(), sizeof(AudioRendererConfig));
|
||||
|
||||
response_data.section_0_size =
|
||||
static_cast<u32>(response_data.state_entries.size() * sizeof(AudioRendererStateEntry));
|
||||
response_data.section_1_size = static_cast<u32>(response_data.section_1.size());
|
||||
response_data.section_2_size = static_cast<u32>(response_data.section_2.size());
|
||||
response_data.section_3_size = static_cast<u32>(response_data.section_3.size());
|
||||
response_data.section_4_size = static_cast<u32>(response_data.section_4.size());
|
||||
response_data.section_5_size = static_cast<u32>(response_data.section_5.size());
|
||||
response_data.total_size = sizeof(AudioRendererResponseData);
|
||||
AudioRendererResponse response_data{config};
|
||||
|
||||
for (unsigned i = 0; i < response_data.state_entries.size(); i++) {
|
||||
// 4 = Busy and 5 = Ready?
|
||||
response_data.state_entries[i].state = 5;
|
||||
ASSERT(ctx.GetWriteBufferSize() == response_data.total_size);
|
||||
|
||||
std::vector<u8> output(response_data.total_size);
|
||||
std::memcpy(output.data(), &response_data, sizeof(AudioRendererResponse));
|
||||
std::vector<MemoryPoolEntry> memory_pool(config.memory_pools_size / 0x20);
|
||||
for (auto& entry : memory_pool) {
|
||||
entry.state = 5;
|
||||
}
|
||||
std::memcpy(output.data() + sizeof(AudioRendererResponse), memory_pool.data(),
|
||||
response_data.memory_pools_size);
|
||||
|
||||
ctx.WriteBuffer(&response_data, response_data.total_size);
|
||||
ctx.WriteBuffer(output);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
|
||||
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
@@ -109,43 +108,55 @@ private:
|
||||
NGLOG_WARNING(Service_Audio, "(STUBBED) called");
|
||||
}
|
||||
|
||||
struct AudioRendererStateEntry {
|
||||
struct MemoryPoolEntry {
|
||||
u32_le state;
|
||||
u32_le unknown_4;
|
||||
u32_le unknown_8;
|
||||
u32_le unknown_c;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererStateEntry) == 0x10,
|
||||
"AudioRendererStateEntry has wrong size");
|
||||
static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
|
||||
|
||||
struct AudioRendererResponseData {
|
||||
u32_le unknown_0;
|
||||
u32_le section_5_size;
|
||||
u32_le section_0_size;
|
||||
u32_le section_1_size;
|
||||
u32_le unknown_10;
|
||||
u32_le section_2_size;
|
||||
u32_le unknown_18;
|
||||
u32_le section_3_size;
|
||||
u32_le section_4_size;
|
||||
u32_le unknown_24;
|
||||
u32_le unknown_28;
|
||||
u32_le unknown_2c;
|
||||
u32_le unknown_30;
|
||||
u32_le unknown_34;
|
||||
u32_le unknown_38;
|
||||
u32_le total_size;
|
||||
|
||||
std::array<AudioRendererStateEntry, 0x18e> state_entries;
|
||||
|
||||
std::array<u8, 0x600> section_1;
|
||||
std::array<u8, 0xe0> section_2;
|
||||
std::array<u8, 0x20> section_3;
|
||||
std::array<u8, 0x10> section_4;
|
||||
std::array<u8, 0xb0> section_5;
|
||||
struct AudioRendererConfig {
|
||||
u32 revision;
|
||||
u32 behavior_size;
|
||||
u32 memory_pools_size;
|
||||
u32 voices_size;
|
||||
u32 voice_resource_size;
|
||||
u32 effects_size;
|
||||
u32 mixes_size;
|
||||
u32 sinks_size;
|
||||
u32 performance_buffer_size;
|
||||
INSERT_PADDING_WORDS(6);
|
||||
u32 total_size;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererResponseData) == 0x20e0,
|
||||
"AudioRendererResponseData has wrong size");
|
||||
static_assert(sizeof(AudioRendererConfig) == 0x40, "AudioRendererConfig has wrong size");
|
||||
|
||||
struct AudioRendererResponse {
|
||||
AudioRendererResponse(const AudioRendererConfig& config) {
|
||||
revision = config.revision;
|
||||
error_info_size = 0xb0;
|
||||
memory_pools_size = (config.memory_pools_size / 0x20) * 0x10;
|
||||
voices_size = (config.voices_size / 0x170) * 0x10;
|
||||
effects_size = (config.effects_size / 0xC0) * 0x10;
|
||||
sinks_size = (config.sinks_size / 0x140) * 0x20;
|
||||
performance_manager_size = 0x10;
|
||||
total_size = sizeof(AudioRendererResponse) + error_info_size + memory_pools_size +
|
||||
voices_size + effects_size + sinks_size + performance_manager_size;
|
||||
}
|
||||
|
||||
u32_le revision;
|
||||
u32_le error_info_size;
|
||||
u32_le memory_pools_size;
|
||||
u32_le voices_size;
|
||||
u32_le unknown_10;
|
||||
u32_le effects_size;
|
||||
u32_le unknown_18;
|
||||
u32_le sinks_size;
|
||||
u32_le performance_manager_size;
|
||||
INSERT_PADDING_WORDS(6);
|
||||
u32_le total_size;
|
||||
};
|
||||
static_assert(sizeof(AudioRendererResponse) == 0x40, "AudioRendererResponse has wrong size");
|
||||
|
||||
/// This is used to trigger the audio event callback.
|
||||
CoreTiming::EventType* audio_event;
|
||||
@@ -258,7 +269,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
|
||||
|
||||
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
auto params = rp.PopRaw<WorkerBufferParameters>();
|
||||
auto params = rp.PopRaw<AudioRendererParameters>();
|
||||
|
||||
u64 buffer_sz = Common::AlignUp(4 * params.unknown8, 0x40);
|
||||
buffer_sz += params.unknownC * 1024;
|
||||
@@ -328,7 +339,7 @@ bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
|
||||
u32_be version_num = (revision - Common::MakeMagic('R', 'E', 'V', '0')); // Byte swap
|
||||
switch (feature) {
|
||||
case AudioFeatures::Splitter:
|
||||
return version_num >= 2;
|
||||
return version_num >= 2u;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ private:
|
||||
void GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx);
|
||||
void GetAudioDevice(Kernel::HLERequestContext& ctx);
|
||||
|
||||
struct WorkerBufferParameters {
|
||||
struct AudioRendererParameters {
|
||||
u32_le sample_rate;
|
||||
u32_le sample_count;
|
||||
u32_le unknown8;
|
||||
@@ -38,8 +38,8 @@ private:
|
||||
u8 padding2[4];
|
||||
u32_le magic;
|
||||
};
|
||||
static_assert(sizeof(WorkerBufferParameters) == 52,
|
||||
"WorkerBufferParameters is an invalid size");
|
||||
static_assert(sizeof(AudioRendererParameters) == 52,
|
||||
"AudioRendererParameters is an invalid size");
|
||||
|
||||
enum class AudioFeatures : u32 {
|
||||
Splitter,
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <cinttypes>
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "core/core.h"
|
||||
#include "core/file_sys/directory.h"
|
||||
#include "core/file_sys/filesystem.h"
|
||||
@@ -258,9 +259,7 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
u64 mode = rp.Pop<u64>();
|
||||
u32 size = rp.Pop<u32>();
|
||||
@@ -275,9 +274,7 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file {}", name);
|
||||
|
||||
@@ -289,9 +286,7 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called directory {}", name);
|
||||
|
||||
@@ -305,13 +300,11 @@ public:
|
||||
std::vector<u8> buffer;
|
||||
buffer.resize(ctx.BufferDescriptorX()[0].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[0].Address(), buffer.data(), buffer.size());
|
||||
auto end = std::find(buffer.begin(), buffer.end(), '\0');
|
||||
std::string src_name(buffer.begin(), end);
|
||||
std::string src_name = Common::StringFromBuffer(buffer);
|
||||
|
||||
buffer.resize(ctx.BufferDescriptorX()[1].Size());
|
||||
Memory::ReadBlock(ctx.BufferDescriptorX()[1].Address(), buffer.data(), buffer.size());
|
||||
end = std::find(buffer.begin(), buffer.end(), '\0');
|
||||
std::string dst_name(buffer.begin(), end);
|
||||
std::string dst_name = Common::StringFromBuffer(buffer);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name);
|
||||
|
||||
@@ -323,9 +316,7 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
auto mode = static_cast<FileSys::Mode>(rp.Pop<u32>());
|
||||
|
||||
@@ -349,9 +340,7 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
// TODO(Subv): Implement this filter.
|
||||
u32 filter_flags = rp.Pop<u32>();
|
||||
@@ -376,9 +365,7 @@ public:
|
||||
IPC::RequestParser rp{ctx};
|
||||
|
||||
auto file_buffer = ctx.ReadBuffer();
|
||||
auto end = std::find(file_buffer.begin(), file_buffer.end(), '\0');
|
||||
|
||||
std::string name(file_buffer.begin(), end);
|
||||
std::string name = Common::StringFromBuffer(file_buffer);
|
||||
|
||||
NGLOG_DEBUG(Service_FS, "called file {}", name);
|
||||
|
||||
|
||||
@@ -84,6 +84,10 @@ private:
|
||||
|
||||
for (size_t controller = 0; controller < mem.controllers.size(); controller++) {
|
||||
for (int index = 0; index < HID_NUM_LAYOUTS; index++) {
|
||||
// TODO(DarkLordZach): Is this layout/controller config actually invalid?
|
||||
if (controller == Controller_Handheld && index == Layout_Single)
|
||||
continue;
|
||||
|
||||
ControllerLayout& layout = mem.controllers[controller].layouts[index];
|
||||
layout.header.num_entries = HID_NUM_ENTRIES;
|
||||
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
|
||||
@@ -94,7 +98,6 @@ private:
|
||||
layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES;
|
||||
|
||||
ControllerInputEntry& entry = layout.entries[layout.header.latest_entry];
|
||||
entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
|
||||
entry.timestamp++;
|
||||
// TODO(shinyquagsire23): Is this always identical to timestamp?
|
||||
entry.timestamp_2++;
|
||||
@@ -103,6 +106,8 @@ private:
|
||||
if (controller != Controller_Handheld)
|
||||
continue;
|
||||
|
||||
entry.connection_state = ConnectionState_Connected | ConnectionState_Wired;
|
||||
|
||||
// TODO(shinyquagsire23): Set up some LUTs for each layout mapping in the future?
|
||||
// For now everything is just the default handheld layout, but split Joy-Con will
|
||||
// rotate the face buttons and directions for certain layouts.
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Service::HID {
|
||||
// Begin enums and output structs
|
||||
|
||||
constexpr u32 HID_NUM_ENTRIES = 17;
|
||||
constexpr u32 HID_NUM_LAYOUTS = 2;
|
||||
constexpr u32 HID_NUM_LAYOUTS = 7;
|
||||
constexpr s32 HID_JOYSTICK_MAX = 0x8000;
|
||||
constexpr s32 HID_JOYSTICK_MIN = -0x8000;
|
||||
|
||||
|
||||
@@ -121,8 +121,9 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
|
||||
}
|
||||
|
||||
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
|
||||
if (input.size() < sizeof(IoctlSubmitGpfifo))
|
||||
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
IoctlSubmitGpfifo params{};
|
||||
std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo));
|
||||
NGLOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
|
||||
|
||||
@@ -12,9 +12,6 @@
|
||||
namespace Service::Set {
|
||||
|
||||
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
u32 id = rp.Pop<u32>();
|
||||
|
||||
static constexpr std::array<LanguageCode, 17> available_language_codes = {{
|
||||
LanguageCode::JA,
|
||||
LanguageCode::EN_US,
|
||||
@@ -50,7 +47,7 @@ SET::SET() : ServiceFramework("set") {
|
||||
{2, nullptr, "MakeLanguageCode"},
|
||||
{3, nullptr, "GetAvailableLanguageCodeCount"},
|
||||
{4, nullptr, "GetRegionCode"},
|
||||
{5, nullptr, "GetAvailableLanguageCodes2"},
|
||||
{5, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes2"},
|
||||
{6, nullptr, "GetAvailableLanguageCodeCount2"},
|
||||
{7, nullptr, "GetKeyCodeMap"},
|
||||
{8, nullptr, "GetQuestFlag"},
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -328,8 +328,9 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
|
||||
|
||||
Texture::FullTextureInfo tex_info{};
|
||||
// TODO(Subv): Use the shader to determine which textures are actually accessed.
|
||||
tex_info.index = (current_texture - tex_info_buffer.address - TextureInfoOffset) /
|
||||
sizeof(Texture::TextureHandle);
|
||||
tex_info.index =
|
||||
static_cast<u32>(current_texture - tex_info_buffer.address - TextureInfoOffset) /
|
||||
sizeof(Texture::TextureHandle);
|
||||
|
||||
// Load the TIC data.
|
||||
if (tex_handle.tic_id != 0) {
|
||||
|
||||
@@ -318,6 +318,7 @@ public:
|
||||
Equation equation_a;
|
||||
Factor factor_source_a;
|
||||
Factor factor_dest_a;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
};
|
||||
|
||||
union {
|
||||
@@ -432,7 +433,27 @@ public:
|
||||
};
|
||||
} rt_control;
|
||||
|
||||
INSERT_PADDING_WORDS(0xCF);
|
||||
INSERT_PADDING_WORDS(0x31);
|
||||
|
||||
u32 independent_blend_enable;
|
||||
|
||||
INSERT_PADDING_WORDS(0x15);
|
||||
|
||||
struct {
|
||||
u32 separate_alpha;
|
||||
Blend::Equation equation_rgb;
|
||||
Blend::Factor factor_source_rgb;
|
||||
Blend::Factor factor_dest_rgb;
|
||||
Blend::Equation equation_a;
|
||||
Blend::Factor factor_source_a;
|
||||
INSERT_PADDING_WORDS(1);
|
||||
Blend::Factor factor_dest_a;
|
||||
|
||||
u32 enable_common;
|
||||
u32 enable[NumRenderTargets];
|
||||
} blend;
|
||||
|
||||
INSERT_PADDING_WORDS(0x77);
|
||||
|
||||
struct {
|
||||
u32 tsc_address_high;
|
||||
@@ -557,9 +578,7 @@ public:
|
||||
|
||||
} vertex_array[NumVertexArrays];
|
||||
|
||||
Blend blend;
|
||||
|
||||
INSERT_PADDING_WORDS(0x39);
|
||||
Blend independent_blend[NumRenderTargets];
|
||||
|
||||
struct {
|
||||
u32 limit_high;
|
||||
@@ -722,6 +741,8 @@ ASSERT_REG_POSITION(vertex_buffer, 0x35D);
|
||||
ASSERT_REG_POSITION(zeta, 0x3F8);
|
||||
ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
|
||||
ASSERT_REG_POSITION(rt_control, 0x487);
|
||||
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
|
||||
ASSERT_REG_POSITION(blend, 0x4CF);
|
||||
ASSERT_REG_POSITION(tsc, 0x557);
|
||||
ASSERT_REG_POSITION(tic, 0x55D);
|
||||
ASSERT_REG_POSITION(code_address, 0x582);
|
||||
@@ -729,7 +750,7 @@ ASSERT_REG_POSITION(draw, 0x585);
|
||||
ASSERT_REG_POSITION(index_array, 0x5F2);
|
||||
ASSERT_REG_POSITION(query, 0x6C0);
|
||||
ASSERT_REG_POSITION(vertex_array[0], 0x700);
|
||||
ASSERT_REG_POSITION(blend, 0x780);
|
||||
ASSERT_REG_POSITION(independent_blend, 0x780);
|
||||
ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
|
||||
ASSERT_REG_POSITION(shader_config[0], 0x800);
|
||||
ASSERT_REG_POSITION(const_buffer, 0x8E0);
|
||||
|
||||
69
src/video_core/engines/maxwell_dma.cpp
Normal file
69
src/video_core/engines/maxwell_dma.cpp
Normal 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
|
||||
155
src/video_core/engines/maxwell_dma.h
Normal file
155
src/video_core/engines/maxwell_dma.h
Normal 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
|
||||
@@ -216,12 +216,12 @@ union Instruction {
|
||||
|
||||
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 {
|
||||
@@ -229,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{};
|
||||
@@ -246,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;
|
||||
}
|
||||
@@ -259,11 +267,20 @@ union Instruction {
|
||||
}
|
||||
} alu;
|
||||
|
||||
union {
|
||||
BitField<48, 1, u64> is_signed;
|
||||
} shift;
|
||||
|
||||
union {
|
||||
BitField<39, 5, u64> shift_amount;
|
||||
BitField<48, 1, u64> negate_b;
|
||||
BitField<49, 1, u64> negate_a;
|
||||
} iscadd;
|
||||
} alu_integer;
|
||||
|
||||
union {
|
||||
BitField<54, 1, u64> saturate;
|
||||
BitField<56, 1, u64> negate_a;
|
||||
} iadd32i;
|
||||
|
||||
union {
|
||||
BitField<20, 8, u64> shift_position;
|
||||
@@ -325,13 +342,22 @@ union Instruction {
|
||||
} fset;
|
||||
|
||||
union {
|
||||
BitField<10, 2, Register::Size> size;
|
||||
BitField<39, 3, u64> pred39;
|
||||
BitField<42, 1, u64> neg_pred;
|
||||
BitField<44, 1, u64> bf;
|
||||
BitField<45, 2, PredOperation> op;
|
||||
BitField<48, 1, u64> is_signed;
|
||||
BitField<49, 3, PredCondition> cond;
|
||||
} iset;
|
||||
|
||||
union {
|
||||
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;
|
||||
BitField<45, 1, u64> negate_a;
|
||||
BitField<49, 1, u64> abs_a;
|
||||
BitField<50, 1, u64> saturate_a;
|
||||
|
||||
union {
|
||||
BitField<39, 2, F2iRoundingOp> rounding;
|
||||
@@ -346,7 +372,7 @@ union Instruction {
|
||||
BitField<31, 4, u64> component_mask;
|
||||
|
||||
bool IsComponentEnabled(size_t component) const {
|
||||
return ((1 << component) & component_mask) != 0;
|
||||
return ((1ull << component) & component_mask) != 0;
|
||||
}
|
||||
} tex;
|
||||
|
||||
@@ -365,7 +391,7 @@ union Instruction {
|
||||
|
||||
ASSERT(component_mask_selector < mask.size());
|
||||
|
||||
return ((1 << component) & mask[component_mask_selector]) != 0;
|
||||
return ((1ull << component) & mask[component_mask_selector]) != 0;
|
||||
}
|
||||
} texs;
|
||||
|
||||
@@ -410,6 +436,7 @@ class OpCode {
|
||||
public:
|
||||
enum class Id {
|
||||
KIL,
|
||||
SSY,
|
||||
BFE_C,
|
||||
BFE_R,
|
||||
BFE_IMM,
|
||||
@@ -434,6 +461,10 @@ public:
|
||||
FMUL_R,
|
||||
FMUL_IMM,
|
||||
FMUL32_IMM,
|
||||
IADD_C,
|
||||
IADD_R,
|
||||
IADD_IMM,
|
||||
IADD32I,
|
||||
ISCADD_C, // Scale and Add
|
||||
ISCADD_R,
|
||||
ISCADD_IMM,
|
||||
@@ -453,6 +484,9 @@ public:
|
||||
I2I_C,
|
||||
I2I_R,
|
||||
I2I_IMM,
|
||||
LOP_C,
|
||||
LOP_R,
|
||||
LOP_IMM,
|
||||
LOP32I,
|
||||
MOV_C,
|
||||
MOV_R,
|
||||
@@ -479,6 +513,9 @@ public:
|
||||
ISETP_C,
|
||||
ISETP_IMM,
|
||||
ISETP_R,
|
||||
ISET_R,
|
||||
ISET_C,
|
||||
ISET_IMM,
|
||||
PSETP,
|
||||
XMAD_IMM,
|
||||
XMAD_CR,
|
||||
@@ -489,15 +526,17 @@ public:
|
||||
enum class Type {
|
||||
Trivial,
|
||||
Arithmetic,
|
||||
ArithmeticImmediate,
|
||||
ArithmeticInteger,
|
||||
ArithmeticIntegerImmediate,
|
||||
Bfe,
|
||||
Logic,
|
||||
Shift,
|
||||
ScaledAdd,
|
||||
Ffma,
|
||||
Flow,
|
||||
Memory,
|
||||
FloatSet,
|
||||
FloatSetPredicate,
|
||||
IntegerSet,
|
||||
IntegerSetPredicate,
|
||||
PredicateSetPredicate,
|
||||
Conversion,
|
||||
@@ -596,6 +635,7 @@ private:
|
||||
std::vector<Matcher> table = {
|
||||
#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
|
||||
INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
|
||||
INST("111000101001----", Id::SSY, Type::Flow, "SSY"),
|
||||
INST("111000100100----", Id::BRA, Type::Flow, "BRA"),
|
||||
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
|
||||
INST("1110111110010---", Id::LD_C, Type::Memory, "LD_C"),
|
||||
@@ -616,10 +656,14 @@ private:
|
||||
INST("0100110001101---", Id::FMUL_C, Type::Arithmetic, "FMUL_C"),
|
||||
INST("0101110001101---", Id::FMUL_R, Type::Arithmetic, "FMUL_R"),
|
||||
INST("0011100-01101---", Id::FMUL_IMM, Type::Arithmetic, "FMUL_IMM"),
|
||||
INST("00011110--------", Id::FMUL32_IMM, Type::Arithmetic, "FMUL32_IMM"),
|
||||
INST("0100110000011---", Id::ISCADD_C, Type::ScaledAdd, "ISCADD_C"),
|
||||
INST("0101110000011---", Id::ISCADD_R, Type::ScaledAdd, "ISCADD_R"),
|
||||
INST("0011100-00011---", Id::ISCADD_IMM, Type::ScaledAdd, "ISCADD_IMM"),
|
||||
INST("00011110--------", Id::FMUL32_IMM, Type::ArithmeticImmediate, "FMUL32_IMM"),
|
||||
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"),
|
||||
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
|
||||
INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
|
||||
INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
|
||||
@@ -633,7 +677,7 @@ private:
|
||||
INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
|
||||
INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
|
||||
INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
|
||||
INST("000000010000----", Id::MOV32_IMM, Type::Arithmetic, "MOV32_IMM"),
|
||||
INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
|
||||
INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
|
||||
INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),
|
||||
INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"),
|
||||
@@ -643,7 +687,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"),
|
||||
@@ -665,6 +712,9 @@ private:
|
||||
INST("010010110110----", Id::ISETP_C, Type::IntegerSetPredicate, "ISETP_C"),
|
||||
INST("010110110110----", Id::ISETP_R, Type::IntegerSetPredicate, "ISETP_R"),
|
||||
INST("0011011-0110----", Id::ISETP_IMM, Type::IntegerSetPredicate, "ISETP_IMM"),
|
||||
INST("010110110101----", Id::ISET_R, Type::IntegerSet, "ISET_R"),
|
||||
INST("010010110101----", Id::ISET_C, Type::IntegerSet, "ISET_C"),
|
||||
INST("0011011-0101----", Id::ISET_IMM, Type::IntegerSet, "ISET_IMM"),
|
||||
INST("0101000010010---", Id::PSETP, Type::PredicateSetPredicate, "PSETP"),
|
||||
INST("0011011-00------", Id::XMAD_IMM, Type::Arithmetic, "XMAD_IMM"),
|
||||
INST("0100111---------", Id::XMAD_CR, Type::Arithmetic, "XMAD_CR"),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -218,6 +218,9 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) {
|
||||
ubo.SetFromRegs(gpu.state.shader_stages[stage]);
|
||||
std::memcpy(buffer_ptr, &ubo, sizeof(ubo));
|
||||
|
||||
// Flush the buffer so that the GPU can see the data we just wrote.
|
||||
glFlushMappedBufferRange(GL_ARRAY_BUFFER, buffer_offset, sizeof(ubo));
|
||||
|
||||
// Upload uniform data as one UBO per stage
|
||||
const GLintptr ubo_offset = buffer_offset;
|
||||
copy_buffer(uniform_buffers[stage].handle, ubo_offset,
|
||||
@@ -346,6 +349,9 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
// Sync the viewport
|
||||
SyncViewport(surfaces_rect, res_scale);
|
||||
|
||||
// Sync the blend state registers
|
||||
SyncBlendState();
|
||||
|
||||
// TODO(bunnei): Sync framebuffer_scale uniform here
|
||||
// TODO(bunnei): Sync scissorbox uniform(s) here
|
||||
|
||||
@@ -452,32 +458,7 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
switch (method) {
|
||||
case MAXWELL3D_REG_INDEX(blend.separate_alpha):
|
||||
ASSERT_MSG(false, "unimplemented");
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.equation_rgb):
|
||||
state.blend.rgb_equation = MaxwellToGL::BlendEquation(regs.blend.equation_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_source_rgb):
|
||||
state.blend.src_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_dest_rgb):
|
||||
state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_rgb);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.equation_a):
|
||||
state.blend.a_equation = MaxwellToGL::BlendEquation(regs.blend.equation_a);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_source_a):
|
||||
state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_source_a);
|
||||
break;
|
||||
case MAXWELL3D_REG_INDEX(blend.factor_dest_a):
|
||||
state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.blend.factor_dest_a);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 method) {}
|
||||
|
||||
void RasterizerOpenGL::FlushAll() {
|
||||
MICROPROFILE_SCOPE(OpenGL_CacheManagement);
|
||||
@@ -652,7 +633,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
|
||||
|
||||
state.Apply();
|
||||
|
||||
return current_bindpoint + entries.size();
|
||||
return current_bindpoint + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit,
|
||||
@@ -698,7 +679,7 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program,
|
||||
|
||||
state.Apply();
|
||||
|
||||
return current_unit + entries.size();
|
||||
return current_unit + static_cast<u32>(entries.size());
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface,
|
||||
@@ -757,14 +738,21 @@ void RasterizerOpenGL::SyncDepthOffset() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SyncBlendEnabled() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
void RasterizerOpenGL::SyncBlendState() {
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
void RasterizerOpenGL::SyncBlendFuncs() {
|
||||
UNREACHABLE();
|
||||
}
|
||||
// TODO(Subv): Support more than just render target 0.
|
||||
state.blend.enabled = regs.blend.enable[0] != 0;
|
||||
|
||||
void RasterizerOpenGL::SyncBlendColor() {
|
||||
UNREACHABLE();
|
||||
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);
|
||||
state.blend.dst_rgb_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_rgb);
|
||||
state.blend.a_equation = MaxwellToGL::BlendEquation(regs.independent_blend[0].equation_a);
|
||||
state.blend.src_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_source_a);
|
||||
state.blend.dst_a_func = MaxwellToGL::BlendFunc(regs.independent_blend[0].factor_dest_a);
|
||||
}
|
||||
|
||||
@@ -121,14 +121,8 @@ private:
|
||||
/// Syncs the depth offset to match the guest state
|
||||
void SyncDepthOffset();
|
||||
|
||||
/// Syncs the blend enabled status to match the guest state
|
||||
void SyncBlendEnabled();
|
||||
|
||||
/// Syncs the blend functions to match the guest state
|
||||
void SyncBlendFuncs();
|
||||
|
||||
/// Syncs the blend color to match the guest state
|
||||
void SyncBlendColor();
|
||||
/// Syncs the blend state to match the guest state
|
||||
void SyncBlendState();
|
||||
|
||||
bool has_ARB_buffer_storage;
|
||||
bool has_ARB_direct_state_access;
|
||||
|
||||
@@ -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
|
||||
@@ -549,7 +575,7 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
|
||||
glCompressedTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format,
|
||||
static_cast<GLsizei>(rect.GetWidth() * GetCompresssionFactor()),
|
||||
static_cast<GLsizei>(rect.GetHeight() * GetCompresssionFactor()), 0,
|
||||
size, &gl_buffer[buffer_offset]);
|
||||
static_cast<GLsizei>(size), &gl_buffer[buffer_offset]);
|
||||
} else {
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
|
||||
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
|
||||
@@ -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);
|
||||
@@ -1033,8 +1056,11 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
params.addr = config.tic.Address();
|
||||
params.is_tiled = config.tic.IsTiled();
|
||||
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format);
|
||||
params.width = config.tic.Width() / params.GetCompresssionFactor();
|
||||
params.height = config.tic.Height() / params.GetCompresssionFactor();
|
||||
|
||||
params.width = Common::AlignUp(config.tic.Width(), params.GetCompresssionFactor()) /
|
||||
params.GetCompresssionFactor();
|
||||
params.height = Common::AlignUp(config.tic.Height(), params.GetCompresssionFactor()) /
|
||||
params.GetCompresssionFactor();
|
||||
|
||||
// TODO(Subv): Different types per component are not supported.
|
||||
ASSERT(config.tic.r_type.Value() == config.tic.g_type.Value() &&
|
||||
@@ -1045,6 +1071,13 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu
|
||||
|
||||
if (config.tic.IsTiled()) {
|
||||
params.block_height = config.tic.BlockHeight();
|
||||
|
||||
// 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()));
|
||||
@@ -1052,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);
|
||||
}
|
||||
|
||||
@@ -1246,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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,13 +531,11 @@ 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) {
|
||||
std::string dest = GetRegister(reg, dest_elem);
|
||||
u64 dest_num_components, u64 value_num_components, u64 dest_elem) {
|
||||
std::string dest = GetRegister(reg, static_cast<u32>(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 + ';');
|
||||
}
|
||||
|
||||
@@ -538,7 +565,7 @@ private:
|
||||
// vertex shader, and what's the value of the fourth element when inside a Tess Eval
|
||||
// shader.
|
||||
ASSERT(stage == Maxwell3D::Regs::ShaderStage::Vertex);
|
||||
return "vec4(0, 0, gl_InstanceID, gl_VertexID)";
|
||||
return "vec4(0, 0, uintBitsToFloat(gl_InstanceID), uintBitsToFloat(gl_VertexID))";
|
||||
default:
|
||||
const u32 index{static_cast<u32>(attribute) -
|
||||
static_cast<u32>(Attribute::Index::Attribute_0)};
|
||||
@@ -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: {
|
||||
@@ -800,58 +859,49 @@ private:
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode::Id::MOV32_IMM: {
|
||||
// mov32i doesn't have abs or neg bits.
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FMUL_C:
|
||||
case OpCode::Id::FMUL_R:
|
||||
case OpCode::Id::FMUL_IMM: {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1, instr.alu.abs_d);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FMUL32_IMM: {
|
||||
// fmul32i doesn't have abs or neg bits.
|
||||
regs.SetRegisterToFloat(
|
||||
instr.gpr0, 0,
|
||||
regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, op_a + " * " + op_b, 1, 1,
|
||||
instr.alu.saturate_d);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FADD_C:
|
||||
case OpCode::Id::FADD_R:
|
||||
case OpCode::Id::FADD_IMM: {
|
||||
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: {
|
||||
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}",
|
||||
@@ -888,6 +938,21 @@ private:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::ArithmeticImmediate: {
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::MOV32_IMM: {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, GetImmediate32(instr), 1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::FMUL32_IMM: {
|
||||
regs.SetRegisterToFloat(
|
||||
instr.gpr0, 0,
|
||||
regs.GetRegisterAsFloat(instr.gpr8) + " * " + GetImmediate32(instr), 1, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Bfe: {
|
||||
ASSERT_MSG(!instr.bfe.negate_b, "Unimplemented");
|
||||
|
||||
@@ -913,49 +978,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);
|
||||
@@ -973,6 +995,19 @@ private:
|
||||
}
|
||||
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::SHR_C:
|
||||
case OpCode::Id::SHR_R:
|
||||
case OpCode::Id::SHR_IMM: {
|
||||
if (!instr.shift.is_signed) {
|
||||
// Logical shift right
|
||||
op_a = "uint(" + op_a + ')';
|
||||
}
|
||||
|
||||
// Cast to int is superfluous for arithmetic shift, it's only for a logical shift
|
||||
regs.SetRegisterToInteger(instr.gpr0, true, 0, "int(" + op_a + " >> " + op_b + ')',
|
||||
1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SHL_C:
|
||||
case OpCode::Id::SHL_R:
|
||||
case OpCode::Id::SHL_IMM:
|
||||
@@ -986,14 +1021,39 @@ private:
|
||||
break;
|
||||
}
|
||||
|
||||
case OpCode::Type::ScaledAdd: {
|
||||
case OpCode::Type::ArithmeticIntegerImmediate: {
|
||||
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8);
|
||||
std::string op_b = std::to_string(instr.alu.imm20_32.Value());
|
||||
|
||||
if (instr.iscadd.negate_a)
|
||||
op_a = '-' + op_a;
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::IADD32I:
|
||||
if (instr.iadd32i.negate_a)
|
||||
op_a = "-(" + op_a + ')';
|
||||
|
||||
std::string op_b = instr.iscadd.negate_b ? "-" : "";
|
||||
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);
|
||||
std::string op_b;
|
||||
if (instr.is_b_imm) {
|
||||
op_b += '(' + std::to_string(instr.alu.GetSignedImm20_20()) + ')';
|
||||
} else {
|
||||
@@ -1005,10 +1065,57 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
std::string shift = std::to_string(instr.iscadd.shift_amount.Value());
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::IADD_C:
|
||||
case OpCode::Id::IADD_R:
|
||||
case OpCode::Id::IADD_IMM: {
|
||||
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());
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
regs.SetRegisterToInteger(instr.gpr0, true, 0,
|
||||
"((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::Ffma: {
|
||||
@@ -1045,33 +1152,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.conversion.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 + ')';
|
||||
@@ -1081,6 +1188,8 @@ private:
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::F2F_R: {
|
||||
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) {
|
||||
@@ -1106,10 +1215,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) {
|
||||
@@ -1142,7 +1252,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: {
|
||||
@@ -1198,8 +1308,8 @@ private:
|
||||
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
|
||||
const std::string sampler = GetSampler(instr.sampler);
|
||||
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
|
||||
// Add an extra scope and declare the texture coords inside to prevent overwriting
|
||||
// them in case they are used as outputs of the texs instruction.
|
||||
// Add an extra scope and declare the texture coords inside to prevent
|
||||
// overwriting them in case they are used as outputs of the texs instruction.
|
||||
shader.AddLine("{");
|
||||
++shader.scope;
|
||||
shader.AddLine(coord);
|
||||
@@ -1230,8 +1340,8 @@ private:
|
||||
shader.AddLine(coord);
|
||||
const std::string texture = "texture(" + sampler + ", coords)";
|
||||
|
||||
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA goes
|
||||
// into gpr28+0 and gpr28+1
|
||||
// TEXS has two destination registers. RG goes into gpr0+0 and gpr0+1, and BA
|
||||
// goes into gpr28+0 and gpr28+1
|
||||
size_t offset{};
|
||||
|
||||
for (const auto& dest : {instr.gpr0.Value(), instr.gpr28.Value()}) {
|
||||
@@ -1380,8 +1490,8 @@ private:
|
||||
op_b = "abs(" + op_b + ')';
|
||||
}
|
||||
|
||||
// The fset instruction sets a register to 1.0 if the condition is true, and to 0
|
||||
// otherwise.
|
||||
// The fset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
|
||||
// condition is true, and to 0 otherwise.
|
||||
std::string second_pred =
|
||||
GetPredicateCondition(instr.fset.pred39, instr.fset.neg_pred != 0);
|
||||
|
||||
@@ -1399,6 +1509,41 @@ private:
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpCode::Type::IntegerSet: {
|
||||
std::string op_a = regs.GetRegisterAsInteger(instr.gpr8, 0, instr.iset.is_signed);
|
||||
|
||||
std::string op_b;
|
||||
|
||||
if (instr.is_b_imm) {
|
||||
op_b = std::to_string(instr.alu.GetSignedImm20_20());
|
||||
} else {
|
||||
if (instr.is_b_gpr) {
|
||||
op_b = regs.GetRegisterAsInteger(instr.gpr20, 0, instr.iset.is_signed);
|
||||
} else {
|
||||
op_b = regs.GetUniform(instr.cbuf34.index, instr.cbuf34.offset,
|
||||
GLSLRegister::Type::Integer);
|
||||
}
|
||||
}
|
||||
|
||||
// The iset instruction sets a register to 1.0 or -1 (depending on the bf bit) if the
|
||||
// condition is true, and to 0 otherwise.
|
||||
std::string second_pred =
|
||||
GetPredicateCondition(instr.iset.pred39, instr.iset.neg_pred != 0);
|
||||
|
||||
std::string comparator = GetPredicateComparison(instr.iset.cond);
|
||||
std::string combiner = GetPredicateCombiner(instr.iset.op);
|
||||
|
||||
std::string predicate = "(((" + op_a + ") " + comparator + " (" + op_b + ")) " +
|
||||
combiner + " (" + second_pred + "))";
|
||||
|
||||
if (instr.iset.bf) {
|
||||
regs.SetRegisterToFloat(instr.gpr0, 0, predicate + " ? 1.0 : 0.0", 1, 1);
|
||||
} else {
|
||||
regs.SetRegisterToInteger(instr.gpr0, false, 0, predicate + " ? 0xFFFFFFFF : 0", 1,
|
||||
1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
switch (opcode->GetId()) {
|
||||
case OpCode::Id::EXIT: {
|
||||
@@ -1412,8 +1557,8 @@ private:
|
||||
|
||||
shader.AddLine("return true;");
|
||||
if (instr.pred.pred_index == static_cast<u64>(Pred::UnusedIndex)) {
|
||||
// If this is an unconditional exit then just end processing here, otherwise we
|
||||
// have to account for the possibility of the condition not being met, so
|
||||
// If this is an unconditional exit then just end processing here, otherwise
|
||||
// we have to account for the possibility of the condition not being met, so
|
||||
// continue processing the next instruction.
|
||||
offset = PROGRAM_END - 1;
|
||||
}
|
||||
@@ -1435,6 +1580,11 @@ private:
|
||||
regs.SetRegisterToInputAttibute(instr.gpr0, attribute.element, attribute.index);
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SSY: {
|
||||
// The SSY opcode tells the GPU where to re-converge divergent execution paths, we
|
||||
// can ignore this when generating GLSL code.
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
NGLOG_CRITICAL(HW_GPU, "Unhandled instruction: {}", opcode->GetName());
|
||||
UNREACHABLE();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -38,8 +38,8 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
|
||||
const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs;
|
||||
|
||||
// TODO(bunnei): Support more than one viewport
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0 : 1.0;
|
||||
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0 : 1.0;
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
|
||||
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
|
||||
}
|
||||
|
||||
} // namespace GLShader
|
||||
|
||||
@@ -196,13 +196,13 @@ void OpenGLState::Apply() const {
|
||||
}
|
||||
|
||||
// Textures
|
||||
for (size_t i = 0; i < std::size(texture_units); ++i) {
|
||||
for (int i = 0; i < std::size(texture_units); ++i) {
|
||||
if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
|
||||
glActiveTexture(TextureUnits::MaxwellTexture(i).Enum());
|
||||
glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
|
||||
}
|
||||
if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {
|
||||
glBindSampler(i, texture_units[i].sampler);
|
||||
glBindSampler(static_cast<GLuint>(i), texture_units[i].sampler);
|
||||
}
|
||||
// Update the texture swizzle
|
||||
if (texture_units[i].swizzle.r != cur_state.texture_units[i].swizzle.r ||
|
||||
|
||||
1646
src/video_core/textures/astc.cpp
Normal file
1646
src/video_core/textures/astc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
15
src/video_core/textures/astc.h
Normal file
15
src/video_core/textures/astc.h
Normal 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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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>
|
||||
@@ -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)),
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user