Compare commits

..
Author SHA1 Message Date
yuzubot fe34f07d5a "Merge Tagged PR 1012" 2019-10-21 12:01:23 +00:00
yuzubot ba0a20b394 "Merge Tagged PR 1340" 2019-10-21 12:01:22 +00:00
yuzubot e0fe8802b6 "Merge Tagged PR 1703" 2019-10-21 12:01:22 +00:00
yuzubot 11d3927b5b "Merge Tagged PR 2365" 2019-10-21 12:01:21 +00:00
yuzubot a1e7391c39 "Merge Tagged PR 2542" 2019-10-21 12:01:20 +00:00
yuzubot 6775e03817 "Merge Tagged PR 2710" 2019-10-21 12:01:19 +00:00
yuzubot abee0adfb8 "Merge Tagged PR 2859" 2019-10-21 12:01:18 +00:00
yuzubot a8f8f36baa "Merge Tagged PR 2914" 2019-10-21 12:01:17 +00:00
yuzubot 96e50c0d8a "Merge Tagged PR 2933" 2019-10-21 12:01:16 +00:00
yuzubot 4d66449142 "Merge Tagged PR 2945" 2019-10-21 12:01:16 +00:00
yuzubot 68694e007a "Merge Tagged PR 2976" 2019-10-21 12:01:15 +00:00
yuzubot 64724ebe5f "Merge Tagged PR 2987" 2019-10-21 12:01:13 +00:00
yuzubot dc69b06d37 "Merge Tagged PR 2995" 2019-10-21 12:01:13 +00:00
yuzubot 4fe711bde9 "Merge Tagged PR 3007" 2019-10-21 12:01:12 +00:00
David 1572fb9bf2 Merge pull request #3003 from ReinUsesLisp/flush-mme-trace
maxwell_3d: Reduce FlushMMEInlineDraw logging to Trace
2019-10-20 21:12:11 +11:00
ReinUsesLisp e3107788e6 maxwell_3d: Reduce FlushMMEInlineDraw logging to Trace 2019-10-20 03:43:17 -03:00
bunnei dd2e96b362 Merge pull request #3001 from bunnei/fix-clang-error
core: Fix clang-format errors.
2019-10-19 14:52:19 -04:00
bunnei a86a88109a core: Fix clang-format errors. 2019-10-19 14:47:18 -04:00
bunnei 8d009a7aae Merge pull request #2998 from cociorbaandrei/fix_null_pderef
Fix null pointer dereference.
2019-10-18 10:50:01 -04:00
Nicolae-Andrei Cociorba 20521da259 Fix null pointer deref. 2019-10-18 14:12:12 +00:00
Rodrigo Locatti dc5eedef71 Merge pull request #2994 from lioncash/fmt
video_core/shader/ast: Minor changes to ASTPrinter
2019-10-18 01:05:25 -03:00
Lioncash 074b38b7a9 video_core/shader/ast: Make ShowCurrentState() and SanityCheck() const member functions
These can also trivially be made const member functions, with the
addition of a few consts.
2019-10-17 20:59:48 -04:00
Lioncash 222f4b45eb video_core/shader/ast: Make ASTManager::Print a const member function
Given all visiting functions never modify the nodes, we can trivially
make this a const member function.
2019-10-17 20:56:39 -04:00
Rodrigo Locatti fd922ddb01 Merge pull request #2993 from lioncash/vulkan-expr
vk_shader_decompiler: Mark operator() function parameters as const references
2019-10-17 21:46:49 -03:00
Lioncash 7831e86c34 video_core/shader/ast: Make ExprPrinter members private
This member already has an accessor, so there's no need for it to be
public.
2019-10-17 20:39:36 -04:00
Lioncash a2eccbf075 video_core/shader/ast: Make Indent() return a string_view
The returned string is simply a substring of our constexpr tabs
string_view, so we can just use a string_view here as well, since the
original string_view is guaranteed to always exist.

