Compare commits
80
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe34f07d5a | ||
|
|
ba0a20b394 | ||
|
|
e0fe8802b6 | ||
|
|
11d3927b5b | ||
|
|
a1e7391c39 | ||
|
|
6775e03817 | ||
|
|
abee0adfb8 | ||
|
|
a8f8f36baa | ||
|
|
96e50c0d8a | ||
|
|
4d66449142 | ||
|
|
68694e007a | ||
|
|
64724ebe5f | ||
|
|
dc69b06d37 | ||
|
|
4fe711bde9 | ||
|
|
1572fb9bf2 | ||
|
|
e3107788e6 | ||
|
|
dd2e96b362 | ||
|
|
a86a88109a | ||
|
|
8d009a7aae | ||
|
|
20521da259 | ||
|
|
dc5eedef71 | ||
|
|
074b38b7a9 | ||
|
|
222f4b45eb | ||
|
|
fd922ddb01 | ||
|
|
7831e86c34 | ||
|
|
a2eccbf075 | ||
|
|
15d177a6ac | ||
|
|
7f6a8a33d4 | ||
|
|
081530686c | ||
|
|
d1713ad451 | ||
|
|
c6bec9aa10 | ||
|
|
2c34d8aabb | ||
|
|
219fdcb9d9 | ||
|
|
a21b88ef8f | ||
|
|
7cf091bb20 | ||
|
|
7398466c33 | ||
|
|
125caf5d6e | ||
|
|
98c6a95079 | ||
|
|
6414d9e2e5 | ||
|
|
b77430df70 | ||
|
|
141d929929 | ||
|
|
9fe8072c67 | ||
|
|
60c602e4e7 | ||
|
|
e00b529a89 | ||
|
|
ef9b31783d | ||
|
|
60315060b1 | ||
|
|
cf9e13c255 | ||
|
|
14f3cebcd4 | ||
|
|
a24e8bf9cf | ||
|
|
77b4916b33 | ||
|
|
4f16ce9294 | ||
|
|
67df3f7742 | ||
|
|
04a1161354 | ||
|
|
2f2ab9b5bc | ||
|
|
b8a62adcf1 | ||
|
|
d1d7ce74d2 | ||
|
|
67658dd6e8 | ||
|
|
9760795bfb | ||
|
|
c9c75f9587 | ||
|
|
2299950de1 | ||
|
|
ba0086e32d | ||
|
|
b25b94400e | ||
|
|
cc1d7048b5 | ||
|
|
524eb15513 | ||
|
|
d5706346d7 | ||
|
|
ac4dbd3b25 | ||
|
|
574440d59f | ||
|
|
cfc2f30dc4 | ||
|
|
f32a49d3d8 | ||
|
|
b9ddb517b1 | ||
|
|
538f5880ff | ||
|
|
75395605d6 | ||
|
|
9f2719d1a4 | ||
|
|
3f104464de | ||
|
|
69fa2e6525 | ||
|
|
782b7a0ca4 | ||
|
|
ffc2ce89a0 | ||
|
|
976d9ef43c | ||
|
|
5b5e60ffec | ||
|
|
4e9f975935 |
No files matched your search
@@ -97,11 +97,11 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
)
|
||||
|
||||
add_library(common STATIC
|
||||
algorithm.h
|
||||
alignment.h
|
||||
assert.h
|
||||
detached_tasks.cpp
|
||||
detached_tasks.h
|
||||
binary_find.h
|
||||
bit_field.h
|
||||
bit_util.h
|
||||
cityhash.cpp
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
|
||||
// Algorithms that operate on iterators, much like the <algorithm> header.
|
||||
//
|
||||
// Note: If the algorithm is not general-purpose and/or doesn't operate on iterators,
|
||||
// it should probably not be placed within this header.
|
||||
|
||||
namespace Common {
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ struct HashableStruct {
|
||||
|
||||
struct PairHash {
|
||||
template <class T1, class T2>
|
||||
std::size_t operator()(const std::pair<T1, T2>& pair) const {
|
||||
std::size_t operator()(const std::pair<T1, T2>& pair) const noexcept {
|
||||
std::size_t seed = std::hash<T1>()(pair.first);
|
||||
boost::hash_combine(seed, std::hash<T2>()(pair.second));
|
||||
return seed;
|
||||
|
||||
+15
-12
@@ -112,8 +112,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||
}
|
||||
struct System::Impl {
|
||||
explicit Impl(System& system)
|
||||
: kernel{system}, fs_controller{system}, cpu_core_manager{system},
|
||||
applet_manager{system}, reporter{system} {}
|
||||
: kernel{system}, fs_controller{system}, cpu_core_manager{system}, reporter{system},
|
||||
applet_manager{system} {}
|
||||
|
||||
Cpu& CurrentCpuCore() {
|
||||
return cpu_core_manager.GetCurrentCore();
|
||||
@@ -240,16 +240,19 @@ struct System::Impl {
|
||||
}
|
||||
|
||||
void Shutdown() {
|
||||
// Log last frame performance stats
|
||||
const auto perf_results = GetAndResetPerfStats();
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_EmulationSpeed",
|
||||
perf_results.emulation_speed * 100.0);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
|
||||
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());
|
||||
// Log last frame performance stats if game was loded
|
||||
if (perf_stats) {
|
||||
const auto perf_results = GetAndResetPerfStats();
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance,
|
||||
"Shutdown_EmulationSpeed",
|
||||
perf_results.emulation_speed * 100.0);
|
||||
telemetry_session->AddField(Telemetry::FieldType::Performance, "Shutdown_Framerate",
|
||||
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());
|
||||
}
|
||||
|
||||
lm_manager.Flush();
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace FileSys {
|
||||
constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
|
||||
|
||||
namespace {
|
||||
|
||||
void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
|
||||
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
|
||||
if (meta.zero_1 != 0) {
|
||||
@@ -52,6 +53,14 @@ void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
|
||||
meta.user_id[1], meta.user_id[0]);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShouldSaveDataBeAutomaticallyCreated(SaveDataSpaceId space, const SaveDataDescriptor& desc) {
|
||||
return desc.type == SaveDataType::CacheStorage ///< Cache Saves
|
||||
|| desc.type == SaveDataType::TemporaryStorage ///< Temp Saves
|
||||
|| (space == SaveDataSpaceId::NandUser && ///< Normal Save Data -- Current Title & User
|
||||
desc.type == SaveDataType::SaveData && desc.title_id == 0 && desc.save_id == 0);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
std::string SaveDataDescriptor::DebugInfo() const {
|
||||
@@ -96,6 +105,10 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
|
||||
|
||||
auto out = dir->GetDirectoryRelative(save_directory);
|
||||
|
||||
if (out == nullptr && ShouldSaveDataBeAutomaticallyCreated(space, meta)) {
|
||||
return Create(space, meta);
|
||||
}
|
||||
|
||||
// Return an error if the save data doesn't actually exist.
|
||||
if (out == nullptr) {
|
||||
// TODO(Subv): Find out correct error code.
|
||||
|
||||
@@ -1139,8 +1139,9 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_AM, "called, kind={:08X}", static_cast<u8>(kind));
|
||||
|
||||
if (kind == LaunchParameterKind::ApplicationSpecific && !launch_popped_application_specific) {
|
||||
const auto backend = BCAT::CreateBackendFromSettings(
|
||||
[this](u64 tid) { return system.GetFileSystemController().GetBCATDirectory(tid); });
|
||||
const auto backend = BCAT::CreateBackendFromSettings(system, [this](u64 tid) {
|
||||
return system.GetFileSystemController().GetBCATDirectory(tid);
|
||||
});
|
||||
const auto build_id_full = system.GetCurrentProcessBuildID();
|
||||
u64 build_id{};
|
||||
std::memcpy(&build_id, build_id_full.data(), sizeof(u64));
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include "common/logging/log.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/hle/service/apm/controller.h"
|
||||
@@ -9,8 +13,7 @@
|
||||
|
||||
namespace Service::APM {
|
||||
|
||||
constexpr PerformanceConfiguration DEFAULT_PERFORMANCE_CONFIGURATION =
|
||||
PerformanceConfiguration::Config7;
|
||||
constexpr auto DEFAULT_PERFORMANCE_CONFIGURATION = PerformanceConfiguration::Config7;
|
||||
|
||||
Controller::Controller(Core::Timing::CoreTiming& core_timing)
|
||||
: core_timing{core_timing}, configs{
|
||||
@@ -22,18 +25,35 @@ Controller::~Controller() = default;
|
||||
|
||||
void Controller::SetPerformanceConfiguration(PerformanceMode mode,
|
||||
PerformanceConfiguration config) {
|
||||
static const std::map<PerformanceConfiguration, u32> PCONFIG_TO_SPEED_MAP{
|
||||
{PerformanceConfiguration::Config1, 1020}, {PerformanceConfiguration::Config2, 1020},
|
||||
{PerformanceConfiguration::Config3, 1224}, {PerformanceConfiguration::Config4, 1020},
|
||||
{PerformanceConfiguration::Config5, 1020}, {PerformanceConfiguration::Config6, 1224},
|
||||
{PerformanceConfiguration::Config7, 1020}, {PerformanceConfiguration::Config8, 1020},
|
||||
{PerformanceConfiguration::Config9, 1020}, {PerformanceConfiguration::Config10, 1020},
|
||||
{PerformanceConfiguration::Config11, 1020}, {PerformanceConfiguration::Config12, 1020},
|
||||
{PerformanceConfiguration::Config13, 1785}, {PerformanceConfiguration::Config14, 1785},
|
||||
{PerformanceConfiguration::Config15, 1020}, {PerformanceConfiguration::Config16, 1020},
|
||||
};
|
||||
static constexpr std::array<std::pair<PerformanceConfiguration, u32>, 16> config_to_speed{{
|
||||
{PerformanceConfiguration::Config1, 1020},
|
||||
{PerformanceConfiguration::Config2, 1020},
|
||||
{PerformanceConfiguration::Config3, 1224},
|
||||
{PerformanceConfiguration::Config4, 1020},
|
||||
{PerformanceConfiguration::Config5, 1020},
|
||||
{PerformanceConfiguration::Config6, 1224},
|
||||
{PerformanceConfiguration::Config7, 1020},
|
||||
{PerformanceConfiguration::Config8, 1020},
|
||||
{PerformanceConfiguration::Config9, 1020},
|
||||
{PerformanceConfiguration::Config10, 1020},
|
||||
{PerformanceConfiguration::Config11, 1020},
|
||||
{PerformanceConfiguration::Config12, 1020},
|
||||
{PerformanceConfiguration::Config13, 1785},
|
||||
{PerformanceConfiguration::Config14, 1785},
|
||||
{PerformanceConfiguration::Config15, 1020},
|
||||
{PerformanceConfiguration::Config16, 1020},
|
||||
}};
|
||||
|
||||
SetClockSpeed(PCONFIG_TO_SPEED_MAP.find(config)->second);
|
||||
const auto iter = std::find_if(config_to_speed.cbegin(), config_to_speed.cend(),
|
||||
[config](const auto& entry) { return entry.first == config; });
|
||||
|
||||
if (iter == config_to_speed.cend()) {
|
||||
LOG_ERROR(Service_APM, "Invalid performance configuration value provided: {}",
|
||||
static_cast<u32>(config));
|
||||
return;
|
||||
}
|
||||
|
||||
SetClockSpeed(iter->second);
|
||||
configs.insert_or_assign(mode, config);
|
||||
}
|
||||
|
||||
@@ -48,7 +68,7 @@ void Controller::SetFromCpuBoostMode(CpuBoostMode mode) {
|
||||
BOOST_MODE_TO_CONFIG_MAP.at(static_cast<u32>(mode)));
|
||||
}
|
||||
|
||||
PerformanceMode Controller::GetCurrentPerformanceMode() {
|
||||
PerformanceMode Controller::GetCurrentPerformanceMode() const {
|
||||
return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld;
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
void SetPerformanceConfiguration(PerformanceMode mode, PerformanceConfiguration config);
|
||||
void SetFromCpuBoostMode(CpuBoostMode mode);
|
||||
|
||||
PerformanceMode GetCurrentPerformanceMode();
|
||||
PerformanceMode GetCurrentPerformanceMode() const;
|
||||
PerformanceConfiguration GetCurrentPerformanceConfiguration(PerformanceMode mode);
|
||||
|
||||
private:
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
|
||||
namespace Service::BCAT {
|
||||
|
||||
ProgressServiceBackend::ProgressServiceBackend(std::string_view event_name) {
|
||||
auto& kernel{Core::System::GetInstance().Kernel()};
|
||||
ProgressServiceBackend::ProgressServiceBackend(Kernel::KernelCore& kernel,
|
||||
std::string_view event_name) {
|
||||
event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Automatic,
|
||||
std::string("ProgressServiceBackend:UpdateEvent:").append(event_name));
|
||||
|
||||
@@ -15,6 +15,14 @@
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/result.h"
|
||||
|
||||
namespace Core {
|
||||
class System;
|
||||
}
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
}
|
||||
|
||||
namespace Service::BCAT {
|
||||
|
||||
struct DeliveryCacheProgressImpl;
|
||||
@@ -88,7 +96,7 @@ public:
|
||||
void FinishDownload(ResultCode result);
|
||||
|
||||
private:
|
||||
explicit ProgressServiceBackend(std::string_view event_name);
|
||||
explicit ProgressServiceBackend(Kernel::KernelCore& kernel, std::string_view event_name);
|
||||
|
||||
Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent() const;
|
||||
DeliveryCacheProgressImpl& GetImpl();
|
||||
@@ -145,6 +153,6 @@ public:
|
||||
std::optional<std::vector<u8>> GetLaunchParameter(TitleIDVersion title) override;
|
||||
};
|
||||
|
||||
std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter);
|
||||
std::unique_ptr<Backend> CreateBackendFromSettings(Core::System& system, DirectoryGetter getter);
|
||||
|
||||
} // namespace Service::BCAT
|
||||
@@ -104,14 +104,15 @@ std::string GetZIPFilePath(u64 title_id) {
|
||||
|
||||
// If the error is something the user should know about (build ID mismatch, bad client version),
|
||||
// display an error.
|
||||
void HandleDownloadDisplayResult(DownloadResult res) {
|
||||
void HandleDownloadDisplayResult(const AM::Applets::AppletManager& applet_manager,
|
||||
DownloadResult res) {
|
||||
if (res == DownloadResult::Success || res == DownloadResult::NoResponse ||
|
||||
res == DownloadResult::GeneralWebError || res == DownloadResult::GeneralFSError ||
|
||||
res == DownloadResult::NoMatchTitleId || res == DownloadResult::InvalidContentType) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto& frontend{Core::System::GetInstance().GetAppletManager().GetAppletFrontendSet()};
|
||||
const auto& frontend{applet_manager.GetAppletFrontendSet()};
|
||||
frontend.error->ShowCustomErrorText(
|
||||
ResultCode(-1), "There was an error while attempting to use Boxcat.",
|
||||
DOWNLOAD_RESULT_LOG_MESSAGES[static_cast<std::size_t>(res)], [] {});
|
||||
@@ -264,12 +265,13 @@ private:
|
||||
u64 build_id;
|
||||
};
|
||||
|
||||
Boxcat::Boxcat(DirectoryGetter getter) : Backend(std::move(getter)) {}
|
||||
Boxcat::Boxcat(AM::Applets::AppletManager& applet_manager_, DirectoryGetter getter)
|
||||
: Backend(std::move(getter)), applet_manager{applet_manager_} {}
|
||||
|
||||
Boxcat::~Boxcat() = default;
|
||||
|
||||
void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
|
||||
ProgressServiceBackend& progress,
|
||||
void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGetter dir_getter,
|
||||
TitleIDVersion title, ProgressServiceBackend& progress,
|
||||
std::optional<std::string> dir_name = {}) {
|
||||
progress.SetNeedHLELock(true);
|
||||
|
||||
@@ -295,7 +297,7 @@ void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
|
||||
FileUtil::Delete(zip_path);
|
||||
}
|
||||
|
||||
HandleDownloadDisplayResult(res);
|
||||
HandleDownloadDisplayResult(applet_manager, res);
|
||||
progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE);
|
||||
return;
|
||||
}
|
||||
@@ -364,17 +366,24 @@ void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
|
||||
|
||||
bool Boxcat::Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) {
|
||||
is_syncing.exchange(true);
|
||||
std::thread([this, title, &progress] { SynchronizeInternal(dir_getter, title, progress); })
|
||||
|
||||
std::thread([this, title, &progress] {
|
||||
SynchronizeInternal(applet_manager, dir_getter, title, progress);
|
||||
})
|
||||
.detach();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Boxcat::SynchronizeDirectory(TitleIDVersion title, std::string name,
|
||||
ProgressServiceBackend& progress) {
|
||||
is_syncing.exchange(true);
|
||||
std::thread(
|
||||
[this, title, name, &progress] { SynchronizeInternal(dir_getter, title, progress, name); })
|
||||
|
||||
std::thread([this, title, name, &progress] {
|
||||
SynchronizeInternal(applet_manager, dir_getter, title, progress, name);
|
||||
})
|
||||
.detach();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -420,7 +429,7 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
|
||||
FileUtil::Delete(path);
|
||||
}
|
||||
|
||||
HandleDownloadDisplayResult(res);
|
||||
HandleDownloadDisplayResult(applet_manager, res);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include <optional>
|
||||
#include "core/hle/service/bcat/backend/backend.h"
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
class AppletManager;
|
||||
}
|
||||
|
||||
namespace Service::BCAT {
|
||||
|
||||
struct EventStatus {
|
||||
@@ -20,12 +24,13 @@ struct EventStatus {
|
||||
/// Boxcat is yuzu's custom backend implementation of Nintendo's BCAT service. It is free to use and
|
||||
/// doesn't require a switch or nintendo account. The content is controlled by the yuzu team.
|
||||
class Boxcat final : public Backend {
|
||||
friend void SynchronizeInternal(DirectoryGetter dir_getter, TitleIDVersion title,
|
||||
friend void SynchronizeInternal(AM::Applets::AppletManager& applet_manager,
|
||||
DirectoryGetter dir_getter, TitleIDVersion title,
|
||||
ProgressServiceBackend& progress,
|
||||
std::optional<std::string> dir_name);
|
||||
|
||||
public:
|
||||
explicit Boxcat(DirectoryGetter getter);
|
||||
explicit Boxcat(AM::Applets::AppletManager& applet_manager_, DirectoryGetter getter);
|
||||
~Boxcat() override;
|
||||
|
||||
bool Synchronize(TitleIDVersion title, ProgressServiceBackend& progress) override;
|
||||
@@ -53,6 +58,7 @@ private:
|
||||
|
||||
class Client;
|
||||
std::unique_ptr<Client> client;
|
||||
AM::Applets::AppletManager& applet_manager;
|
||||
};
|
||||
|
||||
} // namespace Service::BCAT
|
||||
@@ -125,7 +125,11 @@ private:
|
||||
class IBcatService final : public ServiceFramework<IBcatService> {
|
||||
public:
|
||||
explicit IBcatService(Core::System& system_, Backend& backend_)
|
||||
: ServiceFramework("IBcatService"), system{system_}, backend{backend_} {
|
||||
: ServiceFramework("IBcatService"), system{system_}, backend{backend_},
|
||||
progress{{
|
||||
ProgressServiceBackend{system_.Kernel(), "Normal"},
|
||||
ProgressServiceBackend{system_.Kernel(), "Directory"},
|
||||
}} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{10100, &IBcatService::RequestSyncDeliveryCache, "RequestSyncDeliveryCache"},
|
||||
@@ -249,10 +253,7 @@ private:
|
||||
Core::System& system;
|
||||
Backend& backend;
|
||||
|
||||
std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress{
|
||||
ProgressServiceBackend{"Normal"},
|
||||
ProgressServiceBackend{"Directory"},
|
||||
};
|
||||
std::array<ProgressServiceBackend, static_cast<std::size_t>(SyncType::Count)> progress;
|
||||
};
|
||||
|
||||
void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
|
||||
@@ -557,12 +558,12 @@ void Module::Interface::CreateDeliveryCacheStorageServiceWithApplicationId(
|
||||
rb.PushIpcInterface<IDeliveryCacheStorageService>(fsc.GetBCATDirectory(title_id));
|
||||
}
|
||||
|
||||
std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter) {
|
||||
const auto backend = Settings::values.bcat_backend;
|
||||
|
||||
std::unique_ptr<Backend> CreateBackendFromSettings([[maybe_unused]] Core::System& system,
|
||||
DirectoryGetter getter) {
|
||||
#ifdef YUZU_ENABLE_BOXCAT
|
||||
if (backend == "boxcat")
|
||||
return std::make_unique<Boxcat>(std::move(getter));
|
||||
if (Settings::values.bcat_backend == "boxcat") {
|
||||
return std::make_unique<Boxcat>(system.GetAppletManager(), std::move(getter));
|
||||
}
|
||||
#endif
|
||||
|
||||
return std::make_unique<NullBackend>(std::move(getter));
|
||||
@@ -571,7 +572,8 @@ std::unique_ptr<Backend> CreateBackendFromSettings(DirectoryGetter getter) {
|
||||
Module::Interface::Interface(Core::System& system_, std::shared_ptr<Module> module_,
|
||||
FileSystem::FileSystemController& fsc_, const char* name)
|
||||
: ServiceFramework(name), fsc{fsc_}, module{std::move(module_)},
|
||||
backend{CreateBackendFromSettings([&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })},
|
||||
backend{CreateBackendFromSettings(system_,
|
||||
[&fsc_](u64 tid) { return fsc_.GetBCATDirectory(tid); })},
|
||||
system{system_} {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
BufferQueue::BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id)
|
||||
: id(id), layer_id(layer_id) {
|
||||
buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"BufferQueue NativeHandle");
|
||||
}
|
||||
|
||||
@@ -15,6 +15,10 @@
|
||||
#include "core/hle/kernel/writable_event.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
|
||||
namespace Kernel {
|
||||
class KernelCore;
|
||||
}
|
||||
|
||||
namespace Service::NVFlinger {
|
||||
|
||||
struct IGBPBuffer {
|
||||
@@ -44,7 +48,7 @@ public:
|
||||
NativeWindowFormat = 2,
|
||||
};
|
||||
|
||||
BufferQueue(u32 id, u64 layer_id);
|
||||
explicit BufferQueue(Kernel::KernelCore& kernel, u32 id, u64 layer_id);
|
||||
~BufferQueue();
|
||||
|
||||
enum class BufferTransformFlags : u32 {
|
||||
|
||||
@@ -83,7 +83,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
|
||||
|
||||
const u64 layer_id = next_layer_id++;
|
||||
const u32 buffer_queue_id = next_buffer_queue_id++;
|
||||
buffer_queues.emplace_back(buffer_queue_id, layer_id);
|
||||
buffer_queues.emplace_back(system.Kernel(), buffer_queue_id, layer_id);
|
||||
display->CreateLayer(layer_id, buffer_queues.back());
|
||||
return layer_id;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
|
||||
|
||||
StandardVmCallbacks::StandardVmCallbacks(const Core::System& system,
|
||||
const CheatProcessMetadata& metadata)
|
||||
: system(system), metadata(metadata) {}
|
||||
: metadata(metadata), system(system) {}
|
||||
|
||||
StandardVmCallbacks::~StandardVmCallbacks() = default;
|
||||
|
||||
@@ -176,9 +176,8 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
|
||||
|
||||
CheatEngine::CheatEngine(Core::System& system, std::vector<CheatEntry> cheats,
|
||||
const std::array<u8, 0x20>& build_id)
|
||||
: system{system}, core_timing{system.CoreTiming()}, vm{std::make_unique<StandardVmCallbacks>(
|
||||
system, metadata)},
|
||||
cheats(std::move(cheats)) {
|
||||
: vm{std::make_unique<StandardVmCallbacks>(system, metadata)},
|
||||
cheats(std::move(cheats)), core_timing{system.CoreTiming()}, system{system} {
|
||||
metadata.main_nso_build_id = build_id;
|
||||
}
|
||||
|
||||
|
||||
@@ -1133,8 +1133,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
|
||||
case SaveRestoreRegisterOpType::ClearRegs:
|
||||
case SaveRestoreRegisterOpType::Restore:
|
||||
default:
|
||||
src = registers.data();
|
||||
dst = saved_values.data();
|
||||
src = saved_values.data();
|
||||
dst = registers.data();
|
||||
break;
|
||||
}
|
||||
for (std::size_t i = 0; i < NumRegisters; i++) {
|
||||
|
||||
@@ -34,6 +34,10 @@ struct SamplerDescriptor {
|
||||
return raw == rhs.raw;
|
||||
}
|
||||
|
||||
bool operator!=(const SamplerDescriptor& rhs) const noexcept {
|
||||
return raw != rhs.raw;
|
||||
}
|
||||
|
||||
static SamplerDescriptor FromTicTexture(Tegra::Texture::TextureType tic_texture_type) {
|
||||
SamplerDescriptor result;
|
||||
switch (tic_texture_type) {
|
||||
@@ -105,7 +109,7 @@ static_assert(std::is_trivially_copyable_v<SamplerDescriptor>);
|
||||
|
||||
class ConstBufferEngineInterface {
|
||||
public:
|
||||
virtual ~ConstBufferEngineInterface() {}
|
||||
virtual ~ConstBufferEngineInterface() = default;
|
||||
virtual u32 AccessConstBuffer32(ShaderType stage, u64 const_buffer, u64 offset) const = 0;
|
||||
virtual SamplerDescriptor AccessBoundSampler(ShaderType stage, u64 offset) const = 0;
|
||||
virtual SamplerDescriptor AccessBindlessSampler(ShaderType stage, u64 const_buffer,
|
||||
|
||||
@@ -28,6 +28,13 @@ void Fermi2D::CallMethod(const GPU::MethodCall& method_call) {
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<u32, u32> DelimitLine(u32 src_1, u32 src_2, u32 dst_1, u32 dst_2, u32 src_line) {
|
||||
const u32 line_a = src_2 - src_1;
|
||||
const u32 line_b = dst_2 - dst_1;
|
||||
const u32 excess = std::max<s32>(0, line_a - src_line + src_1);
|
||||
return {line_b - (excess * line_b) / line_a, excess};
|
||||
}
|
||||
|
||||
void Fermi2D::HandleSurfaceCopy() {
|
||||
LOG_DEBUG(HW_GPU, "Requested a surface copy with operation {}",
|
||||
static_cast<u32>(regs.operation));
|
||||
@@ -47,20 +54,27 @@ void Fermi2D::HandleSurfaceCopy() {
|
||||
src_blit_x2 = static_cast<u32>((regs.blit_src_x >> 32) + regs.blit_dst_width);
|
||||
src_blit_y2 = static_cast<u32>((regs.blit_src_y >> 32) + regs.blit_dst_height);
|
||||
}
|
||||
const u32 dst_blit_x2 = regs.blit_dst_x + regs.blit_dst_width;
|
||||
const u32 dst_blit_y2 = regs.blit_dst_x + regs.blit_dst_height;
|
||||
const u32 excess_src_x2 = std::max<s32>(0, dst_blit_x2 - regs.dst.width);
|
||||
const u32 excess_src_y2 = std::max<s32>(0, dst_blit_y2 - regs.dst.height);
|
||||
const u32 excess_dst_x2 = std::max<s32>(0, src_blit_x2 - regs.src.width);
|
||||
const u32 excess_dst_y2 = std::max<s32>(0, src_blit_y2 - regs.src.height);
|
||||
|
||||
const Common::Rectangle<u32> src_rect{
|
||||
src_blit_x1, src_blit_y1, std::min<u32>(regs.src.width, src_blit_x2) - excess_src_x2,
|
||||
std::min<u32>(regs.src.height, src_blit_y2) - excess_src_y2};
|
||||
const Common::Rectangle<u32> dst_rect{
|
||||
regs.blit_dst_x, regs.blit_dst_y,
|
||||
std::min<u32>(regs.dst.width, dst_blit_x2) - excess_dst_x2,
|
||||
std::min<u32>(regs.dst.height, dst_blit_y2) - excess_dst_y2};
|
||||
u32 dst_blit_x2 = regs.blit_dst_x + regs.blit_dst_width;
|
||||
u32 dst_blit_y2 = regs.blit_dst_y + regs.blit_dst_height;
|
||||
const auto [new_dst_w, src_excess_x] =
|
||||
DelimitLine(src_blit_x1, src_blit_x2, regs.blit_dst_x, dst_blit_x2, regs.src.width);
|
||||
const auto [new_dst_h, src_excess_y] =
|
||||
DelimitLine(src_blit_y1, src_blit_y2, regs.blit_dst_y, dst_blit_y2, regs.src.height);
|
||||
dst_blit_x2 = new_dst_w + regs.blit_dst_x;
|
||||
src_blit_x2 = src_blit_x2 - src_excess_x;
|
||||
dst_blit_y2 = new_dst_h + regs.blit_dst_y;
|
||||
src_blit_y2 = src_blit_y2 - src_excess_y;
|
||||
const auto [new_src_w, dst_excess_x] =
|
||||
DelimitLine(regs.blit_dst_x, dst_blit_x2, src_blit_x1, src_blit_x2, regs.dst.width);
|
||||
const auto [new_src_h, dst_excess_y] =
|
||||
DelimitLine(regs.blit_dst_y, dst_blit_y2, src_blit_y1, src_blit_y2, regs.dst.height);
|
||||
src_blit_x2 = new_src_w + src_blit_x1;
|
||||
dst_blit_x2 = dst_blit_x2 - dst_excess_x;
|
||||
src_blit_y2 = new_src_h + src_blit_y1;
|
||||
dst_blit_y2 = dst_blit_y2 - dst_excess_y;
|
||||
const Common::Rectangle<u32> src_rect{src_blit_x1, src_blit_y1, src_blit_x2, src_blit_y2};
|
||||
const Common::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y, dst_blit_x2,
|
||||
dst_blit_y2};
|
||||
Config copy_config;
|
||||
copy_config.operation = regs.operation;
|
||||
copy_config.filter = regs.blit_control.filter;
|
||||
|
||||
@@ -101,7 +101,8 @@ void Maxwell3D::InitializeRegisterDefaults() {
|
||||
#define DIRTY_REGS_POS(field_name) (offsetof(Maxwell3D::DirtyRegs, field_name))
|
||||
|
||||
void Maxwell3D::InitDirtySettings() {
|
||||
const auto set_block = [this](const u32 start, const u32 range, const u8 position) {
|
||||
const auto set_block = [this](const std::size_t start, const std::size_t range,
|
||||
const u8 position) {
|
||||
const auto start_itr = dirty_pointers.begin() + start;
|
||||
const auto end_itr = start_itr + range;
|
||||
std::fill(start_itr, end_itr, position);
|
||||
@@ -478,7 +479,7 @@ void Maxwell3D::CallMethodFromMME(const GPU::MethodCall& method_call) {
|
||||
}
|
||||
|
||||
void Maxwell3D::FlushMMEInlineDraw() {
|
||||
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?");
|
||||
ASSERT(mme_draw.instance_count == mme_draw.gl_end_count);
|
||||
|
||||
@@ -339,7 +339,7 @@ void GPU::ProcessSemaphoreTriggerMethod() {
|
||||
block.sequence = regs.semaphore_sequence;
|
||||
// TODO(Kmather73): Generate a real GPU timestamp and write it here instead of
|
||||
// CoreTiming
|
||||
block.timestamp = Core::System::GetInstance().CoreTiming().GetTicks();
|
||||
block.timestamp = system.CoreTiming().GetTicks();
|
||||
memory_manager->WriteBlock(regs.semaphore_address.SemaphoreAddress(), &block,
|
||||
sizeof(block));
|
||||
} else {
|
||||
|
||||
@@ -11,6 +11,77 @@
|
||||
MICROPROFILE_DEFINE(MacroInterp, "GPU", "Execute macro interpreter", MP_RGB(128, 128, 192));
|
||||
|
||||
namespace Tegra {
|
||||
namespace {
|
||||
enum class Operation : u32 {
|
||||
ALU = 0,
|
||||
AddImmediate = 1,
|
||||
ExtractInsert = 2,
|
||||
ExtractShiftLeftImmediate = 3,
|
||||
ExtractShiftLeftRegister = 4,
|
||||
Read = 5,
|
||||
Unused = 6, // This operation doesn't seem to be a valid encoding.
|
||||
Branch = 7,
|
||||
};
|
||||
} // Anonymous namespace
|
||||
|
||||
enum class MacroInterpreter::ALUOperation : u32 {
|
||||
Add = 0,
|
||||
AddWithCarry = 1,
|
||||
Subtract = 2,
|
||||
SubtractWithBorrow = 3,
|
||||
// Operations 4-7 don't seem to be valid encodings.
|
||||
Xor = 8,
|
||||
Or = 9,
|
||||
And = 10,
|
||||
AndNot = 11,
|
||||
Nand = 12
|
||||
};
|
||||
|
||||
enum class MacroInterpreter::ResultOperation : u32 {
|
||||
IgnoreAndFetch = 0,
|
||||
Move = 1,
|
||||
MoveAndSetMethod = 2,
|
||||
FetchAndSend = 3,
|
||||
MoveAndSend = 4,
|
||||
FetchAndSetMethod = 5,
|
||||
MoveAndSetMethodFetchAndSend = 6,
|
||||
MoveAndSetMethodSend = 7
|
||||
};
|
||||
|
||||
enum class MacroInterpreter::BranchCondition : u32 {
|
||||
Zero = 0,
|
||||
NotZero = 1,
|
||||
};
|
||||
|
||||
union MacroInterpreter::Opcode {
|
||||
u32 raw;
|
||||
BitField<0, 3, Operation> operation;
|
||||
BitField<4, 3, ResultOperation> result_operation;
|
||||
BitField<4, 1, BranchCondition> branch_condition;
|
||||
// If set on a branch, then the branch doesn't have a delay slot.
|
||||
BitField<5, 1, u32> branch_annul;
|
||||
BitField<7, 1, u32> is_exit;
|
||||
BitField<8, 3, u32> dst;
|
||||
BitField<11, 3, u32> src_a;
|
||||
BitField<14, 3, u32> src_b;
|
||||
// The signed immediate overlaps the second source operand and the alu operation.
|
||||
BitField<14, 18, s32> immediate;
|
||||
|
||||
BitField<17, 5, ALUOperation> alu_operation;
|
||||
|
||||
// Bitfield instructions data
|
||||
BitField<17, 5, u32> bf_src_bit;
|
||||
BitField<22, 5, u32> bf_size;
|
||||
BitField<27, 5, u32> bf_dst_bit;
|
||||
|
||||
u32 GetBitfieldMask() const {
|
||||
return (1 << bf_size) - 1;
|
||||
}
|
||||
|
||||
s32 GetBranchTarget() const {
|
||||
return static_cast<s32>(immediate * sizeof(u32));
|
||||
}
|
||||
};
|
||||
|
||||
MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/bit_field.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -28,75 +27,11 @@ public:
|
||||
void Execute(u32 offset, std::size_t num_parameters, const u32* parameters);
|
||||
|
||||
private:
|
||||
enum class Operation : u32 {
|
||||
ALU = 0,
|
||||
AddImmediate = 1,
|
||||
ExtractInsert = 2,
|
||||
ExtractShiftLeftImmediate = 3,
|
||||
ExtractShiftLeftRegister = 4,
|
||||
Read = 5,
|
||||
Unused = 6, // This operation doesn't seem to be a valid encoding.
|
||||
Branch = 7,
|
||||
};
|
||||
enum class ALUOperation : u32;
|
||||
enum class BranchCondition : u32;
|
||||
enum class ResultOperation : u32;
|
||||
|
||||
enum class ALUOperation : u32 {
|
||||
Add = 0,
|
||||
AddWithCarry = 1,
|
||||
Subtract = 2,
|
||||
SubtractWithBorrow = 3,
|
||||
// Operations 4-7 don't seem to be valid encodings.
|
||||
Xor = 8,
|
||||
Or = 9,
|
||||
And = 10,
|
||||
AndNot = 11,
|
||||
Nand = 12
|
||||
};
|
||||
|
||||
enum class ResultOperation : u32 {
|
||||
IgnoreAndFetch = 0,
|
||||
Move = 1,
|
||||
MoveAndSetMethod = 2,
|
||||
FetchAndSend = 3,
|
||||
MoveAndSend = 4,
|
||||
FetchAndSetMethod = 5,
|
||||
MoveAndSetMethodFetchAndSend = 6,
|
||||
MoveAndSetMethodSend = 7
|
||||
};
|
||||
|
||||
enum class BranchCondition : u32 {
|
||||
Zero = 0,
|
||||
NotZero = 1,
|
||||
};
|
||||
|
||||
union Opcode {
|
||||
u32 raw;
|
||||
BitField<0, 3, Operation> operation;
|
||||
BitField<4, 3, ResultOperation> result_operation;
|
||||
BitField<4, 1, BranchCondition> branch_condition;
|
||||
BitField<5, 1, u32>
|
||||
branch_annul; // If set on a branch, then the branch doesn't have a delay slot.
|
||||
BitField<7, 1, u32> is_exit;
|
||||
BitField<8, 3, u32> dst;
|
||||
BitField<11, 3, u32> src_a;
|
||||
BitField<14, 3, u32> src_b;
|
||||
// The signed immediate overlaps the second source operand and the alu operation.
|
||||
BitField<14, 18, s32> immediate;
|
||||
|
||||
BitField<17, 5, ALUOperation> alu_operation;
|
||||
|
||||
// Bitfield instructions data
|
||||
BitField<17, 5, u32> bf_src_bit;
|
||||
BitField<22, 5, u32> bf_size;
|
||||
BitField<27, 5, u32> bf_dst_bit;
|
||||
|
||||
u32 GetBitfieldMask() const {
|
||||
return (1 << bf_size) - 1;
|
||||
}
|
||||
|
||||
s32 GetBranchTarget() const {
|
||||
return static_cast<s32>(immediate * sizeof(u32));
|
||||
}
|
||||
};
|
||||
union Opcode;
|
||||
|
||||
union MethodAddress {
|
||||
u32 raw;
|
||||
@@ -149,9 +84,10 @@ private:
|
||||
|
||||
Engines::Maxwell3D& maxwell3d;
|
||||
|
||||
u32 pc; ///< Current program counter
|
||||
std::optional<u32>
|
||||
delayed_pc; ///< Program counter to execute at after the delay slot is executed.
|
||||
/// Current program counter
|
||||
u32 pc;
|
||||
/// Program counter to execute at after the delay slot is executed.
|
||||
std::optional<u32> delayed_pc;
|
||||
|
||||
static constexpr std::size_t NumMacroRegisters = 8;
|
||||
|
||||
|
||||
@@ -1127,7 +1127,7 @@ private:
|
||||
for (const auto& variant : extras) {
|
||||
if (const auto argument = std::get_if<TextureArgument>(&variant)) {
|
||||
expr += GenerateTextureArgument(*argument);
|
||||
} else if (std::get_if<TextureAoffi>(&variant)) {
|
||||
} else if (std::holds_alternative<TextureAoffi>(variant)) {
|
||||
expr += GenerateTextureAoffi(meta->aoffi);
|
||||
} else {
|
||||
UNREACHABLE();
|
||||
@@ -1137,8 +1137,8 @@ private:
|
||||
return expr + ')';
|
||||
}
|
||||
|
||||
std::string GenerateTextureArgument(TextureArgument argument) {
|
||||
const auto [type, operand] = argument;
|
||||
std::string GenerateTextureArgument(const TextureArgument& argument) {
|
||||
const auto& [type, operand] = argument;
|
||||
if (operand == nullptr) {
|
||||
return {};
|
||||
}
|
||||
@@ -1214,7 +1214,7 @@ private:
|
||||
|
||||
std::string BuildImageValues(Operation operation) {
|
||||
constexpr std::array constructors{"uint", "uvec2", "uvec3", "uvec4"};
|
||||
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
const auto& meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
|
||||
const std::size_t values_count{meta.values.size()};
|
||||
std::string expr = fmt::format("{}(", constructors.at(values_count - 1));
|
||||
@@ -1759,14 +1759,14 @@ private:
|
||||
return {"0", Type::Int};
|
||||
}
|
||||
|
||||
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
const auto& meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
return {fmt::format("imageLoad({}, {}){}", GetImage(meta.image),
|
||||
BuildIntegerCoordinates(operation), GetSwizzle(meta.element)),
|
||||
Type::Uint};
|
||||
}
|
||||
|
||||
Expression ImageStore(Operation operation) {
|
||||
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
const auto& meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
code.AddLine("imageStore({}, {}, {});", GetImage(meta.image),
|
||||
BuildIntegerCoordinates(operation), BuildImageValues(operation));
|
||||
return {};
|
||||
@@ -1774,7 +1774,7 @@ private:
|
||||
|
||||
template <const std::string_view& opname>
|
||||
Expression AtomicImage(Operation operation) {
|
||||
const auto meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
const auto& meta{std::get<MetaImage>(operation.GetMeta())};
|
||||
ASSERT(meta.values.size() == 1);
|
||||
|
||||
return {fmt::format("imageAtomic{}({}, {}, {})", opname, GetImage(meta.image),
|
||||
@@ -2225,7 +2225,7 @@ private:
|
||||
code.AddLine("#ifdef SAMPLER_{}_IS_BUFFER", sampler.GetIndex());
|
||||
}
|
||||
|
||||
std::string GetDeclarationWithSuffix(u32 index, const std::string& name) const {
|
||||
std::string GetDeclarationWithSuffix(u32 index, std::string_view name) const {
|
||||
return fmt::format("{}_{}_{}", name, index, suffix);
|
||||
}
|
||||
|
||||
@@ -2250,17 +2250,15 @@ private:
|
||||
ShaderWriter code;
|
||||
};
|
||||
|
||||
static constexpr std::string_view flow_var = "flow_var_";
|
||||
|
||||
std::string GetFlowVariable(u32 i) {
|
||||
return fmt::format("{}{}", flow_var, i);
|
||||
return fmt::format("flow_var_{}", i);
|
||||
}
|
||||
|
||||
class ExprDecompiler {
|
||||
public:
|
||||
explicit ExprDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
|
||||
|
||||
void operator()(VideoCommon::Shader::ExprAnd& expr) {
|
||||
void operator()(const ExprAnd& expr) {
|
||||
inner += "( ";
|
||||
std::visit(*this, *expr.operand1);
|
||||
inner += " && ";
|
||||
@@ -2268,7 +2266,7 @@ public:
|
||||
inner += ')';
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ExprOr& expr) {
|
||||
void operator()(const ExprOr& expr) {
|
||||
inner += "( ";
|
||||
std::visit(*this, *expr.operand1);
|
||||
inner += " || ";
|
||||
@@ -2276,17 +2274,17 @@ public:
|
||||
inner += ')';
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ExprNot& expr) {
|
||||
void operator()(const ExprNot& expr) {
|
||||
inner += '!';
|
||||
std::visit(*this, *expr.operand1);
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ExprPredicate& expr) {
|
||||
void operator()(const ExprPredicate& expr) {
|
||||
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
|
||||
inner += decomp.GetPredicate(pred);
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ExprCondCode& expr) {
|
||||
void operator()(const ExprCondCode& expr) {
|
||||
const Node cc = decomp.ir.GetConditionCode(expr.cc);
|
||||
std::string target;
|
||||
|
||||
@@ -2308,11 +2306,11 @@ public:
|
||||
inner += target;
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ExprVar& expr) {
|
||||
void operator()(const ExprVar& expr) {
|
||||
inner += GetFlowVariable(expr.var_index);
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ExprBoolean& expr) {
|
||||
void operator()(const ExprBoolean& expr) {
|
||||
inner += expr.value ? "true" : "false";
|
||||
}
|
||||
|
||||
@@ -2321,7 +2319,7 @@ public:
|
||||
"( ftou(" + decomp.GetRegister(expr.gpr) + ") == " + std::to_string(expr.value) + ')';
|
||||
}
|
||||
|
||||
std::string& GetResult() {
|
||||
const std::string& GetResult() const {
|
||||
return inner;
|
||||
}
|
||||
|
||||
@@ -2334,7 +2332,7 @@ class ASTDecompiler {
|
||||
public:
|
||||
explicit ASTDecompiler(GLSLDecompiler& decomp) : decomp{decomp} {}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTProgram& ast) {
|
||||
void operator()(const ASTProgram& ast) {
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
Visit(current);
|
||||
@@ -2342,7 +2340,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTIfThen& ast) {
|
||||
void operator()(const ASTIfThen& ast) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
decomp.code.AddLine("if ({}) {{", expr_parser.GetResult());
|
||||
@@ -2356,7 +2354,7 @@ public:
|
||||
decomp.code.AddLine("}}");
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTIfElse& ast) {
|
||||
void operator()(const ASTIfElse& ast) {
|
||||
decomp.code.AddLine("else {{");
|
||||
decomp.code.scope++;
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
@@ -2368,29 +2366,29 @@ public:
|
||||
decomp.code.AddLine("}}");
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) {
|
||||
void operator()([[maybe_unused]] const ASTBlockEncoded& ast) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) {
|
||||
void operator()(const ASTBlockDecoded& ast) {
|
||||
decomp.VisitBlock(ast.nodes);
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTVarSet& ast) {
|
||||
void operator()(const ASTVarSet& ast) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
decomp.code.AddLine("{} = {};", GetFlowVariable(ast.index), expr_parser.GetResult());
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTLabel& ast) {
|
||||
void operator()(const ASTLabel& ast) {
|
||||
decomp.code.AddLine("// Label_{}:", ast.index);
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTGoto& ast) {
|
||||
void operator()([[maybe_unused]] const ASTGoto& ast) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTDoWhile& ast) {
|
||||
void operator()(const ASTDoWhile& ast) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
decomp.code.AddLine("do {{");
|
||||
@@ -2404,7 +2402,7 @@ public:
|
||||
decomp.code.AddLine("}} while({});", expr_parser.GetResult());
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTReturn& ast) {
|
||||
void operator()(const ASTReturn& ast) {
|
||||
const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
|
||||
if (!is_true) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
@@ -2424,7 +2422,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTBreak& ast) {
|
||||
void operator()(const ASTBreak& ast) {
|
||||
const bool is_true = VideoCommon::Shader::ExprIsTrue(ast.condition);
|
||||
if (!is_true) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
@@ -2439,7 +2437,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(VideoCommon::Shader::ASTNode& node) {
|
||||
void Visit(const ASTNode& node) {
|
||||
std::visit(*this, *node->GetInnerData());
|
||||
}
|
||||
|
||||
@@ -2452,9 +2450,9 @@ void GLSLDecompiler::DecompileAST() {
|
||||
for (u32 i = 0; i < num_flow_variables; i++) {
|
||||
code.AddLine("bool {} = false;", GetFlowVariable(i));
|
||||
}
|
||||
|
||||
ASTDecompiler decompiler{*this};
|
||||
VideoCommon::Shader::ASTNode program = ir.GetASTProgram();
|
||||
decompiler.Visit(program);
|
||||
decompiler.Visit(ir.GetASTProgram());
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
@@ -1648,32 +1648,32 @@ class ExprDecompiler {
|
||||
public:
|
||||
explicit ExprDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {}
|
||||
|
||||
Id operator()(VideoCommon::Shader::ExprAnd& expr) {
|
||||
Id operator()(const ExprAnd& expr) {
|
||||
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
|
||||
const Id op1 = Visit(expr.operand1);
|
||||
const Id op2 = Visit(expr.operand2);
|
||||
return decomp.Emit(decomp.OpLogicalAnd(type_def, op1, op2));
|
||||
}
|
||||
|
||||
Id operator()(VideoCommon::Shader::ExprOr& expr) {
|
||||
Id operator()(const ExprOr& expr) {
|
||||
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
|
||||
const Id op1 = Visit(expr.operand1);
|
||||
const Id op2 = Visit(expr.operand2);
|
||||
return decomp.Emit(decomp.OpLogicalOr(type_def, op1, op2));
|
||||
}
|
||||
|
||||
Id operator()(VideoCommon::Shader::ExprNot& expr) {
|
||||
Id operator()(const ExprNot& expr) {
|
||||
const Id type_def = decomp.GetTypeDefinition(Type::Bool);
|
||||
const Id op1 = Visit(expr.operand1);
|
||||
return decomp.Emit(decomp.OpLogicalNot(type_def, op1));
|
||||
}
|
||||
|
||||
Id operator()(VideoCommon::Shader::ExprPredicate& expr) {
|
||||
Id operator()(const ExprPredicate& expr) {
|
||||
const auto pred = static_cast<Tegra::Shader::Pred>(expr.predicate);
|
||||
return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.predicates.at(pred)));
|
||||
}
|
||||
|
||||
Id operator()(VideoCommon::Shader::ExprCondCode& expr) {
|
||||
Id operator()(const ExprCondCode& expr) {
|
||||
const Node cc = decomp.ir.GetConditionCode(expr.cc);
|
||||
Id target;
|
||||
|
||||
@@ -1693,22 +1693,22 @@ public:
|
||||
return decomp.Emit(decomp.OpLoad(decomp.t_bool, target));
|
||||
}
|
||||
|
||||
Id operator()(VideoCommon::Shader::ExprVar& expr) {
|
||||
Id operator()(const ExprVar& expr) {
|
||||
return decomp.Emit(decomp.OpLoad(decomp.t_bool, decomp.flow_variables.at(expr.var_index)));
|
||||
}
|
||||
|
||||
Id operator()(VideoCommon::Shader::ExprBoolean& expr) {
|
||||
Id operator()(const ExprBoolean& expr) {
|
||||
return expr.value ? decomp.v_true : decomp.v_false;
|
||||
}
|
||||
|
||||
Id operator()(VideoCommon::Shader::ExprGprEqual& expr) {
|
||||
Id operator()(const ExprGprEqual& expr) {
|
||||
const Id target = decomp.Constant(decomp.t_uint, expr.value);
|
||||
const Id gpr = decomp.BitcastTo<Type::Uint>(
|
||||
decomp.Emit(decomp.OpLoad(decomp.t_float, decomp.registers.at(expr.gpr))));
|
||||
return decomp.Emit(decomp.OpLogicalEqual(decomp.t_uint, gpr, target));
|
||||
}
|
||||
|
||||
Id Visit(VideoCommon::Shader::Expr& node) {
|
||||
Id Visit(const Expr& node) {
|
||||
return std::visit(*this, *node);
|
||||
}
|
||||
|
||||
@@ -1720,7 +1720,7 @@ class ASTDecompiler {
|
||||
public:
|
||||
explicit ASTDecompiler(SPIRVDecompiler& decomp) : decomp{decomp} {}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTProgram& ast) {
|
||||
void operator()(const ASTProgram& ast) {
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
Visit(current);
|
||||
@@ -1728,7 +1728,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTIfThen& ast) {
|
||||
void operator()(const ASTIfThen& ast) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
const Id condition = expr_parser.Visit(ast.condition);
|
||||
const Id then_label = decomp.OpLabel();
|
||||
@@ -1745,33 +1745,33 @@ public:
|
||||
decomp.Emit(endif_label);
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTIfElse& ast) {
|
||||
void operator()([[maybe_unused]] const ASTIfElse& ast) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTBlockEncoded& ast) {
|
||||
void operator()([[maybe_unused]] const ASTBlockEncoded& ast) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTBlockDecoded& ast) {
|
||||
void operator()(const ASTBlockDecoded& ast) {
|
||||
decomp.VisitBasicBlock(ast.nodes);
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTVarSet& ast) {
|
||||
void operator()(const ASTVarSet& ast) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
const Id condition = expr_parser.Visit(ast.condition);
|
||||
decomp.Emit(decomp.OpStore(decomp.flow_variables.at(ast.index), condition));
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTLabel& ast) {
|
||||
void operator()([[maybe_unused]] const ASTLabel& ast) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTGoto& ast) {
|
||||
void operator()([[maybe_unused]] const ASTGoto& ast) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTDoWhile& ast) {
|
||||
void operator()(const ASTDoWhile& ast) {
|
||||
const Id loop_label = decomp.OpLabel();
|
||||
const Id endloop_label = decomp.OpLabel();
|
||||
const Id loop_start_block = decomp.OpLabel();
|
||||
@@ -1794,7 +1794,7 @@ public:
|
||||
decomp.Emit(endloop_label);
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTReturn& ast) {
|
||||
void operator()(const ASTReturn& ast) {
|
||||
if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
const Id condition = expr_parser.Visit(ast.condition);
|
||||
@@ -1824,7 +1824,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void operator()(VideoCommon::Shader::ASTBreak& ast) {
|
||||
void operator()(const ASTBreak& ast) {
|
||||
if (!VideoCommon::Shader::ExprIsTrue(ast.condition)) {
|
||||
ExprDecompiler expr_parser{decomp};
|
||||
const Id condition = expr_parser.Visit(ast.condition);
|
||||
@@ -1844,7 +1844,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void Visit(VideoCommon::Shader::ASTNode& node) {
|
||||
void Visit(const ASTNode& node) {
|
||||
std::visit(*this, *node->GetInnerData());
|
||||
}
|
||||
|
||||
@@ -1860,9 +1860,11 @@ void SPIRVDecompiler::DecompileAST() {
|
||||
Name(id, fmt::format("flow_var_{}", i));
|
||||
flow_variables.emplace(i, AddGlobalVariable(id));
|
||||
}
|
||||
|
||||
const ASTNode program = ir.GetASTProgram();
|
||||
ASTDecompiler decompiler{*this};
|
||||
VideoCommon::Shader::ASTNode program = ir.GetASTProgram();
|
||||
decompiler.Visit(program);
|
||||
|
||||
const Id next_block = OpLabel();
|
||||
Emit(OpBranch(next_block));
|
||||
Emit(next_block);
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
@@ -233,7 +236,8 @@ public:
|
||||
return inner;
|
||||
}
|
||||
|
||||
std::string inner{};
|
||||
private:
|
||||
std::string inner;
|
||||
};
|
||||
|
||||
class ASTPrinter {
|
||||
@@ -253,7 +257,7 @@ public:
|
||||
void operator()(const ASTIfThen& ast) {
|
||||
ExprPrinter expr_parser{};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
inner += Ident() + "if (" + expr_parser.GetResult() + ") {\n";
|
||||
inner += fmt::format("{}if ({}) {{\n", Indent(), expr_parser.GetResult());
|
||||
scope++;
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
@@ -261,11 +265,13 @@ public:
|
||||
current = current->GetNext();
|
||||
}
|
||||
scope--;
|
||||
inner += Ident() + "}\n";
|
||||
inner += fmt::format("{}}}\n", Indent());
|
||||
}
|
||||
|
||||
void operator()(const ASTIfElse& ast) {
|
||||
inner += Ident() + "else {\n";
|
||||
inner += Indent();
|
||||
inner += "else {\n";
|
||||
|
||||
scope++;
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
@@ -273,40 +279,41 @@ public:
|
||||
current = current->GetNext();
|
||||
}
|
||||
scope--;
|
||||
inner += Ident() + "}\n";
|
||||
|
||||
inner += Indent();
|
||||
inner += "}\n";
|
||||
}
|
||||
|
||||
void operator()(const ASTBlockEncoded& ast) {
|
||||
inner += Ident() + "Block(" + std::to_string(ast.start) + ", " + std::to_string(ast.end) +
|
||||
");\n";
|
||||
inner += fmt::format("{}Block({}, {});\n", Indent(), ast.start, ast.end);
|
||||
}
|
||||
|
||||
void operator()(const ASTBlockDecoded& ast) {
|
||||
inner += Ident() + "Block;\n";
|
||||
void operator()([[maybe_unused]] const ASTBlockDecoded& ast) {
|
||||
inner += Indent();
|
||||
inner += "Block;\n";
|
||||
}
|
||||
|
||||
void operator()(const ASTVarSet& ast) {
|
||||
ExprPrinter expr_parser{};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
inner +=
|
||||
Ident() + "V" + std::to_string(ast.index) + " := " + expr_parser.GetResult() + ";\n";
|
||||
inner += fmt::format("{}V{} := {};\n", Indent(), ast.index, expr_parser.GetResult());
|
||||
}
|
||||
|
||||
void operator()(const ASTLabel& ast) {
|
||||
inner += "Label_" + std::to_string(ast.index) + ":\n";
|
||||
inner += fmt::format("Label_{}:\n", ast.index);
|
||||
}
|
||||
|
||||
void operator()(const ASTGoto& ast) {
|
||||
ExprPrinter expr_parser{};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
inner += Ident() + "(" + expr_parser.GetResult() + ") -> goto Label_" +
|
||||
std::to_string(ast.label) + ";\n";
|
||||
inner +=
|
||||
fmt::format("{}({}) -> goto Label_{};\n", Indent(), expr_parser.GetResult(), ast.label);
|
||||
}
|
||||
|
||||
void operator()(const ASTDoWhile& ast) {
|
||||
ExprPrinter expr_parser{};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
inner += Ident() + "do {\n";
|
||||
inner += fmt::format("{}do {{\n", Indent());
|
||||
scope++;
|
||||
ASTNode current = ast.nodes.GetFirst();
|
||||
while (current) {
|
||||
@@ -314,32 +321,23 @@ public:
|
||||
current = current->GetNext();
|
||||
}
|
||||
scope--;
|
||||
inner += Ident() + "} while (" + expr_parser.GetResult() + ");\n";
|
||||
inner += fmt::format("{}}} while ({});\n", Indent(), expr_parser.GetResult());
|
||||
}
|
||||
|
||||
void operator()(const ASTReturn& ast) {
|
||||
ExprPrinter expr_parser{};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
inner += Ident() + "(" + expr_parser.GetResult() + ") -> " +
|
||||
(ast.kills ? "discard" : "exit") + ";\n";
|
||||
inner += fmt::format("{}({}) -> {};\n", Indent(), expr_parser.GetResult(),
|
||||
ast.kills ? "discard" : "exit");
|
||||
}
|
||||
|
||||
void operator()(const ASTBreak& ast) {
|
||||
ExprPrinter expr_parser{};
|
||||
std::visit(expr_parser, *ast.condition);
|
||||
inner += Ident() + "(" + expr_parser.GetResult() + ") -> break;\n";
|
||||
inner += fmt::format("{}({}) -> break;\n", Indent(), expr_parser.GetResult());
|
||||
}
|
||||
|
||||
std::string& Ident() {
|
||||
if (memo_scope == scope) {
|
||||
return tabs_memo;
|
||||
}
|
||||
tabs_memo = tabs.substr(0, scope * 2);
|
||||
memo_scope = scope;
|
||||
return tabs_memo;
|
||||
}
|
||||
|
||||
void Visit(ASTNode& node) {
|
||||
void Visit(const ASTNode& node) {
|
||||
std::visit(*this, *node->GetInnerData());
|
||||
}
|
||||
|
||||
@@ -348,16 +346,29 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
std::string_view Indent() {
|
||||
if (space_segment_scope == scope) {
|
||||
return space_segment;
|
||||
}
|
||||
|
||||
// Ensure that we don't exceed our view.
|
||||
ASSERT(scope * 2 < spaces.size());
|
||||
|
||||
space_segment = spaces.substr(0, scope * 2);
|
||||
space_segment_scope = scope;
|
||||
return space_segment;
|
||||
}
|
||||
|
||||
std::string inner{};
|
||||
std::string_view space_segment;
|
||||
|
||||
u32 scope{};
|
||||
u32 space_segment_scope{};
|
||||
|
||||
std::string tabs_memo{};
|
||||
u32 memo_scope{};
|
||||
|
||||
static constexpr std::string_view tabs{" "};
|
||||
static constexpr std::string_view spaces{" "};
|
||||
};
|
||||
|
||||
std::string ASTManager::Print() {
|
||||
std::string ASTManager::Print() const {
|
||||
ASTPrinter printer{};
|
||||
printer.Visit(main_node);
|
||||
return printer.GetResult();
|
||||
@@ -553,13 +564,13 @@ bool ASTManager::DirectlyRelated(const ASTNode& first, const ASTNode& second) co
|
||||
return min->GetParent() == max->GetParent();
|
||||
}
|
||||
|
||||
void ASTManager::ShowCurrentState(std::string_view state) {
|
||||
void ASTManager::ShowCurrentState(std::string_view state) const {
|
||||
LOG_CRITICAL(HW_GPU, "\nState {}:\n\n{}\n", state, Print());
|
||||
SanityCheck();
|
||||
}
|
||||
|
||||
void ASTManager::SanityCheck() {
|
||||
for (auto& label : labels) {
|
||||
void ASTManager::SanityCheck() const {
|
||||
for (const auto& label : labels) {
|
||||
if (!label->GetParent()) {
|
||||
LOG_CRITICAL(HW_GPU, "Sanity Check Failed");
|
||||
}
|
||||
|
||||
@@ -328,13 +328,13 @@ public:
|
||||
|
||||
void InsertReturn(Expr condition, bool kills);
|
||||
|
||||
std::string Print();
|
||||
std::string Print() const;
|
||||
|
||||
void Decompile();
|
||||
|
||||
void ShowCurrentState(std::string_view state);
|
||||
void ShowCurrentState(std::string_view state) const;
|
||||
|
||||
void SanityCheck();
|
||||
void SanityCheck() const;
|
||||
|
||||
void Clear();
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ ConstBufferLocker::ConstBufferLocker(Tegra::Engines::ShaderType shader_stage,
|
||||
Tegra::Engines::ConstBufferEngineInterface& engine)
|
||||
: stage{shader_stage}, engine{&engine} {}
|
||||
|
||||
ConstBufferLocker::~ConstBufferLocker() = default;
|
||||
|
||||
std::optional<u32> ConstBufferLocker::ObtainKey(u32 buffer, u32 offset) {
|
||||
const std::pair<u32, u32> key = {buffer, offset};
|
||||
const auto iter = keys.find(key);
|
||||
@@ -29,7 +31,7 @@ std::optional<u32> ConstBufferLocker::ObtainKey(u32 buffer, u32 offset) {
|
||||
return iter->second;
|
||||
}
|
||||
if (!engine) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
const u32 value = engine->AccessConstBuffer32(stage, buffer, offset);
|
||||
keys.emplace(key, value);
|
||||
@@ -43,7 +45,7 @@ std::optional<SamplerDescriptor> ConstBufferLocker::ObtainBoundSampler(u32 offse
|
||||
return iter->second;
|
||||
}
|
||||
if (!engine) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
const SamplerDescriptor value = engine->AccessBoundSampler(stage, offset);
|
||||
bound_samplers.emplace(key, value);
|
||||
@@ -58,7 +60,7 @@ std::optional<Tegra::Engines::SamplerDescriptor> ConstBufferLocker::ObtainBindle
|
||||
return iter->second;
|
||||
}
|
||||
if (!engine) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
const SamplerDescriptor value = engine->AccessBindlessSampler(stage, buffer, offset);
|
||||
bindless_samplers.emplace(key, value);
|
||||
|
||||
@@ -16,6 +16,11 @@ using BoundSamplerMap = std::unordered_map<u32, Tegra::Engines::SamplerDescripto
|
||||
using BindlessSamplerMap =
|
||||
std::unordered_map<std::pair<u32, u32>, Tegra::Engines::SamplerDescriptor, Common::PairHash>;
|
||||
|
||||
/**
|
||||
* The ConstBufferLocker is a class use to interface the 3D and compute engines with the shader
|
||||
* compiler. with it, the shader can obtain required data from GPU state and store it for disk
|
||||
* shader compilation.
|
||||
**/
|
||||
class ConstBufferLocker {
|
||||
public:
|
||||
explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage);
|
||||
@@ -23,6 +28,8 @@ public:
|
||||
explicit ConstBufferLocker(Tegra::Engines::ShaderType shader_stage,
|
||||
Tegra::Engines::ConstBufferEngineInterface& engine);
|
||||
|
||||
~ConstBufferLocker();
|
||||
|
||||
/// Retrieves a key from the locker, if it's registered, it will give the registered value, if
|
||||
/// not it will obtain it from maxwell3d and register it.
|
||||
std::optional<u32> ObtainKey(u32 buffer, u32 offset);
|
||||
|
||||
@@ -41,14 +41,10 @@ BlockBranchInfo MakeBranchInfo(Args&&... args) {
|
||||
return std::make_shared<BranchData>(T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
bool BlockBranchInfoAreEqual(BlockBranchInfo first, BlockBranchInfo second) {
|
||||
return false; //(*first) == (*second);
|
||||
}
|
||||
|
||||
bool BlockBranchIsIgnored(BlockBranchInfo first) {
|
||||
bool ignore = false;
|
||||
if (std::holds_alternative<SingleBranch>(*first)) {
|
||||
auto branch = std::get_if<SingleBranch>(first.get());
|
||||
const auto branch = std::get_if<SingleBranch>(first.get());
|
||||
ignore = branch->ignore;
|
||||
}
|
||||
return ignore;
|
||||
@@ -151,10 +147,10 @@ std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState&
|
||||
const Instruction instr = {state.program_code[pos]};
|
||||
const auto opcode = OpCode::Decode(instr);
|
||||
if (opcode->get().GetId() != OpCode::Id::BRX) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
if (instr.brx.constant_buffer != 0) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
track_register = instr.gpr8.Value();
|
||||
result.relative_position = instr.brx.GetBranchExtend();
|
||||
@@ -184,7 +180,7 @@ std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState&
|
||||
}
|
||||
|
||||
if (!found_track) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
found_track = false;
|
||||
|
||||
@@ -208,7 +204,7 @@ std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState&
|
||||
}
|
||||
|
||||
if (!found_track) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
found_track = false;
|
||||
|
||||
@@ -233,7 +229,7 @@ std::optional<BranchIndirectInfo> TrackBranchIndirectInfo(const CFGRebuildState&
|
||||
}
|
||||
|
||||
if (!found_track) {
|
||||
return {};
|
||||
return std::nullopt;
|
||||
}
|
||||
return {result};
|
||||
}
|
||||
@@ -440,8 +436,8 @@ std::pair<ParseResult, ParseInfo> ParseCode(CFGRebuildState& state, u32 address)
|
||||
branches.emplace_back(value, target);
|
||||
}
|
||||
parse_info.end_address = offset;
|
||||
parse_info.branch_info =
|
||||
MakeBranchInfo<MultiBranch>(static_cast<u32>(instr.gpr8.Value()), branches);
|
||||
parse_info.branch_info = MakeBranchInfo<MultiBranch>(
|
||||
static_cast<u32>(instr.gpr8.Value()), std::move(branches));
|
||||
|
||||
return {ParseResult::ControlCaught, parse_info};
|
||||
} else {
|
||||
@@ -486,7 +482,7 @@ bool TryInspectAddress(CFGRebuildState& state) {
|
||||
current_block.end = address - 1;
|
||||
new_block.branch = current_block.branch;
|
||||
BlockBranchInfo forward_branch = MakeBranchInfo<SingleBranch>();
|
||||
auto branch = std::get_if<SingleBranch>(forward_branch.get());
|
||||
const auto branch = std::get_if<SingleBranch>(forward_branch.get());
|
||||
branch->address = address;
|
||||
branch->ignore = true;
|
||||
current_block.branch = forward_branch;
|
||||
@@ -504,7 +500,7 @@ bool TryInspectAddress(CFGRebuildState& state) {
|
||||
BlockInfo& block_info = CreateBlockInfo(state, address, parse_info.end_address);
|
||||
block_info.branch = parse_info.branch_info;
|
||||
if (std::holds_alternative<SingleBranch>(*block_info.branch)) {
|
||||
auto branch = std::get_if<SingleBranch>(block_info.branch.get());
|
||||
const auto branch = std::get_if<SingleBranch>(block_info.branch.get());
|
||||
if (branch->condition.IsUnconditional()) {
|
||||
return true;
|
||||
}
|
||||
@@ -550,7 +546,7 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
gather_labels(q2.ssy_stack, state.ssy_labels, block);
|
||||
gather_labels(q2.pbk_stack, state.pbk_labels, block);
|
||||
if (std::holds_alternative<SingleBranch>(*block.branch)) {
|
||||
auto branch = std::get_if<SingleBranch>(block.branch.get());
|
||||
const auto branch = std::get_if<SingleBranch>(block.branch.get());
|
||||
if (!branch->condition.IsUnconditional()) {
|
||||
q2.address = block.end + 1;
|
||||
state.queries.push_back(q2);
|
||||
@@ -573,8 +569,8 @@ bool TryQuery(CFGRebuildState& state) {
|
||||
state.queries.push_back(std::move(conditional_query));
|
||||
return true;
|
||||
}
|
||||
auto multi_branch = std::get_if<MultiBranch>(block.branch.get());
|
||||
for (auto& branch_case : multi_branch->branches) {
|
||||
const auto multi_branch = std::get_if<MultiBranch>(block.branch.get());
|
||||
for (const auto& branch_case : multi_branch->branches) {
|
||||
Query conditional_query{q2};
|
||||
conditional_query.address = branch_case.address;
|
||||
state.queries.push_back(std::move(conditional_query));
|
||||
@@ -612,7 +608,7 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
|
||||
return MakeExpr<ExprBoolean>(true);
|
||||
});
|
||||
if (std::holds_alternative<SingleBranch>(*branch_info)) {
|
||||
auto branch = std::get_if<SingleBranch>(branch_info.get());
|
||||
const auto branch = std::get_if<SingleBranch>(branch_info.get());
|
||||
if (branch->address < 0) {
|
||||
if (branch->kill) {
|
||||
mm.InsertReturn(get_expr(branch->condition), true);
|
||||
@@ -624,8 +620,8 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
|
||||
mm.InsertGoto(get_expr(branch->condition), branch->address);
|
||||
return;
|
||||
}
|
||||
auto multi_branch = std::get_if<MultiBranch>(branch_info.get());
|
||||
for (auto& branch_case : multi_branch->branches) {
|
||||
const auto multi_branch = std::get_if<MultiBranch>(branch_info.get());
|
||||
for (const auto& branch_case : multi_branch->branches) {
|
||||
mm.InsertGoto(MakeExpr<ExprGprEqual>(multi_branch->gpr, branch_case.cmp_value),
|
||||
branch_case.address);
|
||||
}
|
||||
|
||||
@@ -51,6 +51,10 @@ public:
|
||||
std::tie(b.condition, b.address, b.kill, b.is_sync, b.is_brk, b.ignore);
|
||||
}
|
||||
|
||||
bool operator!=(const SingleBranch& b) const {
|
||||
return !operator==(b);
|
||||
}
|
||||
|
||||
Condition condition{};
|
||||
s32 address{exit_branch};
|
||||
bool kill{};
|
||||
@@ -67,7 +71,7 @@ struct CaseBranch {
|
||||
|
||||
class MultiBranch {
|
||||
public:
|
||||
MultiBranch(u32 gpr, std::vector<CaseBranch>& branches)
|
||||
MultiBranch(u32 gpr, std::vector<CaseBranch>&& branches)
|
||||
: gpr{gpr}, branches{std::move(branches)} {}
|
||||
|
||||
u32 gpr{};
|
||||
|
||||
@@ -166,9 +166,17 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}();
|
||||
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, instr, false);
|
||||
TrackGlobalMemory(bb, instr, false);
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(type);
|
||||
if (!real_address_base || !base_address) {
|
||||
// Tracking failed, load zeroes.
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
SetRegister(bb, instr.gpr0.Value() + i, Immediate(0.0f));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
@@ -260,22 +268,19 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
}();
|
||||
|
||||
const auto [real_address_base, base_address, descriptor] =
|
||||
TrackAndGetGlobalMemory(bb, instr, true);
|
||||
|
||||
// Encode in temporary registers like this: real_base_address, {registers_to_be_written...}
|
||||
SetTemporary(bb, 0, real_address_base);
|
||||
TrackGlobalMemory(bb, instr, true);
|
||||
if (!real_address_base || !base_address) {
|
||||
// Tracking failed, skip the store.
|
||||
break;
|
||||
}
|
||||
|
||||
const u32 count = GetUniformTypeElementsCount(type);
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
SetTemporary(bb, i + 1, GetRegister(instr.gpr0.Value() + i));
|
||||
}
|
||||
for (u32 i = 0; i < count; ++i) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node real_address = Operation(OperationCode::UAdd, real_address_base, it_offset);
|
||||
const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporary(i + 1)));
|
||||
const Node value = GetRegister(instr.gpr0.Value() + i);
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, value));
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -301,15 +306,17 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
return pc;
|
||||
}
|
||||
|
||||
std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeBlock& bb,
|
||||
Instruction instr,
|
||||
bool is_write) {
|
||||
std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackGlobalMemory(NodeBlock& bb,
|
||||
Instruction instr,
|
||||
bool is_write) {
|
||||
const auto addr_register{GetRegister(instr.gmem.gpr)};
|
||||
const auto immediate_offset{static_cast<u32>(instr.gmem.offset)};
|
||||
|
||||
const auto [base_address, index, offset] =
|
||||
TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()));
|
||||
ASSERT(base_address != nullptr);
|
||||
ASSERT_OR_EXECUTE_MSG(base_address,
|
||||
{ return std::make_tuple(nullptr, nullptr, GlobalMemoryBase{}); },
|
||||
"Global memory tracking failed");
|
||||
|
||||
bb.push_back(Comment(fmt::format("Base address is c[0x{:x}][0x{:x}]", index, offset)));
|
||||
|
||||
|
||||
@@ -127,6 +127,10 @@ public:
|
||||
return gpr == b.gpr && value == b.value;
|
||||
}
|
||||
|
||||
bool operator!=(const ExprGprEqual& b) const {
|
||||
return !operator==(b);
|
||||
}
|
||||
|
||||
u32 gpr;
|
||||
u32 value;
|
||||
};
|
||||
|
||||
@@ -410,7 +410,7 @@ public:
|
||||
explicit OperationNode(OperationCode code) : OperationNode(code, Meta{}) {}
|
||||
|
||||
explicit OperationNode(OperationCode code, Meta meta)
|
||||
: OperationNode(code, meta, std::vector<Node>{}) {}
|
||||
: OperationNode(code, std::move(meta), std::vector<Node>{}) {}
|
||||
|
||||
explicit OperationNode(OperationCode code, std::vector<Node> operands)
|
||||
: OperationNode(code, Meta{}, std::move(operands)) {}
|
||||
|
||||
@@ -378,8 +378,9 @@ private:
|
||||
std::pair<Node, s64> TrackRegister(const GprNode* tracked, const NodeBlock& code,
|
||||
s64 cursor) const;
|
||||
|
||||
std::tuple<Node, Node, GlobalMemoryBase> TrackAndGetGlobalMemory(
|
||||
NodeBlock& bb, Tegra::Shader::Instruction instr, bool is_write);
|
||||
std::tuple<Node, Node, GlobalMemoryBase> TrackGlobalMemory(NodeBlock& bb,
|
||||
Tegra::Shader::Instruction instr,
|
||||
bool is_write);
|
||||
|
||||
const ProgramCode& program_code;
|
||||
const u32 main_offset;
|
||||
|
||||
@@ -260,6 +260,7 @@ PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case Tegra::Texture::TextureFormat::R32_G32_B32_A32:
|
||||
switch (component_type) {
|
||||
case Tegra::Texture::ComponentType::FLOAT:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/microprofile.h"
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/binary_find.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/morton.h"
|
||||
|
||||
@@ -250,6 +250,16 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
|
||||
return params;
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceTarget SurfaceParams::ExpectedTarget(
|
||||
const VideoCommon::Shader::Sampler& entry) {
|
||||
return TextureTypeToSurfaceTarget(entry.GetType(), entry.IsArray());
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceTarget SurfaceParams::ExpectedTarget(
|
||||
const VideoCommon::Shader::Image& entry) {
|
||||
return ImageTypeToSurfaceTarget(entry.GetType());
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsLayered() const {
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
|
||||
@@ -41,6 +41,14 @@ public:
|
||||
static SurfaceParams CreateForFermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config);
|
||||
|
||||
/// Obtains the texture target from a shader's sampler entry.
|
||||
static VideoCore::Surface::SurfaceTarget ExpectedTarget(
|
||||
const VideoCommon::Shader::Sampler& entry);
|
||||
|
||||
/// Obtains the texture target from a shader's sampler entry.
|
||||
static VideoCore::Surface::SurfaceTarget ExpectedTarget(
|
||||
const VideoCommon::Shader::Image& entry);
|
||||
|
||||
std::size_t Hash() const {
|
||||
return static_cast<std::size_t>(
|
||||
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
|
||||
|
||||
@@ -62,10 +62,10 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/***
|
||||
* `Guard` guarantees that rendertargets don't unregister themselves if the
|
||||
/**
|
||||
* Guarantees that rendertargets don't unregister themselves if the
|
||||
* collide. Protection is currently only done on 3D slices.
|
||||
***/
|
||||
*/
|
||||
void GuardRenderTargets(bool new_guard) {
|
||||
guard_render_targets = new_guard;
|
||||
}
|
||||
@@ -94,10 +94,16 @@ public:
|
||||
std::lock_guard lock{mutex};
|
||||
const auto gpu_addr{tic.Address()};
|
||||
if (!gpu_addr) {
|
||||
return {};
|
||||
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
|
||||
}
|
||||
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
if (!cache_addr) {
|
||||
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
|
||||
}
|
||||
const auto params{SurfaceParams::CreateForTexture(tic, entry)};
|
||||
const auto [surface, view] = GetSurface(gpu_addr, params, true, false);
|
||||
const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false);
|
||||
if (guard_samplers) {
|
||||
sampled_textures.push_back(surface);
|
||||
}
|
||||
@@ -109,10 +115,15 @@ public:
|
||||
std::lock_guard lock{mutex};
|
||||
const auto gpu_addr{tic.Address()};
|
||||
if (!gpu_addr) {
|
||||
return {};
|
||||
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
|
||||
}
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
if (!cache_addr) {
|
||||
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
|
||||
}
|
||||
const auto params{SurfaceParams::CreateForImage(tic, entry)};
|
||||
const auto [surface, view] = GetSurface(gpu_addr, params, true, false);
|
||||
const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false);
|
||||
if (guard_samplers) {
|
||||
sampled_textures.push_back(surface);
|
||||
}
|
||||
@@ -142,11 +153,17 @@ public:
|
||||
SetEmptyDepthBuffer();
|
||||
return {};
|
||||
}
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
if (!cache_addr) {
|
||||
SetEmptyDepthBuffer();
|
||||
return {};
|
||||
}
|
||||
const auto depth_params{SurfaceParams::CreateForDepthBuffer(
|
||||
system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
|
||||
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
|
||||
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
|
||||
auto surface_view = GetSurface(gpu_addr, depth_params, preserve_contents, true);
|
||||
auto surface_view = GetSurface(gpu_addr, cache_addr, depth_params, preserve_contents, true);
|
||||
if (depth_buffer.target)
|
||||
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
|
||||
depth_buffer.target = surface_view.first;
|
||||
@@ -179,8 +196,16 @@ public:
|
||||
return {};
|
||||
}
|
||||
|
||||
auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
|
||||
preserve_contents, true);
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
if (!cache_addr) {
|
||||
SetEmptyColorBuffer(index);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto surface_view =
|
||||
GetSurface(gpu_addr, cache_addr, SurfaceParams::CreateForFramebuffer(system, index),
|
||||
preserve_contents, true);
|
||||
if (render_targets[index].target)
|
||||
render_targets[index].target->MarkAsRenderTarget(false, NO_RT);
|
||||
render_targets[index].target = surface_view.first;
|
||||
@@ -229,8 +254,14 @@ public:
|
||||
const GPUVAddr src_gpu_addr = src_config.Address();
|
||||
const GPUVAddr dst_gpu_addr = dst_config.Address();
|
||||
DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr);
|
||||
std::pair<TSurface, TView> dst_surface = GetSurface(dst_gpu_addr, dst_params, true, false);
|
||||
std::pair<TSurface, TView> src_surface = GetSurface(src_gpu_addr, src_params, true, false);
|
||||
const auto dst_host_ptr{system.GPU().MemoryManager().GetPointer(dst_gpu_addr)};
|
||||
const auto dst_cache_addr{ToCacheAddr(dst_host_ptr)};
|
||||
const auto src_host_ptr{system.GPU().MemoryManager().GetPointer(src_gpu_addr)};
|
||||
const auto src_cache_addr{ToCacheAddr(src_host_ptr)};
|
||||
std::pair<TSurface, TView> dst_surface =
|
||||
GetSurface(dst_gpu_addr, dst_cache_addr, dst_params, true, false);
|
||||
std::pair<TSurface, TView> src_surface =
|
||||
GetSurface(src_gpu_addr, src_cache_addr, src_params, true, false);
|
||||
ImageBlit(src_surface.second, dst_surface.second, copy_config);
|
||||
dst_surface.first->MarkAsModified(true, Tick());
|
||||
}
|
||||
@@ -287,7 +318,7 @@ protected:
|
||||
const Tegra::Engines::Fermi2D::Config& copy_config) = 0;
|
||||
|
||||
// Depending on the backend, a buffer copy can be slow as it means deoptimizing the texture
|
||||
// and reading it from a sepparate buffer.
|
||||
// and reading it from a separate buffer.
|
||||
virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0;
|
||||
|
||||
void ManageRenderTargetUnregister(TSurface& surface) {
|
||||
@@ -346,13 +377,6 @@ protected:
|
||||
return new_surface;
|
||||
}
|
||||
|
||||
std::pair<TSurface, TView> GetFermiSurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
||||
SurfaceParams params = SurfaceParams::CreateForFermiCopySurface(config);
|
||||
const GPUVAddr gpu_addr = config.Address();
|
||||
return GetSurface(gpu_addr, params, true, false);
|
||||
}
|
||||
|
||||
Core::System& system;
|
||||
|
||||
private:
|
||||
@@ -386,12 +410,13 @@ private:
|
||||
};
|
||||
|
||||
/**
|
||||
* `PickStrategy` takes care of selecting a proper strategy to deal with a texture recycle.
|
||||
* @param overlaps, the overlapping surfaces registered in the cache.
|
||||
* @param params, the paremeters on the new surface.
|
||||
* @param gpu_addr, the starting address of the new surface.
|
||||
* @param untopological, tells the recycler that the texture has no way to match the overlaps
|
||||
* due to topological reasons.
|
||||
* Takes care of selecting a proper strategy to deal with a texture recycle.
|
||||
*
|
||||
* @param overlaps The overlapping surfaces registered in the cache.
|
||||
* @param params The parameters on the new surface.
|
||||
* @param gpu_addr The starting address of the new surface.
|
||||
* @param untopological Indicates to the recycler that the texture has no way
|
||||
* to match the overlaps due to topological reasons.
|
||||
**/
|
||||
RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params,
|
||||
const GPUVAddr gpu_addr, const MatchTopologyResult untopological) {
|
||||
@@ -402,7 +427,7 @@ private:
|
||||
if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) {
|
||||
return RecycleStrategy::Flush;
|
||||
}
|
||||
for (auto s : overlaps) {
|
||||
for (const auto& s : overlaps) {
|
||||
const auto& s_params = s->GetSurfaceParams();
|
||||
if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) {
|
||||
return RecycleStrategy::Flush;
|
||||
@@ -419,16 +444,19 @@ private:
|
||||
}
|
||||
|
||||
/**
|
||||
* `RecycleSurface` es a method we use to decide what to do with textures we can't resolve in
|
||||
*the cache It has 2 implemented strategies: Ignore and Flush. Ignore just unregisters all the
|
||||
*overlaps and loads the new texture. Flush, flushes all the overlaps into memory and loads the
|
||||
*new surface from that data.
|
||||
* @param overlaps, the overlapping surfaces registered in the cache.
|
||||
* @param params, the paremeters on the new surface.
|
||||
* @param gpu_addr, the starting address of the new surface.
|
||||
* @param preserve_contents, tells if the new surface should be loaded from meory or left blank
|
||||
* @param untopological, tells the recycler that the texture has no way to match the overlaps
|
||||
* due to topological reasons.
|
||||
* Used to decide what to do with textures we can't resolve in the cache It has 2 implemented
|
||||
* strategies: Ignore and Flush.
|
||||
*
|
||||
* - Ignore: Just unregisters all the overlaps and loads the new texture.
|
||||
* - Flush: Flushes all the overlaps into memory and loads the new surface from that data.
|
||||
*
|
||||
* @param overlaps The overlapping surfaces registered in the cache.
|
||||
* @param params The parameters for the new surface.
|
||||
* @param gpu_addr The starting address of the new surface.
|
||||
* @param preserve_contents Indicates that the new surface should be loaded from memory or left
|
||||
* blank.
|
||||
* @param untopological Indicates to the recycler that the texture has no way to match the
|
||||
* overlaps due to topological reasons.
|
||||
**/
|
||||
std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps,
|
||||
const SurfaceParams& params, const GPUVAddr gpu_addr,
|
||||
@@ -465,10 +493,12 @@ private:
|
||||
}
|
||||
|
||||
/**
|
||||
* `RebuildSurface` this method takes a single surface and recreates into another that
|
||||
* may differ in format, target or width alingment.
|
||||
* @param current_surface, the registered surface in the cache which we want to convert.
|
||||
* @param params, the new surface params which we'll use to recreate the surface.
|
||||
* Takes a single surface and recreates into another that may differ in
|
||||
* format, target or width alignment.
|
||||
*
|
||||
* @param current_surface The registered surface in the cache which we want to convert.
|
||||
* @param params The new surface params which we'll use to recreate the surface.
|
||||
* @param is_render Whether or not the surface is a render target.
|
||||
**/
|
||||
std::pair<TSurface, TView> RebuildSurface(TSurface current_surface, const SurfaceParams& params,
|
||||
bool is_render) {
|
||||
@@ -502,12 +532,14 @@ private:
|
||||
}
|
||||
|
||||
/**
|
||||
* `ManageStructuralMatch` this method takes a single surface and checks with the new surface's
|
||||
* params if it's an exact match, we return the main view of the registered surface. If it's
|
||||
* formats don't match, we rebuild the surface. We call this last method a `Mirage`. If formats
|
||||
* Takes a single surface and checks with the new surface's params if it's an exact
|
||||
* match, we return the main view of the registered surface. If its formats don't
|
||||
* match, we rebuild the surface. We call this last method a `Mirage`. If formats
|
||||
* match but the targets don't, we create an overview View of the registered surface.
|
||||
* @param current_surface, the registered surface in the cache which we want to convert.
|
||||
* @param params, the new surface params which we want to check.
|
||||
*
|
||||
* @param current_surface The registered surface in the cache which we want to convert.
|
||||
* @param params The new surface params which we want to check.
|
||||
* @param is_render Whether or not the surface is a render target.
|
||||
**/
|
||||
std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface,
|
||||
const SurfaceParams& params, bool is_render) {
|
||||
@@ -529,13 +561,14 @@ private:
|
||||
}
|
||||
|
||||
/**
|
||||
* `TryReconstructSurface` unlike `RebuildSurface` where we know the registered surface
|
||||
* matches the candidate in some way, we got no guarantess here. We try to see if the overlaps
|
||||
* are sublayers/mipmaps of the new surface, if they all match we end up recreating a surface
|
||||
* for them, else we return nothing.
|
||||
* @param overlaps, the overlapping surfaces registered in the cache.
|
||||
* @param params, the paremeters on the new surface.
|
||||
* @param gpu_addr, the starting address of the new surface.
|
||||
* Unlike RebuildSurface where we know whether or not registered surfaces match the candidate
|
||||
* in some way, we have no guarantees here. We try to see if the overlaps are sublayers/mipmaps
|
||||
* of the new surface, if they all match we end up recreating a surface for them,
|
||||
* else we return nothing.
|
||||
*
|
||||
* @param overlaps The overlapping surfaces registered in the cache.
|
||||
* @param params The parameters on the new surface.
|
||||
* @param gpu_addr The starting address of the new surface.
|
||||
**/
|
||||
std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps,
|
||||
const SurfaceParams& params,
|
||||
@@ -575,7 +608,7 @@ private:
|
||||
} else if (Settings::values.use_accurate_gpu_emulation && passed_tests != overlaps.size()) {
|
||||
return {};
|
||||
}
|
||||
for (auto surface : overlaps) {
|
||||
for (const auto& surface : overlaps) {
|
||||
Unregister(surface);
|
||||
}
|
||||
new_surface->MarkAsModified(modified, Tick());
|
||||
@@ -584,36 +617,31 @@ private:
|
||||
}
|
||||
|
||||
/**
|
||||
* `GetSurface` gets the starting address and parameters of a candidate surface and tries
|
||||
* to find a matching surface within the cache. This is done in 3 big steps. The first is to
|
||||
* check the 1st Level Cache in order to find an exact match, if we fail, we move to step 2.
|
||||
* Step 2 is checking if there are any overlaps at all, if none, we just load the texture from
|
||||
* memory else we move to step 3. Step 3 consists on figuring the relationship between the
|
||||
* candidate texture and the overlaps. We divide the scenarios depending if there's 1 or many
|
||||
* overlaps. If there's many, we just try to reconstruct a new surface out of them based on the
|
||||
* candidate's parameters, if we fail, we recycle. When there's only 1 overlap then we have to
|
||||
* check if the candidate is a view (layer/mipmap) of the overlap or if the registered surface
|
||||
* is a mipmap/layer of the candidate. In this last case we reconstruct a new surface.
|
||||
* @param gpu_addr, the starting address of the candidate surface.
|
||||
* @param params, the paremeters on the candidate surface.
|
||||
* @param preserve_contents, tells if the new surface should be loaded from meory or left blank.
|
||||
* Gets the starting address and parameters of a candidate surface and tries
|
||||
* to find a matching surface within the cache. This is done in 3 big steps:
|
||||
*
|
||||
* 1. Check the 1st Level Cache in order to find an exact match, if we fail, we move to step 2.
|
||||
*
|
||||
* 2. Check if there are any overlaps at all, if there are none, we just load the texture from
|
||||
* memory else we move to step 3.
|
||||
*
|
||||
* 3. Consists of figuring out the relationship between the candidate texture and the
|
||||
* overlaps. We divide the scenarios depending if there's 1 or many overlaps. If
|
||||
* there's many, we just try to reconstruct a new surface out of them based on the
|
||||
* candidate's parameters, if we fail, we recycle. When there's only 1 overlap then we
|
||||
* have to check if the candidate is a view (layer/mipmap) of the overlap or if the
|
||||
* registered surface is a mipmap/layer of the candidate. In this last case we reconstruct
|
||||
* a new surface.
|
||||
*
|
||||
* @param gpu_addr The starting address of the candidate surface.
|
||||
* @param params The parameters on the candidate surface.
|
||||
* @param preserve_contents Indicates that the new surface should be loaded from memory or
|
||||
* left blank.
|
||||
* @param is_render Whether or not the surface is a render target.
|
||||
**/
|
||||
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params,
|
||||
bool preserve_contents, bool is_render) {
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||
|
||||
// Step 0: guarantee a valid surface
|
||||
if (!cache_addr) {
|
||||
// Return a null surface if it's invalid
|
||||
SurfaceParams new_params = params;
|
||||
new_params.width = 1;
|
||||
new_params.height = 1;
|
||||
new_params.depth = 1;
|
||||
new_params.block_height = 0;
|
||||
new_params.block_depth = 0;
|
||||
return InitializeSurface(gpu_addr, new_params, false);
|
||||
}
|
||||
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const CacheAddr cache_addr,
|
||||
const SurfaceParams& params, bool preserve_contents,
|
||||
bool is_render) {
|
||||
|
||||
// Step 1
|
||||
// Check Level 1 Cache for a fast structural match. If candidate surface
|
||||
@@ -651,7 +679,7 @@ private:
|
||||
// Step 3
|
||||
// Now we need to figure the relationship between the texture and its overlaps
|
||||
// we do a topological test to ensure we can find some relationship. If it fails
|
||||
// inmediatly recycle the texture
|
||||
// immediately recycle the texture
|
||||
for (const auto& surface : overlaps) {
|
||||
const auto topological_result = surface->MatchesTopology(params);
|
||||
if (topological_result != MatchTopologyResult::FullMatch) {
|
||||
@@ -720,12 +748,13 @@ private:
|
||||
}
|
||||
|
||||
/**
|
||||
* `DeduceSurface` gets the starting address and parameters of a candidate surface and tries
|
||||
* to find a matching surface within the cache that's similar to it. If there are many textures
|
||||
* Gets the starting address and parameters of a candidate surface and tries to find a
|
||||
* matching surface within the cache that's similar to it. If there are many textures
|
||||
* or the texture found if entirely incompatible, it will fail. If no texture is found, the
|
||||
* blit will be unsuccessful.
|
||||
* @param gpu_addr, the starting address of the candidate surface.
|
||||
* @param params, the paremeters on the candidate surface.
|
||||
*
|
||||
* @param gpu_addr The starting address of the candidate surface.
|
||||
* @param params The parameters on the candidate surface.
|
||||
**/
|
||||
Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) {
|
||||
const auto host_ptr{system.GPU().MemoryManager().GetPointer(gpu_addr)};
|
||||
@@ -777,11 +806,50 @@ private:
|
||||
}
|
||||
|
||||
/**
|
||||
* `DeduceBestBlit` gets the a source and destination starting address and parameters,
|
||||
* Gets a null surface based on a target texture.
|
||||
* @param target The target of the null surface.
|
||||
**/
|
||||
TView GetNullSurface(SurfaceTarget target) {
|
||||
const u32 i_target = static_cast<u32>(target);
|
||||
if (const auto it = invalid_cache.find(i_target); it != invalid_cache.end()) {
|
||||
return it->second->GetMainView();
|
||||
}
|
||||
SurfaceParams params{};
|
||||
params.target = target;
|
||||
params.is_tiled = false;
|
||||
params.srgb_conversion = false;
|
||||
params.is_layered = false;
|
||||
params.block_width = 0;
|
||||
params.block_height = 0;
|
||||
params.block_depth = 0;
|
||||
params.tile_width_spacing = 1;
|
||||
params.width = 1;
|
||||
params.height = 1;
|
||||
params.depth = 1;
|
||||
params.pitch = 4;
|
||||
params.num_levels = 1;
|
||||
params.emulated_levels = 1;
|
||||
params.pixel_format = VideoCore::Surface::PixelFormat::RGBA16F;
|
||||
params.component_type = VideoCore::Surface::ComponentType::Float;
|
||||
params.type = VideoCore::Surface::SurfaceType::ColorTexture;
|
||||
auto surface = CreateSurface(0ULL, params);
|
||||
invalid_memory.clear();
|
||||
invalid_memory.resize(surface->GetHostSizeInBytes(), 0U);
|
||||
surface->UploadTexture(invalid_memory);
|
||||
surface->MarkAsModified(false, Tick());
|
||||
invalid_cache.emplace(i_target, surface);
|
||||
return surface->GetMainView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the a source and destination starting address and parameters,
|
||||
* and tries to deduce if they are supposed to be depth textures. If so, their
|
||||
* parameters are modified and fixed into so.
|
||||
* @param gpu_addr, the starting address of the candidate surface.
|
||||
* @param params, the parameters on the candidate surface.
|
||||
*
|
||||
* @param src_params The parameters of the candidate surface.
|
||||
* @param dst_params The parameters of the destination surface.
|
||||
* @param src_gpu_addr The starting address of the candidate surface.
|
||||
* @param dst_gpu_addr The starting address of the destination surface.
|
||||
**/
|
||||
void DeduceBestBlit(SurfaceParams& src_params, SurfaceParams& dst_params,
|
||||
const GPUVAddr src_gpu_addr, const GPUVAddr dst_gpu_addr) {
|
||||
@@ -970,6 +1038,11 @@ private:
|
||||
|
||||
std::vector<TSurface> sampled_textures;
|
||||
|
||||
/// This cache stores null surfaces in order to be used as a placeholder
|
||||
/// for invalid texture calls.
|
||||
std::unordered_map<u32, TSurface> invalid_cache;
|
||||
std::vector<u8> invalid_memory{};
|
||||
|
||||
StagingCache staging_cache;
|
||||
std::recursive_mutex mutex;
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user