Compare commits

...
Author SHA1 Message Date
Zach Hilman 7979ccd956 Merge pull request #2883 from ogniK5377/log-game
Log the current title id and game name which is booting
2019-09-21 21:34:19 -04:00
MysticExileandMorph 2b514275ad Update README.md (#2871)
* Update README.md

* Adress feedback

* Add link to our compatability page

* Address feedback

Co-Authored-By: Morph <[email protected]>

* Fix spelling

Co-Authored-By: Morph <[email protected]>

* Small fix

* Removed the mention of a specific game

* fix
2019-09-21 17:37:21 -04:00
Rodrigo Locatti 9286976948 Merge pull request #2878 from FernandoS27/icmp
shader_ir: Implement ICMP
2019-09-21 18:06:07 -03:00
Fernando Sahmkow 527b841c15 Shader_IR: ICMP corrections and fixes 2019-09-21 14:28:03 -04:00
David 97b8c9d2c3 Merge pull request #2885 from Hexagon12/port-4944
Port citra-emu/citra#4944: "Added Host CPU and OS to log"
2019-09-21 23:46:58 +10:00
pbarilla 8fd266a7c4 Added Host CPU and OS to log 2019-09-21 16:11:46 +03:00
David 183c445c30 Merge pull request #2806 from FearlessTobi/port-4882
Port citra-emu/citra#4882: "Add frametime logging for tracking performance over time"
2019-09-21 20:09:55 +10:00
David c7c8ffbc13 Merge pull request #2872 from FernandoS27/mem-gpu-opt
Core/Memory: Only FlushAndInvalidate GPU if the page is marked as RasterizerCachedMemory
2019-09-21 20:07:25 +10:00
David 25383b9ff2 Merge pull request #2576 from DarkLordZach/nsp-fix-1
nsp: Fix various errors with loading and processing of extracted NSPs
2019-09-21 20:05:24 +10:00
David c41365a56f Merge pull request #2866 from Morph1984/checkbox_fix
configure_input: Uncheck "Joycons Docked" when "Use Docked Mode" is checked
2019-09-21 19:58:22 +10:00
David 9ad42fb0cf Merge pull request #2868 from ReinUsesLisp/fix-mipmaps
maxwell_to_gl: Fix mipmap filtering
2019-09-21 19:57:09 +10:00
David Marcec b4db662053 Swapped TID and Game name to make it easier to parse 2019-09-21 17:43:18 +10:00
David Marcec 934ce530f6 Log the current title id and game name which is booting
Spit out a LOG_INFO of the current game name and it's title id in the log. This helps to read log files and figure out which games have which issues
2019-09-21 17:34:46 +10:00
David b9fd1e2bed Merge pull request #2879 from ogniK5377/trace-submitgpfifo
Mark KickOffPb & SubmitGPFIFO as trace
2019-09-21 16:22:39 +10:00
David 41836f3a17 Merge pull request #2880 from ogniK5377/drawarrays-trace
Mark DrawArrays as LOG_TRACE
2019-09-21 16:11:54 +10:00
David Marcec 01a4afee42 Mark DrawArrays as LOG_TRACE
There's no reason to clog logs with DrawArray.
2019-09-21 15:43:58 +10:00
David Marcec c2f966dbc1 Mark KickOffPb & SubmitGPFIFO as trace
These functions are not stubbed and are called fairly often. Due to the nature of how often they're called, we should keep them marked as LOG_TRACE instead of LOG_DEBUG or LOG_WARNING
2019-09-21 15:41:11 +10:00
bunnei bbe82d62b0 Merge pull request #2846 from ReinUsesLisp/fixup-viewport-index
gl_shader_decompiler: Avoid writing output attribute when unimplemented
2019-09-20 17:11:20 -04:00
bunnei 88d857499b Merge pull request #2855 from ReinUsesLisp/shfl
shader_ir/warp: Implement SHFL for Nvidia devices
2019-09-20 17:10:42 -04:00
Fernando Sahmkow 4b81d19a1a Shader_IR: Implement ICMP. 2019-09-19 20:56:29 -04:00
Fernando Sahmkow 50259d7bdc Core/Memory: Only FlushAndInvalidate GPU if the page is marked as RasterizerCachedMemory
This commit avoids Invalidating and Flushing the GPU if the page is not
marked as a RasterizerCache Page.
2019-09-18 22:47:22 -04:00
bunnei b31880dc5e Merge pull request #2784 from ReinUsesLisp/smem
shader_ir: Implement shared memory
2019-09-18 16:26:05 -04:00
ReinUsesLisp 0526bf1895 shader_ir/warp: Implement SHFL 2019-09-17 17:44:07 -03:00
ReinUsesLisp 2dd6411753 maxwell_to_gl: Fix mipmap filtering
OpenGL texture filters follow GL_<texture_filter>_MIPMAP_<mipmap_filter>
but we were using them in the opposite way.
2019-09-17 03:32:24 -03:00
Morph 8d778c90e2 When docked mode is checked, uncheck "joycons docked" 2019-09-16 23:26:32 -04:00
Fernando Sahmkow 393cc3ef2f Merge pull request #2851 from ReinUsesLisp/srgb
renderer_opengl: Fix sRGB blits
2019-09-15 10:38:10 -04:00
Fernando Sahmkow b8b1747704 Merge pull request #2824 from ReinUsesLisp/mme
Revert "Revert #2466" and stub FirmwareCall 4
2019-09-15 06:17:04 -04:00
Rodrigo Locatti 193bfefce4 maxwell_3d: Update firmware 4 call stub commentary 2019-09-14 22:51:18 -03:00
ReinUsesLisp 6b997c8f7f renderer_opengl: Fix rebase mistake 2019-09-11 00:09:37 -03:00
Fernando Sahmkow e60d281a01 gl_rasterizer: Correct sRGB Fix regression 2019-09-10 19:31:42 -03:00
ReinUsesLisp 78574746bd renderer_opengl: Fix sRGB blits
Removes the sRGB hack of tracking if a frame used an sRGB rendertarget
to apply at least once to blit the final texture as sRGB. Instead of
doing this apply sRGB if the presented image has sRGB.

Also enable sRGB by default on Maxwell3D registers as some games seem to
assume this.
2019-09-10 19:31:42 -03:00
FearlessTobi d36a7a43c5 Address review comments 2019-09-10 12:57:45 +02:00
fearlessTobiandjroweboy 684b616f0d Add frametime logging for tracking performance over time
Co-Authored-By: jroweboy <[email protected]>
2019-09-10 12:44:19 +02:00
ReinUsesLisp 17a9b0178d gl_shader_decompiler: Avoid writing output attribute when unimplemented 2019-09-06 15:02:12 -03:00
ReinUsesLisp 0f7b813d65 gl_shader_decompiler: Implement shared memory 2019-09-05 01:40:24 -03:00
ReinUsesLisp 4de04eba39 shader_ir: Implement LD_S
Loads from shared memory.
2019-09-05 01:38:37 -03:00
ReinUsesLisp f17415d431 shader_ir: Implement ST_S
This instruction writes to a memory buffer shared with threads within
the same work group. It is known as "shared" memory in GLSL.
2019-09-05 01:38:37 -03:00
ReinUsesLisp 5f309b88db Revert "Revert #2466" and stub FirmwareCall 4 2019-09-04 01:55:45 -03:00
Zach Hilman b957a4862f nsp: Correct status codes for extracted NSPs
Avoids all extracted NSPs being marked as error file type because they don't have program NCAs.
2019-06-10 00:21:41 -04:00
Zach Hilman 1c75945dc4 nsp: Use title ID from NPDM metadata for extracted type NSPs
Avoids 0 being used as title ID for all extracted NSPs.
2019-06-10 00:19:23 -04:00
33 changed files with 511 additions and 112 deletions

No files matched your search

+4 -2
View File
@@ -6,9 +6,11 @@ yuzu emulator
yuzu is an experimental open-source emulator for the Nintendo Switch from the creators of [Citra](https://citra-emu.org/).
It is written in C++ with portability in mind, with builds actively maintained for Windows, Linux and macOS. The emulator is currently only useful for homebrew development and research purposes.
It is written in C++ with portability in mind, with builds actively maintained for Windows and Linux. The emulator is capable of running several commercial games.
yuzu only emulates a subset of Switch hardware and therefore is generally only useful for running/debugging homebrew applications. yuzu can boot some games, to varying degrees of success.
yuzu only emulates a subset of Switch hardware and therefore most commercial games **do not** run at full speed or are not fully functional.
Do you want to check which games are compatible and which ones are not? Please visit our [Compatibility page](https://yuzu-emu.org/game/)!
yuzu is licensed under the GPLv2 (or any later version). Refer to the license.txt file included.
+17 -8
View File
@@ -160,10 +160,6 @@ struct System::Impl {
LOG_DEBUG(Core, "Initialized OK");
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats.BeginSystemFrame();
return ResultStatus::Success;
}
@@ -206,6 +202,16 @@ struct System::Impl {
main_process->Run(load_parameters->main_thread_priority,
load_parameters->main_thread_stack_size);
u64 title_id{0};
if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
static_cast<u32>(load_result));
}
perf_stats = std::make_unique<PerfStats>(title_id);
// Reset counters and set time origin to current frame
GetAndResetPerfStats();
perf_stats->BeginSystemFrame();
status = ResultStatus::Success;
return status;
}
@@ -219,6 +225,8 @@ struct System::Impl {
perf_results.game_fps);
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Frametime",
perf_results.frametime * 1000.0);
telemetry_session->AddField(Telemetry::FieldType::Performance, "Mean_Frametime_MS",
perf_stats->GetMeanFrametime());
is_powered_on = false;
@@ -229,6 +237,7 @@ struct System::Impl {
service_manager.reset();
cheat_engine.reset();
telemetry_session.reset();
perf_stats.reset();
gpu_core.reset();
// Close all CPU/threading state
@@ -286,7 +295,7 @@ struct System::Impl {
}
PerfStatsResults GetAndResetPerfStats() {
return perf_stats.GetAndResetStats(core_timing.GetGlobalTimeUs());
return perf_stats->GetAndResetStats(core_timing.GetGlobalTimeUs());
}
Timing::CoreTiming core_timing;
@@ -327,7 +336,7 @@ struct System::Impl {
ResultStatus status = ResultStatus::Success;
std::string status_details = "";
Core::PerfStats perf_stats;
std::unique_ptr<Core::PerfStats> perf_stats;
Core::FrameLimiter frame_limiter;
};
@@ -480,11 +489,11 @@ const Timing::CoreTiming& System::CoreTiming() const {
}
Core::PerfStats& System::GetPerfStats() {
return impl->perf_stats;
return *impl->perf_stats;
}
const Core::PerfStats& System::GetPerfStats() const {
return impl->perf_stats;
return *impl->perf_stats;
}
Core::FrameLimiter& System::FrameLimiter() {
+26
View File
@@ -14,6 +14,7 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
#include "core/file_sys/program_metadata.h"
#include "core/file_sys/submission_package.h"
#include "core/loader/loader.h"
@@ -78,6 +79,10 @@ Loader::ResultStatus NSP::GetStatus() const {
}
Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
return Loader::ResultStatus::Success;
}
const auto iter = program_status.find(title_id);
if (iter == program_status.end())
return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
@@ -85,12 +90,29 @@ Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
}
u64 NSP::GetFirstTitleID() const {
if (IsExtractedType()) {
return GetProgramTitleID();
}
if (program_status.empty())
return 0;
return program_status.begin()->first;
}
u64 NSP::GetProgramTitleID() const {
if (IsExtractedType()) {
if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
return 0;
}
ProgramMetadata meta;
if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
return meta.GetTitleID();
} else {
return 0;
}
}
const auto out = GetFirstTitleID();
if ((out & 0x800) == 0)
return out;
@@ -102,6 +124,10 @@ u64 NSP::GetProgramTitleID() const {
}
std::vector<u64> NSP::GetTitleIDs() const {
if (IsExtractedType()) {
return {GetProgramTitleID()};
}
std::vector<u64> out;
out.reserve(ncas.size());
for (const auto& kv : ncas)
@@ -146,8 +146,8 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
params.address, params.num_entries, params.flags.raw);
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
params.num_entries * sizeof(Tegra::CommandListHeader),
@@ -179,8 +179,8 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
params.address, params.num_entries, params.flags.raw);
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
Tegra::CommandList entries(params.num_entries);
Memory::ReadBlock(params.address, entries.data(),
+13 -14
View File
@@ -26,20 +26,18 @@ AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
if (nsp->GetStatus() != ResultStatus::Success)
return;
if (nsp->IsExtractedType())
return;
const auto control_nca =
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
return;
std::tie(nacp_file, icon_file) =
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
if (nsp->IsExtractedType()) {
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
} else {
const auto control_nca =
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
return;
std::tie(nacp_file, icon_file) =
FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
if (title_id == 0)
return;
@@ -56,11 +54,11 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
if (nsp.GetStatus() == ResultStatus::Success) {
// Extracted Type case
if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) {
FileSys::IsDirectoryExeFS(nsp.GetExeFS())) {
return FileType::NSP;
}
// Non-Ectracted Type case
// Non-Extracted Type case
if (!nsp.IsExtractedType() &&
nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
@@ -77,7 +75,7 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
if (title_id == 0) {
if (!nsp->IsExtractedType() && title_id == 0) {
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
}
@@ -91,7 +89,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
return {nsp_program_status, {}};
}
if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
if (!nsp->IsExtractedType() &&
nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
}
+7 -2
View File
@@ -43,8 +43,13 @@ static void MapPages(Common::PageTable& page_table, VAddr base, u64 size, u8* me
// During boot, current_page_table might not be set yet, in which case we need not flush
if (Core::System::GetInstance().IsPoweredOn()) {
Core::System::GetInstance().GPU().FlushAndInvalidateRegion(base << PAGE_BITS,
size * PAGE_SIZE);
auto& gpu = Core::System::GetInstance().GPU();
for (u64 i = 0; i < size; i++) {
const auto page = base + i;
if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
}
}
}
VAddr end = base + size;
+46 -1
View File
@@ -4,8 +4,14 @@
#include <algorithm>
#include <chrono>
#include <iterator>
#include <mutex>
#include <numeric>
#include <sstream>
#include <thread>
#include <fmt/chrono.h>
#include <fmt/format.h>
#include "common/file_util.h"
#include "common/math_util.h"
#include "core/perf_stats.h"
#include "core/settings.h"
@@ -15,8 +21,31 @@ using DoubleSecs = std::chrono::duration<double, std::chrono::seconds::period>;
using std::chrono::duration_cast;
using std::chrono::microseconds;
// Purposefully ignore the first five frames, as there's a significant amount of overhead in
// booting that we shouldn't account for
constexpr std::size_t IgnoreFrames = 5;
namespace Core {
PerfStats::PerfStats(u64 title_id) : title_id(title_id) {}
PerfStats::~PerfStats() {
if (!Settings::values.record_frame_times || title_id == 0) {
return;
}
const std::time_t t = std::time(nullptr);
std::ostringstream stream;
std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index,
std::ostream_iterator<double>(stream, "\n"));
const std::string& path = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
// %F Date format expanded is "%Y-%m-%d"
const std::string filename =
fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id);
FileUtil::IOFile file(filename, "w");
file.WriteString(stream.str());
}
void PerfStats::BeginSystemFrame() {
std::lock_guard lock{object_mutex};
@@ -27,7 +56,12 @@ void PerfStats::EndSystemFrame() {
std::lock_guard lock{object_mutex};
auto frame_end = Clock::now();
accumulated_frametime += frame_end - frame_begin;
const auto frame_time = frame_end - frame_begin;
if (current_index < perf_history.size()) {
perf_history[current_index++] =
std::chrono::duration<double, std::milli>(frame_time).count();
}
accumulated_frametime += frame_time;
system_frames += 1;
previous_frame_length = frame_end - previous_frame_end;
@@ -40,6 +74,17 @@ void PerfStats::EndGameFrame() {
game_frames += 1;
}
double PerfStats::GetMeanFrametime() {
std::lock_guard lock{object_mutex};
if (current_index <= IgnoreFrames) {
return 0;
}
const double sum = std::accumulate(perf_history.begin() + IgnoreFrames,
perf_history.begin() + current_index, 0);
return sum / (current_index - IgnoreFrames);
}
PerfStatsResults PerfStats::GetAndResetStats(microseconds current_system_time_us) {
std::lock_guard lock{object_mutex};
+20 -1
View File
@@ -4,7 +4,9 @@
#pragma once
#include <array>
#include <chrono>
#include <cstddef>
#include <mutex>
#include "common/common_types.h"
@@ -27,6 +29,10 @@ struct PerfStatsResults {
*/
class PerfStats {
public:
explicit PerfStats(u64 title_id);
~PerfStats();
using Clock = std::chrono::high_resolution_clock;
void BeginSystemFrame();
@@ -35,6 +41,11 @@ public:
PerfStatsResults GetAndResetStats(std::chrono::microseconds current_system_time_us);
/**
* Returns the Arthimetic Mean of all frametime values stored in the performance history.
*/
double GetMeanFrametime();
/**
* Gets the ratio between walltime and the emulated time of the previous system frame. This is
* useful for scaling inputs or outputs moving between the two time domains.
@@ -42,7 +53,15 @@ public:
double GetLastFrameTimeScale();
private:
std::mutex object_mutex;
std::mutex object_mutex{};
/// Title ID for the game that is running. 0 if there is no game running yet
u64 title_id{0};
/// Current index for writing to the perf_history array
std::size_t current_index{0};
/// Stores an hour of historical frametime data useful for processing and tracking performance
/// regressions with code changes.
std::array<double, 216000> perf_history = {};
/// Point when the cumulative counters were reset
Clock::time_point reset_point = Clock::now();
+1
View File
@@ -409,6 +409,7 @@ struct Values {
float volume;
// Debugging
bool record_frame_times;
bool use_gdbstub;
u16 gdbstub_port;
std::string program_args;
+16 -1
View File
@@ -89,6 +89,9 @@ void Maxwell3D::InitializeRegisterDefaults() {
// Commercial games seem to assume this value is enabled and nouveau sets this value manually.
regs.rt_separate_frag_data = 1;
// Some games (like Super Mario Odyssey) assume that SRGB is enabled.
regs.framebuffer_srgb = 1;
}
#define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name))
@@ -329,6 +332,10 @@ void Maxwell3D::CallMethod(const GPU::MethodCall& method_call) {
ProcessMacroBind(method_call.argument);
break;
}
case MAXWELL3D_REG_INDEX(firmware[4]): {
ProcessFirmwareCall4();
break;
}
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
@@ -419,6 +426,14 @@ void Maxwell3D::ProcessMacroBind(u32 data) {
macro_positions[regs.macros.entry++] = data;
}
void Maxwell3D::ProcessFirmwareCall4() {
LOG_WARNING(HW_GPU, "(STUBBED) called");
// Firmware call 4 is a blob that changes some registers depending on its parameters.
// These registers don't affect emulation and so are stubbed by setting 0xd00 to 1.
regs.reg_array[0xd00] = 1;
}
void Maxwell3D::ProcessQueryGet() {
const GPUVAddr sequence_address{regs.query.QueryAddress()};
// Since the sequence address is given as a GPU VAddr, we have to convert it to an application
@@ -526,7 +541,7 @@ void Maxwell3D::ProcessSyncPoint() {
}
void Maxwell3D::DrawArrays() {
LOG_DEBUG(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
LOG_TRACE(HW_GPU, "called, topology={}, count={}", static_cast<u32>(regs.draw.topology.Value()),
regs.vertex_buffer.count);
ASSERT_MSG(!(regs.index_array.count && regs.vertex_buffer.count), "Both indexed and direct?");
+7 -1
View File
@@ -1089,7 +1089,9 @@ public:
INSERT_PADDING_WORDS(14);
} shader_config[MaxShaderProgram];
INSERT_PADDING_WORDS(0x80);
INSERT_PADDING_WORDS(0x60);
u32 firmware[0x20];
struct {
u32 cb_size;
@@ -1319,6 +1321,9 @@ private:
/// Handles writes to the macro bind register.
void ProcessMacroBind(u32 data);
/// Handles firmware blob 4
void ProcessFirmwareCall4();
/// Handles a write to the CLEAR_BUFFERS register.
void ProcessClearBuffers();
@@ -1431,6 +1436,7 @@ ASSERT_REG_POSITION(vertex_array[0], 0x700);
ASSERT_REG_POSITION(independent_blend, 0x780);
ASSERT_REG_POSITION(vertex_array_limit[0], 0x7C0);
ASSERT_REG_POSITION(shader_config[0], 0x800);
ASSERT_REG_POSITION(firmware, 0x8C0);
ASSERT_REG_POSITION(const_buffer, 0x8E0);
ASSERT_REG_POSITION(cb_bind[0], 0x904);
ASSERT_REG_POSITION(tex_cb_index, 0x982);
+31
View File
@@ -566,6 +566,13 @@ enum class ImageAtomicOperation : u64 {
Exch = 8,
};
enum class ShuffleOperation : u64 {
Idx = 0, // shuffleNV
Up = 1, // shuffleUpNV
Down = 2, // shuffleDownNV
Bfly = 3, // shuffleXorNV
};
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -599,6 +606,15 @@ union Instruction {
BitField<42, 1, u64> negate_value;
} vote;
union {
BitField<30, 2, ShuffleOperation> operation;
BitField<48, 3, u64> pred48;
BitField<28, 1, u64> is_index_imm;
BitField<29, 1, u64> is_mask_imm;
BitField<20, 5, u64> index_imm;
BitField<34, 13, u64> mask_imm;
} shfl;
union {
BitField<8, 8, Register> gpr;
BitField<20, 24, s64> offset;
@@ -933,6 +949,11 @@ union Instruction {
BitField<49, 3, PredCondition> cond;
} isetp;
union {
BitField<48, 1, u64> is_signed;
BitField<49, 3, PredCondition> cond;
} icmp;
union {
BitField<0, 3, u64> pred0;
BitField<3, 3, u64> pred3;
@@ -1542,6 +1563,7 @@ public:
BRK,
DEPBAR,
VOTE,
SHFL,
BFE_C,
BFE_R,
BFE_IMM,
@@ -1628,6 +1650,10 @@ public:
SEL_C,
SEL_R,
SEL_IMM,
ICMP_RC,
ICMP_R,
ICMP_CR,
ICMP_IMM,
MUFU, // Multi-Function Operator
RRO_C, // Range Reduction Operator
RRO_R,
@@ -1833,6 +1859,7 @@ private:
INST("111000110000----", Id::EXIT, Type::Flow, "EXIT"),
INST("1111000011110---", Id::DEPBAR, Type::Synch, "DEPBAR"),
INST("0101000011011---", Id::VOTE, Type::Warp, "VOTE"),
INST("1110111100010---", Id::SHFL, Type::Warp, "SHFL"),
INST("1110111111011---", Id::LD_A, Type::Memory, "LD_A"),
INST("1110111101001---", Id::LD_S, Type::Memory, "LD_S"),
INST("1110111101000---", Id::LD_L, Type::Memory, "LD_L"),
@@ -1892,6 +1919,10 @@ private:
INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"),
INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"),
INST("0011100-10100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"),
INST("010100110100----", Id::ICMP_RC, Type::ArithmeticInteger, "ICMP_RC"),
INST("010110110100----", Id::ICMP_R, Type::ArithmeticInteger, "ICMP_R"),
INST("010010110100----", Id::ICMP_CR, Type::ArithmeticInteger, "ICMP_CR"),
INST("0011011-0100----", Id::ICMP_IMM, Type::ArithmeticInteger, "ICMP_IMM"),
INST("0101101111011---", Id::LEA_R2, Type::ArithmeticInteger, "LEA_R2"),
INST("0101101111010---", Id::LEA_R1, Type::ArithmeticInteger, "LEA_R1"),
INST("001101101101----", Id::LEA_IMM, Type::ArithmeticInteger, "LEA_IMM"),
+1 -3
View File
@@ -131,9 +131,7 @@ bool MacroInterpreter::Step(u32 offset, bool is_delay_slot) {
// An instruction with the Exit flag will not actually
// cause an exit if it's executed inside a delay slot.
// TODO(Blinkhawk): Reversed to always exit. The behavior explained above requires further
// testing on the MME code.
if (opcode.is_exit) {
if (opcode.is_exit && !is_delay_slot) {
// Exit has a delay slot, execute the next instruction
Step(offset, true);
return false;
@@ -489,9 +489,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
// Assume that a surface will be written to if it is used as a framebuffer, even if
// the shader doesn't actually write to it.
texture_cache.MarkColorBufferInUse(*single_color_target);
// Workaround for and issue in nvidia drivers
// https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
state.framebuffer_srgb.enabled |= color_surface->GetSurfaceParams().srgb_conversion;
}
fbkey.is_single_buffer = true;
@@ -512,11 +509,6 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
// Assume that a surface will be written to if it is used as a framebuffer, even
// if the shader doesn't actually write to it.
texture_cache.MarkColorBufferInUse(index);
// Enable sRGB only for supported formats
// Workaround for and issue in nvidia drivers
// https://devtalk.nvidia.com/default/topic/776591/opengl/gl_framebuffer_srgb-functions-incorrectly/
state.framebuffer_srgb.enabled |=
color_surface->GetSurfaceParams().srgb_conversion;
}
fbkey.color_attachments[index] =
@@ -906,6 +898,7 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
}
screen_info.display_texture = surface->GetTexture();
screen_info.display_srgb = surface->GetSurfaceParams().srgb_conversion;
return true;
}
@@ -214,7 +214,8 @@ CachedProgram SpecializeShader(const std::string& code, const GLShader::ShaderEn
std::string source = "#version 430 core\n"
"#extension GL_ARB_separate_shader_objects : enable\n"
"#extension GL_NV_gpu_shader5 : enable\n"
"#extension GL_NV_shader_thread_group : enable\n";
"#extension GL_NV_shader_thread_group : enable\n"
"#extension GL_NV_shader_thread_shuffle : enable\n";
if (entries.shader_viewport_layer_array) {
source += "#extension GL_ARB_shader_viewport_layer_array : enable\n";
}
@@ -325,6 +325,7 @@ public:
DeclareRegisters();
DeclarePredicates();
DeclareLocalMemory();
DeclareSharedMemory();
DeclareInternalFlags();
DeclareInputAttributes();
DeclareOutputAttributes();
@@ -499,6 +500,13 @@ private:
code.AddNewLine();
}
void DeclareSharedMemory() {
if (stage != ProgramType::Compute) {
return;
}
code.AddLine("shared uint {}[];", GetSharedMemory());
}
void DeclareInternalFlags() {
for (u32 flag = 0; flag < static_cast<u32>(InternalFlag::Amount); flag++) {
const auto flag_code = static_cast<InternalFlag>(flag);
@@ -881,6 +889,12 @@ private:
Type::Uint};
}
if (const auto smem = std::get_if<SmemNode>(&*node)) {
return {
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
Type::Uint};
}
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
return {GetInternalFlag(internal_flag->GetFlag()), Type::Bool};
}
@@ -1007,10 +1021,10 @@ private:
return {std::move(temporary), value.GetType()};
}
Expression GetOutputAttribute(const AbufNode* abuf) {
std::optional<Expression> GetOutputAttribute(const AbufNode* abuf) {
switch (const auto attribute = abuf->GetIndex()) {
case Attribute::Index::Position:
return {"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float};
return {{"gl_Position"s + GetSwizzle(abuf->GetElement()), Type::Float}};
case Attribute::Index::LayerViewportPointSize:
switch (abuf->GetElement()) {
case 0:
@@ -1020,25 +1034,25 @@ private:
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
return {};
}
return {"gl_Layer", Type::Int};
return {{"gl_Layer", Type::Int}};
case 2:
if (IsVertexShader(stage) && !device.HasVertexViewportLayer()) {
return {};
}
return {"gl_ViewportIndex", Type::Int};
return {{"gl_ViewportIndex", Type::Int}};
case 3:
UNIMPLEMENTED_MSG("Requires some state changes for gl_PointSize to work in shader");
return {"gl_PointSize", Type::Float};
return {{"gl_PointSize", Type::Float}};
}
return {};
case Attribute::Index::ClipDistances0123:
return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float};
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement()), Type::Float}};
case Attribute::Index::ClipDistances4567:
return {fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float};
return {{fmt::format("gl_ClipDistance[{}]", abuf->GetElement() + 4), Type::Float}};
default:
if (IsGenericAttribute(attribute)) {
return {GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()),
Type::Float};
return {
{GetOutputAttribute(attribute) + GetSwizzle(abuf->GetElement()), Type::Float}};
}
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
return {};
@@ -1278,7 +1292,11 @@ private:
target = {GetRegister(gpr->GetIndex()), Type::Float};
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
target = GetOutputAttribute(abuf);
auto output = GetOutputAttribute(abuf);
if (!output) {
return {};
}
target = std::move(*output);
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
if (stage == ProgramType::Compute) {
LOG_WARNING(Render_OpenGL, "Local memory is stubbed on compute shaders");
@@ -1286,6 +1304,11 @@ private:
target = {
fmt::format("{}[{} >> 2]", GetLocalMemory(), Visit(lmem->GetAddress()).AsUint()),
Type::Uint};
} else if (const auto smem = std::get_if<SmemNode>(&*dest)) {
ASSERT(stage == ProgramType::Compute);
target = {
fmt::format("{}[{} >> 2]", GetSharedMemory(), Visit(smem->GetAddress()).AsUint()),
Type::Uint};
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
const std::string real = Visit(gmem->GetRealAddress()).AsUint();
const std::string base = Visit(gmem->GetBaseAddress()).AsUint();
@@ -1934,8 +1957,7 @@ private:
Expression BallotThread(Operation operation) {
const std::string value = VisitOperand(operation, 0).AsBool();
if (!device.HasWarpIntrinsics()) {
LOG_ERROR(Render_OpenGL,
"Nvidia warp intrinsics are not available and its required by a shader");
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
// Stub on non-Nvidia devices by simulating all threads voting the same as the active
// one.
return {fmt::format("({} ? 0xFFFFFFFFU : 0U)", value), Type::Uint};
@@ -1946,8 +1968,7 @@ private:
Expression Vote(Operation operation, const char* func) {
const std::string value = VisitOperand(operation, 0).AsBool();
if (!device.HasWarpIntrinsics()) {
LOG_ERROR(Render_OpenGL,
"Nvidia vote intrinsics are not available and its required by a shader");
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
// Stub with a warp size of one.
return {value, Type::Bool};
}
@@ -1964,15 +1985,54 @@ private:
Expression VoteEqual(Operation operation) {
if (!device.HasWarpIntrinsics()) {
LOG_ERROR(Render_OpenGL,
"Nvidia vote intrinsics are not available and its required by a shader");
// We must return true here since a stub for a theoretical warp size of 1 will always
// return an equal result for all its votes.
LOG_ERROR(Render_OpenGL, "Nvidia vote intrinsics are required by this shader");
// We must return true here since a stub for a theoretical warp size of 1.
// This will always return an equal result across all votes.
return {"true", Type::Bool};
}
return Vote(operation, "allThreadsEqualNV");
}
template <const std::string_view& func>
Expression Shuffle(Operation operation) {
const std::string value = VisitOperand(operation, 0).AsFloat();
if (!device.HasWarpIntrinsics()) {
LOG_ERROR(Render_OpenGL, "Nvidia shuffle intrinsics are required by this shader");
// On a "single-thread" device we are either on the same thread or out of bounds. Both
// cases return the passed value.
return {value, Type::Float};
}
const std::string index = VisitOperand(operation, 1).AsUint();
const std::string width = VisitOperand(operation, 2).AsUint();
return {fmt::format("{}({}, {}, {})", func, value, index, width), Type::Float};
}
template <const std::string_view& func>
Expression InRangeShuffle(Operation operation) {
const std::string index = VisitOperand(operation, 0).AsUint();
const std::string width = VisitOperand(operation, 1).AsUint();
if (!device.HasWarpIntrinsics()) {
// On a "single-thread" device we are only in bounds when the requested index is 0.
return {fmt::format("({} == 0U)", index), Type::Bool};
}
const std::string in_range = code.GenerateTemporary();
code.AddLine("bool {};", in_range);
code.AddLine("{}(0U, {}, {}, {});", func, index, width, in_range);
return {in_range, Type::Bool};
}
struct Func final {
Func() = delete;
~Func() = delete;
static constexpr std::string_view ShuffleIndexed = "shuffleNV";
static constexpr std::string_view ShuffleUp = "shuffleUpNV";
static constexpr std::string_view ShuffleDown = "shuffleDownNV";
static constexpr std::string_view ShuffleButterfly = "shuffleXorNV";
};
static constexpr std::array operation_decompilers = {
&GLSLDecompiler::Assign,
@@ -2135,6 +2195,16 @@ private:
&GLSLDecompiler::VoteAll,
&GLSLDecompiler::VoteAny,
&GLSLDecompiler::VoteEqual,
&GLSLDecompiler::Shuffle<Func::ShuffleIndexed>,
&GLSLDecompiler::Shuffle<Func::ShuffleUp>,
&GLSLDecompiler::Shuffle<Func::ShuffleDown>,
&GLSLDecompiler::Shuffle<Func::ShuffleButterfly>,
&GLSLDecompiler::InRangeShuffle<Func::ShuffleIndexed>,
&GLSLDecompiler::InRangeShuffle<Func::ShuffleUp>,
&GLSLDecompiler::InRangeShuffle<Func::ShuffleDown>,
&GLSLDecompiler::InRangeShuffle<Func::ShuffleButterfly>,
};
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
@@ -2175,6 +2245,10 @@ private:
return "lmem_" + suffix;
}
std::string GetSharedMemory() const {
return fmt::format("smem_{}", suffix);
}
std::string GetInternalFlag(InternalFlag flag) const {
constexpr std::array InternalFlagNames = {"zero_flag", "sign_flag", "carry_flag",
"overflow_flag"};
@@ -16,7 +16,6 @@ namespace OpenGL {
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
OpenGLState OpenGLState::cur_state;
bool OpenGLState::s_rgb_used;
namespace {
@@ -282,8 +281,6 @@ void OpenGLState::ApplySRgb() const {
return;
cur_state.framebuffer_srgb.enabled = framebuffer_srgb.enabled;
if (framebuffer_srgb.enabled) {
// Track if sRGB is used
s_rgb_used = true;
glEnable(GL_FRAMEBUFFER_SRGB);
} else {
glDisable(GL_FRAMEBUFFER_SRGB);
-10
View File
@@ -175,14 +175,6 @@ public:
return cur_state;
}
static bool GetsRGBUsed() {
return s_rgb_used;
}
static void ClearsRGBUsed() {
s_rgb_used = false;
}
void SetDefaultViewports();
/// Apply this state as the current OpenGL state
void Apply();
@@ -253,8 +245,6 @@ public:
private:
static OpenGLState cur_state;
// Workaround for sRGB problems caused by QT not supporting srgb output
static bool s_rgb_used;
struct {
bool blend_state;
bool stencil_state;
@@ -145,7 +145,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
case Tegra::Texture::TextureMipmapFilter::None:
return GL_LINEAR;
case Tegra::Texture::TextureMipmapFilter::Nearest:
return GL_NEAREST_MIPMAP_LINEAR;
return GL_LINEAR_MIPMAP_NEAREST;
case Tegra::Texture::TextureMipmapFilter::Linear:
return GL_LINEAR_MIPMAP_LINEAR;
}
@@ -157,7 +157,7 @@ inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode,
case Tegra::Texture::TextureMipmapFilter::Nearest:
return GL_NEAREST_MIPMAP_NEAREST;
case Tegra::Texture::TextureMipmapFilter::Linear:
return GL_LINEAR_MIPMAP_NEAREST;
return GL_NEAREST_MIPMAP_LINEAR;
}
}
}
@@ -264,7 +264,6 @@ void RendererOpenGL::CreateRasterizer() {
if (rasterizer) {
return;
}
OpenGLState::ClearsRGBUsed();
rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, screen_info);
}
@@ -343,9 +342,7 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
}};
state.textures[0] = screen_info.display_texture;
// Workaround brigthness problems in SMO by enabling sRGB in the final output
// if it has been used in the frame. Needed because of this bug in QT: QTBUG-50987
state.framebuffer_srgb.enabled = OpenGLState::GetsRGBUsed();
state.framebuffer_srgb.enabled = screen_info.display_srgb;
state.AllDirty();
state.Apply();
glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), vertices.data());
@@ -355,8 +352,6 @@ void RendererOpenGL::DrawScreenTriangles(const ScreenInfo& screen_info, float x,
state.textures[0] = 0;
state.AllDirty();
state.Apply();
// Clear sRGB state for the next frame
OpenGLState::ClearsRGBUsed();
}
/**
@@ -406,8 +401,8 @@ void RendererOpenGL::CaptureScreenshot() {
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, state.GetsRGBUsed() ? GL_SRGB8 : GL_RGB8, layout.width,
layout.height);
glRenderbufferStorage(GL_RENDERBUFFER, screen_info.display_srgb ? GL_SRGB8 : GL_RGB8,
layout.width, layout.height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
DrawScreen(layout);
@@ -38,7 +38,8 @@ struct TextureInfo {
/// Structure used for storing information about the display target for the Switch screen
struct ScreenInfo {
GLuint display_texture;
GLuint display_texture{};
bool display_srgb{};
const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
TextureInfo texture;
};
@@ -1127,6 +1127,46 @@ private:
return {};
}
Id ShuffleIndexed(Operation) {
UNIMPLEMENTED();
return {};
}
Id ShuffleUp(Operation) {
UNIMPLEMENTED();
return {};
}
Id ShuffleDown(Operation) {
UNIMPLEMENTED();
return {};
}
Id ShuffleButterfly(Operation) {
UNIMPLEMENTED();
return {};
}
Id InRangeShuffleIndexed(Operation) {
UNIMPLEMENTED();
return {};
}
Id InRangeShuffleUp(Operation) {
UNIMPLEMENTED();
return {};
}
Id InRangeShuffleDown(Operation) {
UNIMPLEMENTED();
return {};
}
Id InRangeShuffleButterfly(Operation) {
UNIMPLEMENTED();
return {};
}
Id DeclareBuiltIn(spv::BuiltIn builtin, spv::StorageClass storage, Id type,
const std::string& name) {
const Id id = OpVariable(type, storage);
@@ -1431,6 +1471,16 @@ private:
&SPIRVDecompiler::VoteAll,
&SPIRVDecompiler::VoteAny,
&SPIRVDecompiler::VoteEqual,
&SPIRVDecompiler::ShuffleIndexed,
&SPIRVDecompiler::ShuffleUp,
&SPIRVDecompiler::ShuffleDown,
&SPIRVDecompiler::ShuffleButterfly,
&SPIRVDecompiler::InRangeShuffleIndexed,
&SPIRVDecompiler::InRangeShuffleUp,
&SPIRVDecompiler::InRangeShuffleDown,
&SPIRVDecompiler::InRangeShuffleButterfly,
};
static_assert(operation_decompilers.size() == static_cast<std::size_t>(OperationCode::Amount));
@@ -138,6 +138,35 @@ u32 ShaderIR::DecodeArithmeticInteger(NodeBlock& bb, u32 pc) {
SetRegister(bb, instr.gpr0, value);
break;
}
case OpCode::Id::ICMP_CR:
case OpCode::Id::ICMP_R:
case OpCode::Id::ICMP_RC:
case OpCode::Id::ICMP_IMM: {
const Node zero = Immediate(0);
const auto [op_b, test] = [&]() -> std::pair<Node, Node> {
switch (opcode->get().GetId()) {
case OpCode::Id::ICMP_CR:
return {GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset),
GetRegister(instr.gpr39)};
case OpCode::Id::ICMP_R:
return {GetRegister(instr.gpr20), GetRegister(instr.gpr39)};
case OpCode::Id::ICMP_RC:
return {GetRegister(instr.gpr39),
GetConstBuffer(instr.cbuf34.index, instr.cbuf34.offset)};
case OpCode::Id::ICMP_IMM:
return {Immediate(instr.alu.GetSignedImm20_20()), GetRegister(instr.gpr39)};
default:
UNREACHABLE();
return {zero, zero};
}
}();
const Node op_a = GetRegister(instr.gpr8);
const Node comparison =
GetPredicateComparisonInteger(instr.icmp.cond, instr.icmp.is_signed != 0, test, zero);
SetRegister(bb, instr.gpr0, Operation(OperationCode::Select, comparison, op_a, op_b));
break;
}
case OpCode::Id::LOP_C:
case OpCode::Id::LOP_R:
case OpCode::Id::LOP_IMM: {
+29 -19
View File
@@ -35,7 +35,7 @@ u32 GetUniformTypeElementsCount(Tegra::Shader::UniformType uniform_type) {
return 1;
}
}
} // namespace
} // Anonymous namespace
u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
const Instruction instr = {program_code[pc]};
@@ -106,16 +106,17 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
}
break;
}
case OpCode::Id::LD_L: {
LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}",
static_cast<u64>(instr.ld_l.unknown.Value()));
const auto GetLmem = [&](s32 offset) {
case OpCode::Id::LD_L:
LOG_DEBUG(HW_GPU, "LD_L cache management mode: {}", static_cast<u64>(instr.ld_l.unknown));
[[fallthrough]];
case OpCode::Id::LD_S: {
const auto GetMemory = [&](s32 offset) {
ASSERT(offset % 4 == 0);
const Node immediate_offset = Immediate(static_cast<s32>(instr.smem_imm) + offset);
const Node address = Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8),
immediate_offset);
return GetLocalMemory(address);
return opcode->get().GetId() == OpCode::Id::LD_S ? GetSharedMemory(address)
: GetLocalMemory(address);
};
switch (instr.ldst_sl.type.Value()) {
@@ -135,14 +136,16 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
return 0;
}
}();
for (u32 i = 0; i < count; ++i)
SetTemporary(bb, i, GetLmem(i * 4));
for (u32 i = 0; i < count; ++i)
for (u32 i = 0; i < count; ++i) {
SetTemporary(bb, i, GetMemory(i * 4));
}
for (u32 i = 0; i < count; ++i) {
SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
}
break;
}
default:
UNIMPLEMENTED_MSG("LD_L Unhandled type: {}",
UNIMPLEMENTED_MSG("{} Unhandled type: {}", opcode->get().GetName(),
static_cast<u32>(instr.ldst_sl.type.Value()));
}
break;
@@ -209,27 +212,34 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::ST_L: {
case OpCode::Id::ST_L:
LOG_DEBUG(HW_GPU, "ST_L cache management mode: {}",
static_cast<u64>(instr.st_l.cache_management.Value()));
const auto GetLmemAddr = [&](s32 offset) {
[[fallthrough]];
case OpCode::Id::ST_S: {
const auto GetAddress = [&](s32 offset) {
ASSERT(offset % 4 == 0);
const Node immediate = Immediate(static_cast<s32>(instr.smem_imm) + offset);
return Operation(OperationCode::IAdd, NO_PRECISE, GetRegister(instr.gpr8), immediate);
};
const auto set_memory = opcode->get().GetId() == OpCode::Id::ST_L
? &ShaderIR::SetLocalMemory
: &ShaderIR::SetSharedMemory;
switch (instr.ldst_sl.type.Value()) {
case Tegra::Shader::StoreType::Bits128:
SetLocalMemory(bb, GetLmemAddr(12), GetRegister(instr.gpr0.Value() + 3));
SetLocalMemory(bb, GetLmemAddr(8), GetRegister(instr.gpr0.Value() + 2));
(this->*set_memory)(bb, GetAddress(12), GetRegister(instr.gpr0.Value() + 3));
(this->*set_memory)(bb, GetAddress(8), GetRegister(instr.gpr0.Value() + 2));
[[fallthrough]];
case Tegra::Shader::StoreType::Bits64:
SetLocalMemory(bb, GetLmemAddr(4), GetRegister(instr.gpr0.Value() + 1));
(this->*set_memory)(bb, GetAddress(4), GetRegister(instr.gpr0.Value() + 1));
[[fallthrough]];
case Tegra::Shader::StoreType::Bits32:
SetLocalMemory(bb, GetLmemAddr(0), GetRegister(instr.gpr0));
(this->*set_memory)(bb, GetAddress(0), GetRegister(instr.gpr0));
break;
default:
UNIMPLEMENTED_MSG("ST_L Unhandled type: {}",
UNIMPLEMENTED_MSG("{} unhandled type: {}", opcode->get().GetName(),
static_cast<u32>(instr.ldst_sl.type.Value()));
}
break;
+47
View File
@@ -13,6 +13,7 @@ namespace VideoCommon::Shader {
using Tegra::Shader::Instruction;
using Tegra::Shader::OpCode;
using Tegra::Shader::Pred;
using Tegra::Shader::ShuffleOperation;
using Tegra::Shader::VoteOperation;
namespace {
@@ -44,6 +45,52 @@ u32 ShaderIR::DecodeWarp(NodeBlock& bb, u32 pc) {
SetPredicate(bb, instr.vote.dest_pred, vote);
break;
}
case OpCode::Id::SHFL: {
Node mask = instr.shfl.is_mask_imm ? Immediate(static_cast<u32>(instr.shfl.mask_imm))
: GetRegister(instr.gpr39);
Node width = [&] {
// Convert the obscure SHFL mask back into GL_NV_shader_thread_shuffle's width. This has
// been done reversing Nvidia's math. It won't work on all cases due to SHFL having
// different parameters that don't properly map to GLSL's interface, but it should work
// for cases emitted by Nvidia's compiler.
if (instr.shfl.operation == ShuffleOperation::Up) {
return Operation(
OperationCode::ILogicalShiftRight,
Operation(OperationCode::IAdd, std::move(mask), Immediate(-0x2000)),
Immediate(8));
} else {
return Operation(OperationCode::ILogicalShiftRight,
Operation(OperationCode::IAdd, Immediate(0x201F),
Operation(OperationCode::INegate, std::move(mask))),
Immediate(8));
}
}();
const auto [operation, in_range] = [instr]() -> std::pair<OperationCode, OperationCode> {
switch (instr.shfl.operation) {
case ShuffleOperation::Idx:
return {OperationCode::ShuffleIndexed, OperationCode::InRangeShuffleIndexed};
case ShuffleOperation::Up:
return {OperationCode::ShuffleUp, OperationCode::InRangeShuffleUp};
case ShuffleOperation::Down:
return {OperationCode::ShuffleDown, OperationCode::InRangeShuffleDown};
case ShuffleOperation::Bfly:
return {OperationCode::ShuffleButterfly, OperationCode::InRangeShuffleButterfly};
}
UNREACHABLE_MSG("Invalid SHFL operation: {}",
static_cast<u64>(instr.shfl.operation.Value()));
return {};
}();
// Setting the predicate before the register is intentional to avoid overwriting.
Node index = instr.shfl.is_index_imm ? Immediate(static_cast<u32>(instr.shfl.index_imm))
: GetRegister(instr.gpr20);
SetPredicate(bb, instr.shfl.pred48, Operation(in_range, index, width));
SetRegister(
bb, instr.gpr0,
Operation(operation, GetRegister(instr.gpr8), std::move(index), std::move(width)));
break;
}
default:
UNIMPLEMENTED_MSG("Unhandled warp instruction: {}", opcode->get().GetName());
break;
+25 -1
View File
@@ -181,6 +181,16 @@ enum class OperationCode {
VoteAny, /// (bool) -> bool
VoteEqual, /// (bool) -> bool
ShuffleIndexed, /// (uint value, uint index, uint width) -> uint
ShuffleUp, /// (uint value, uint index, uint width) -> uint
ShuffleDown, /// (uint value, uint index, uint width) -> uint
ShuffleButterfly, /// (uint value, uint index, uint width) -> uint
InRangeShuffleIndexed, /// (uint index, uint width) -> bool
InRangeShuffleUp, /// (uint index, uint width) -> bool
InRangeShuffleDown, /// (uint index, uint width) -> bool
InRangeShuffleButterfly, /// (uint index, uint width) -> bool
Amount,
};
@@ -206,12 +216,13 @@ class PredicateNode;
class AbufNode;
class CbufNode;
class LmemNode;
class SmemNode;
class GmemNode;
class CommentNode;
using NodeData =
std::variant<OperationNode, ConditionalNode, GprNode, ImmediateNode, InternalFlagNode,
PredicateNode, AbufNode, CbufNode, LmemNode, GmemNode, CommentNode>;
PredicateNode, AbufNode, CbufNode, LmemNode, SmemNode, GmemNode, CommentNode>;
using Node = std::shared_ptr<NodeData>;
using Node4 = std::array<Node, 4>;
using NodeBlock = std::vector<Node>;
@@ -583,6 +594,19 @@ private:
Node address;
};
/// Shared memory node
class SmemNode final {
public:
explicit SmemNode(Node address) : address{std::move(address)} {}
const Node& GetAddress() const {
return address;
}
private:
Node address;
};
/// Global memory node
class GmemNode final {
public:
+9
View File
@@ -137,6 +137,10 @@ Node ShaderIR::GetLocalMemory(Node address) {
return MakeNode<LmemNode>(std::move(address));
}
Node ShaderIR::GetSharedMemory(Node address) {
return MakeNode<SmemNode>(std::move(address));
}
Node ShaderIR::GetTemporary(u32 id) {
return GetRegister(Register::ZeroIndex + 1 + id);
}
@@ -378,6 +382,11 @@ void ShaderIR::SetLocalMemory(NodeBlock& bb, Node address, Node value) {
Operation(OperationCode::Assign, GetLocalMemory(std::move(address)), std::move(value)));
}
void ShaderIR::SetSharedMemory(NodeBlock& bb, Node address, Node value) {
bb.push_back(
Operation(OperationCode::Assign, GetSharedMemory(std::move(address)), std::move(value)));
}
void ShaderIR::SetTemporary(NodeBlock& bb, u32 id, Node value) {
SetRegister(bb, Register::ZeroIndex + 1 + id, std::move(value));
}
+5 -1
View File
@@ -208,6 +208,8 @@ private:
Node GetInternalFlag(InternalFlag flag, bool negated = false);
/// Generates a node representing a local memory address
Node GetLocalMemory(Node address);
/// Generates a node representing a shared memory address
Node GetSharedMemory(Node address);
/// Generates a temporary, internally it uses a post-RZ register
Node GetTemporary(u32 id);
@@ -217,8 +219,10 @@ private:
void SetPredicate(NodeBlock& bb, u64 dest, Node src);
/// Sets an internal flag. src value must be a bool-evaluated node
void SetInternalFlag(NodeBlock& bb, InternalFlag flag, Node value);
/// Sets a local memory address. address and value must be a number-evaluated node
/// Sets a local memory address with a value.
void SetLocalMemory(NodeBlock& bb, Node address, Node value);
/// Sets a shared memory address with a value.
void SetSharedMemory(NodeBlock& bb, Node address, Node value);
/// Sets a temporary. Internally it uses a post-RZ register
void SetTemporary(NodeBlock& bb, u32 id, Node value);
+5
View File
@@ -466,6 +466,9 @@ void Config::ReadDataStorageValues() {
void Config::ReadDebuggingValues() {
qt_config->beginGroup(QStringLiteral("Debugging"));
// Intentionally not using the QT default setting as this is intended to be changed in the ini
Settings::values.record_frame_times =
qt_config->value(QStringLiteral("record_frame_times"), false).toBool();
Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
Settings::values.program_args =
@@ -879,6 +882,8 @@ void Config::SaveDataStorageValues() {
void Config::SaveDebuggingValues() {
qt_config->beginGroup(QStringLiteral("Debugging"));
// Intentionally not using the QT default setting as this is intended to be changed in the ini
qt_config->setValue(QStringLiteral("record_frame_times"), Settings::values.record_frame_times);
WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
WriteSetting(QStringLiteral("program_args"),
@@ -182,6 +182,8 @@ void ConfigureInput::UpdateUIEnabled() {
players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
}
ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() &&
!ui->use_docked_mode->isChecked());
ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
!ui->use_docked_mode->isChecked());
+11 -3
View File
@@ -54,6 +54,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QProgressDialog>
#include <QShortcut>
#include <QStatusBar>
#include <QSysInfo>
#include <QtConcurrent/QtConcurrent>
#include <fmt/format.h>
@@ -66,6 +67,9 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "common/microprofile.h"
#include "common/scm_rev.h"
#include "common/scope_exit.h"
#ifdef ARCHITECTURE_x86_64
#include "common/x64/cpu_detect.h"
#endif
#include "common/telemetry.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
@@ -205,6 +209,10 @@ GMainWindow::GMainWindow()
LOG_INFO(Frontend, "yuzu Version: {} | {}-{}", Common::g_build_fullname, Common::g_scm_branch,
Common::g_scm_desc);
#ifdef ARCHITECTURE_x86_64
LOG_INFO(Frontend, "Host CPU: {}", Common::GetCPUCaps().cpu_string);
#endif
LOG_INFO(Frontend, "Host OS: {}", QSysInfo::prettyProductName().toStdString());
UpdateWindowTitle();
show();
@@ -964,11 +972,11 @@ void GMainWindow::BootGame(const QString& filename) {
}
status_bar_update_timer.start(2000);
const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
std::string title_name;
const auto res = Core::System::GetInstance().GetGameName(title_name);
if (res != Loader::ResultStatus::Success) {
const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
const auto [nacp, icon_file] = FileSys::PatchManager(title_id).GetControlMetadata();
if (nacp != nullptr)
title_name = nacp->GetApplicationName();
@@ -976,7 +984,7 @@ void GMainWindow::BootGame(const QString& filename) {
if (title_name.empty())
title_name = FileUtil::GetFilename(filename.toStdString());
}
LOG_INFO(Frontend, "Booting game: {:016X} | {}", title_id, title_name);
UpdateWindowTitle(QString::fromStdString(title_name));
loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
+2
View File
@@ -374,6 +374,8 @@ void Config::ReadValues() {
Settings::values.use_dev_keys = sdl2_config->GetBoolean("Miscellaneous", "use_dev_keys", false);
// Debugging
Settings::values.record_frame_times =
sdl2_config->GetBoolean("Debugging", "record_frame_times", false);
Settings::values.use_gdbstub = sdl2_config->GetBoolean("Debugging", "use_gdbstub", false);
Settings::values.gdbstub_port =
static_cast<u16>(sdl2_config->GetInteger("Debugging", "gdbstub_port", 24689));
+2
View File
@@ -213,6 +213,8 @@ region_value =
log_filter = *:Trace
[Debugging]
# Record frame time data, can be found in the log directory. Boolean value
record_frame_times =
# Port for listening to GDB connections.
use_gdbstub=false
gdbstub_port=24689