Now the function is fully non-allocating.
2019-10-17 20:29:00 -04:00
Lioncash 15d177a6ac video_core/shader/ast: Make Indent() private
It's never used outside of this class, so we can narrow its scope down.
2019-10-17 20:26:13 -04:00
Lioncash 7f6a8a33d4 video_core/shader/ast: Rename Ident() to Indent()
This can be confusing, given "ident" is generally used as a shorthand
for "identifier".
2019-10-17 20:26:13 -04:00
Lioncash 081530686c video_core/shader/ast: Make use of fmt where applicable
Makes a few strings nicer to read and also eliminates a bit of string
churn with operator+.
2019-10-17 20:26:10 -04:00
bunnei d1713ad451 Merge pull request #2992 from lioncash/dmnt
dmnt_cheat_vm: Correct register Restore and ClearRegs behavior
2019-10-17 19:52:24 -04:00
Lioncash c6bec9aa10 vk_shader_decompiler: Mark operator() function parameters as const references
These parameters aren't actually modified in any way, so they can be
made const references.
2019-10-17 19:44:00 -04:00
Lioncash 2c34d8aabb dmnt_cheat_vm: Correct register Restore and ClearRegs behavior
Previously these were performing the same behavior as the Save and
ClearSaved opcode types.
2019-10-17 18:49:39 -04:00
Rodrigo Locatti 219fdcb9d9 Merge pull request #2966 from FernandoS27/astc-formats
Implement a series of ASTC formats and R4G4B4A4 format
2019-10-17 19:24:11 -03:00
Rodrigo Locatti a21b88ef8f Merge pull request #2979 from lioncash/macro
video_core/macro_interpreter: Make definitions of most private enums/unions hidden
2019-10-17 19:21:09 -03:00
Rodrigo Locatti 7cf091bb20 Merge pull request #2989 from lioncash/apm
service/apm/controller: Minor interface changes
2019-10-17 19:20:14 -03:00
Rodrigo Locatti 7398466c33 Merge pull request #2990 from lioncash/warn
core: Resolve -Wreorder warnings
2019-10-17 19:18:52 -03:00
Lioncash 125caf5d6e video_core/macro_interpreter: Make definitions of most private enums/unions hidden
This allows the implementation of these types to change without
requiring a rebuild of everything that includes the macro interpreter
header.
2019-10-17 17:55:46 -04:00
Lioncash 98c6a95079 core/core: Resolve -Wreorder warnings
Amends the initializer lists to be ordered in the same manner that
they're declared within the class.
2019-10-17 17:51:06 -04:00
Lioncash 6414d9e2e5 core/memory/cheat_engine: Resolve -Wreorder warnings
Amends the initializer lists to be ordered in the same manner that
they're declared within the class.
2019-10-17 16:21:47 -04:00
Lioncash b77430df70 apm/controller: Make SetPerformanceConfiguration() use an array of pairs over a map
While a map is an OK way to do lookups (and usually recommended in most
cases), this is a map that lives for the entire duration of the program
and only deallocates its contents when the program terminates.

Given the total size of the map is quite small, we can simply use a
std::array of pairs and utilize std::find_if to perform the same
behavior without loss of performance.

This eliminates a static constructor and places the data into the
read-only segment.

While we're at it, we can also handle malformed inputs instead of
directly dereferencing the resulting iterator.
2019-10-17 16:13:14 -04:00
Lioncash 141d929929 apm/controller: Make GetCurrentPerformanceMode() a const member function
This doesn't modify instance state, so it can be made const qualified.
2019-10-17 15:58:25 -04:00
bunnei 9fe8072c67 Merge pull request #2980 from lioncash/warn
maxwell_3d: Silence truncation warnings
2019-10-17 14:02:16 -04:00
Rodrigo Locatti 60c602e4e7 Merge pull request #2978 from lioncash/doxygen
video_core/texture_cache: Amend Doxygen references
2019-10-16 22:09:40 -03:00
Rodrigo Locatti e00b529a89 Merge pull request #2982 from lioncash/surface
texture_cache: Avoid unnecessary surface copies within PickStrategy() and TryReconstructSurface()
2019-10-16 19:43:32 -03:00
bunnei ef9b31783d Merge pull request #2912 from FernandoS27/async-fixes
General fixes to Async GPU
2019-10-16 10:34:48 -04:00
Rodrigo Locatti 60315060b1 Merge pull request #2984 from lioncash/fallthrough2
video_core/surface: Add missing break in PixelFormatFromTextureFormat()
2019-10-15 23:08:34 -03:00
Lioncash cf9e13c255 video_core/surface: Add missing break in PixelFormatFromTextureFormat()
Prevents fallthrough into the following case.
2019-10-15 21:53:15 -04:00
Rodrigo Locatti 14f3cebcd4 Merge pull request #2981 from lioncash/copy
gl_shader_decompiler: Minor cleanup-related changes
2019-10-15 21:07:25 -03:00
Lioncash a24e8bf9cf texture_cache: Avoid unnecessary surface copies within PickStrategy() and TryReconstructSurface()
We can take these by const reference and avoid making unnecessary
copies, preventing some atomic reference count increments and
decrements.
2019-10-15 19:31:33 -04:00
Lioncash 77b4916b33 control_flow: Silence truncation warnings
This can be trivially fixed by making the input size a size_t.
CFGRebuildState's constructor parameter is already a std::size_t, so
this just makes the size type fully conform with it.
2019-10-15 19:10:28 -04:00
Lioncash 4f16ce9294 gl_shader_decompiler: Make ExprDecompiler's GetResult() a const member function
This is only ever used to read, but not write, the resulting string, so
we can enforce this by making it a const member function.
2019-10-15 19:02:59 -04:00
Lioncash 67df3f7742 gl_shader_decompiler: Use a std::string_view with GetDeclarationWithSuffix()
This allows the function to be completely non-allocating for inputs of
all sizes (i.e. there's no heap cost for an input to convert to a
std::string_view).
2019-10-15 19:00:48 -04:00
Lioncash 04a1161354 gl_shader_decompiler: Fold flow_var constant into GetFlowVariable()
This is only ever used within this function, so we can narrow it's scope
down.
2019-10-15 18:58:36 -04:00
Lioncash 2f2ab9b5bc gl_shader_decompiler: Mark ASTDecompiler/ExprDecompiler parameters as const references where applicable
These member functions don't actually modify the input parameter, so we
can make this explicit with the use of const.
2019-10-15 18:57:02 -04:00
Lioncash b8a62adcf1 gl_shader_decompiler: Pass by reference to GenerateTextureArgument()
Avoids an unnecessary atomic reference count increment and decrement.
2019-10-15 18:29:37 -04:00
Lioncash d1d7ce74d2 gl_shader_decompiler: Use std::holds_alternative within GenerateTexture()
This only ever queries if the type exists within the variant, but
doesn't actually do anything with the return value. We can just use
std::holds_alternative for this use case.
2019-10-15 18:25:48 -04:00
Lioncash 67658dd6e8 shader/node: std::move Meta instance within OperationNode constructor
Allows usages of the constructor to avoid an unnecessary copy.
2019-10-15 18:21:59 -04:00
Lioncash 9760795bfb gl_shader_decompiler: Avoid unnecessary copies of MetaImage
MetaImage contains a std::vector, so copying here could result in
unnecessary reallocations. Given the operation lives throughout the
entire scope, this is safe to do.
2019-10-15 18:14:55 -04:00
Lioncash c9c75f9587 maxwell_3d: Silence truncation warnings
A trivial warning caused by not using size_t as the argument types
instead of u32.
2019-10-15 17:51:35 -04:00
bunnei 2299950de1 Merge pull request #2972 from lioncash/system
{bcat, gpu, nvflinger}: Remove trivial usages of the global system accessor
2019-10-15 17:49:12 -04:00
bunnei ba0086e32d Merge pull request #2977 from lioncash/algorithm
common: Rename binary_find.h to algorithm.h
2019-10-15 16:41:52 -04:00
Lioncash b25b94400e video_core/gpu: Remove use of the global system accessor
We can just make use of the reference member variable instead of
accessing the global system instance.
2019-10-15 16:39:30 -04:00
Lioncash cc1d7048b5 bcat: Remove use of global system accessors
Removes all uses of the global system accessor within the BCAT
interface.
2019-10-15 16:39:27 -04:00
Lioncash 524eb15513 video_core/texture_cache: Amend Doxygen references
Amends the doxygen comments so that they properly resolve. While we're
at it, we can correct some typos and fix up some of the comments'
formatting in order to make them slightly nicer to read.
2019-10-15 15:40:00 -04:00
Lioncash d5706346d7 common/algorithm: Add description comment indicating intended algorithms
Makes it explicit that the header is intended for iterator-based
algorithms that can ideally operate on any type.
2019-10-15 15:25:23 -04:00
Lioncash ac4dbd3b25 common: Rename binary_find.h to algorithm.h
Makes the header more general for other potential algorithms in the
future. While we're at it, include a missing <functional> include to
satisfy the use of std::less.
2019-10-15 15:24:50 -04:00
Lioncash 574440d59f nvflinger/buffer_queue: Remove use of a global system accessor 2019-10-12 09:17:56 -04:00
Fernando Sahmkow cfc2f30dc4 AsyncGpu: Address Feedback 2019-10-11 13:41:15 -04:00
Fernando Sahmkow f32a49d3d8 Surfaces: Implement R4G4B4A4U format. 2019-10-09 12:57:02 -04:00
Fernando Sahmkow b9ddb517b1 Surfaces: Implement ASTC 6x6 10x10 12x12 8x6 6x5 2019-10-09 12:44:31 -04:00
Fernando Sahmkow 538f5880ff GL_Renderer: Remove lefting snippet. 2019-10-04 19:59:55 -04:00
Fernando Sahmkow 75395605d6 NvFlinger: Remove leftover from corrections and clang format. 2019-10-04 19:59:54 -04:00
Fernando Sahmkow 9f2719d1a4 Gl_Rasterizer: Protect CPU Memory mapping from multiple threads. 2019-10-04 19:59:53 -04:00
Fernando Sahmkow 3f104464de Core: Wait for GPU to be idle before shutting down. 2019-10-04 19:59:53 -04:00
Fernando Sahmkow 69fa2e6525 Nvdrv: Correct Event setup in Nvdrv
Events are supposed to be cleared on quering. This fixes that issue.
2019-10-04 19:59:52 -04:00
Fernando Sahmkow 782b7a0ca4 NVFlinger: Reverse the change that only signaled events on buffer acquire.
This has been hardware tested and it seems that NVFlinger will still 
signal even if there are no buffers to present.
2019-10-04 19:59:51 -04:00
Fernando Sahmkow ffc2ce89a0 Nvdrv: Do framelimiting only in the CPU Thread 2019-10-04 19:59:50 -04:00
Fernando Sahmkow 976d9ef43c NvFlinger: Don't swap buffers if a frame is missing and always trigger event in sync gpu. 2019-10-04 19:59:49 -04:00
Fernando Sahmkow 5b5e60ffec GPU_Async: Correct fences, display events and more.
This commit uses guest fences on vSync event instead of an articial fake 
fence we had.
It also corrects to keep signaling display events while loading the game 
as the OS is suppose to send buffers to vSync during that time.
2019-10-04 19:59:48 -04:00
Fernando Sahmkow 4e9f975935 Nvdrv: Correct Async regression and avoid signaling empty buffer vsyncs 2019-10-04 19:59:47 -04:00
42 changed files with 611 additions and 390 deletions

No files matched your search

+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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();
+13
View File
@@ -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.
+3 -2
View File
@@ -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));
+34 -14
View File
@@ -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;
}
+1 -1
View File
@@ -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));
+10 -2
View File
@@ -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
+19 -10
View File
@@ -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;
}
}
+8 -2
View File
@@ -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
+13 -11
View File
@@ -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 {
+1 -1
View File
@@ -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;
}
+3 -4
View File
@@ -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;
}
+2 -2
View File
@@ -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 -14
View File
@@ -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;
+3 -2
View File
@@ -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);
+1 -1
View File
@@ -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 {
+71
View File
@@ -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) {}
+8 -72
View File
@@ -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);
+48 -37
View File
@@ -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");
}
+3 -3
View File
@@ -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);
+16 -20
View File
@@ -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);
}
+5 -1
View File
@@ -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{};
+23 -16
View File
@@ -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)));
+4
View File
@@ -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;
};
+1 -1
View File
@@ -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)) {}
+3 -2
View File
@@ -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;
+1
View File
@@ -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"
+2 -3
View File
@@ -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)));
+164 -91
View File
@@ -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;
};