Compare commits

...

57 Commits

Author SHA1 Message Date
bunnei
c43eaa94f3 gl_shader_decompiler: Implement SEL instruction. 2018-07-22 00:37:12 -04:00
bunnei
4cd5df95d6 Merge pull request #761 from bunnei/improve-raster-cache
Improvements to rasterizer cache
2018-07-21 20:28:53 -07:00
bunnei
63fbf9a7d3 gl_rasterizer_cache: Blit surfaces on recreation instead of flush and load. 2018-07-21 21:51:06 -04:00
bunnei
4301f0b539 gl_rasterizer_cache: Use GPUVAddr as cache key, not parameter set. 2018-07-21 21:51:06 -04:00
bunnei
cd47391c2d gl_rasterizer_cache: Use zeta_width and zeta_height registers for depth buffer. 2018-07-21 21:51:06 -04:00
bunnei
d8c60029d6 gl_rasterizer: Use zeta_enable register to enable depth buffer. 2018-07-21 21:51:06 -04:00
bunnei
5287991a36 maxwell_3d: Add depth buffer enable, width, and height registers. 2018-07-21 21:51:05 -04:00
bunnei
53a219f163 Merge pull request #759 from lioncash/redundant
file_util: Remove redundant duplicate return in GetPathWithoutTop()
2018-07-21 18:50:38 -07:00
bunnei
3ac736c003 Merge pull request #748 from lioncash/namespace
video_core: Use nested namespaces where applicable
2018-07-21 18:50:14 -07:00
bunnei
f5e87f4ce1 Merge pull request #758 from lioncash/sync
common: Remove synchronized_wrapper.h
2018-07-21 18:30:31 -07:00
bunnei
9533875eeb Merge pull request #760 from lioncash/path
file_util: Use an enum class for GetUserPath()
2018-07-21 18:30:04 -07:00
bunnei
d95a1a3742 Merge pull request #762 from Subv/ioctl2
GPU: Implement the NVGPU_IOCTL_CHANNEL_KICKOFF_PB ioctl2 command.
2018-07-21 18:28:55 -07:00
Subv
5c49e56d41 GPU: Implement the NVGPU_IOCTL_CHANNEL_KICKOFF_PB ioctl2 command.
This behaves quite similarly to the SubmitGPFIFO command. Referenced from Ryujinx.
Many thanks to @gdkchan for investigating this!
2018-07-21 15:50:02 -05:00
Lioncash
d66b43dadf file_util: Use an enum class for GetUserPath()
Instead of using an unsigned int as a parameter and expecting a user to
always pass in the correct values, we can just convert the enum into an
enum class and use that type as the parameter type instead, which makes
the interface more type safe.

We also get rid of the bookkeeping "NUM_" element in the enum by just
using an unordered map. This function is generally low-frequency in
terms of calls (and I'd hope so, considering otherwise would mean we're
slamming the disk with IO all the time) so I'd consider this acceptable
in this case.
2018-07-21 16:21:19 -04:00
Lioncash
34d6a1349c file_util: Remove explicit type from std::min() in GetPathWithoutTop()
Given both operands are the same type, there won't be an issue with
overload selection that requires making this explicit.
2018-07-21 15:19:32 -04:00
Lioncash
41660c8923 file_util: Remove redundant duplicate return in GetPathWithoutTop() 2018-07-21 15:18:23 -04:00
Lioncash
973fdce79b common: Remove synchronized_wrapper.h
This is entirely unused in the codebase.
2018-07-21 14:51:44 -04:00
bunnei
0f20fa5a1e Merge pull request #754 from lioncash/part
partition_filesystem, vfs_real: Minor changes
2018-07-21 11:38:52 -07:00
bunnei
de7cb91995 Merge pull request #750 from lioncash/ctx
arm_interface: Remove unused tls_address member of ThreadContext
2018-07-21 11:38:16 -07:00
bunnei
867ba1ceee Merge pull request #756 from lioncash/dynarmic
externals: Update dynarmic to 7ea1241
2018-07-21 11:37:38 -07:00
bunnei
1c7c1347d8 Merge pull request #746 from lioncash/tests
tests/arm_test_common: Minor changes
2018-07-21 10:55:29 -07:00
bunnei
ff8754f921 Merge pull request #747 from lioncash/unimplemented
gl_shader_manager: Remove unimplemented function prototype
2018-07-21 10:54:58 -07:00
bunnei
89cc8c1617 Merge pull request #755 from lioncash/ctor
file_sys/errors: Remove redundant object constructor calls
2018-07-21 10:53:53 -07:00
bunnei
552aac7e6c Merge pull request #749 from lioncash/consistency
gpu: Rename Get3DEngine() to Maxwell3D()
2018-07-21 10:51:00 -07:00
bunnei
fe2498a650 Merge pull request #751 from Subv/tpidr_el0
CPU: Save and restore the TPIDR_EL0 system register on every context switch
2018-07-21 10:48:30 -07:00
bunnei
3d938b8c60 Merge pull request #753 from lioncash/const
vfs: Minor changes
2018-07-21 10:44:08 -07:00
Lioncash
1519ce7eab externals: Update dynarmic to 7ea1241
Resolves an issue with TPIDR setting being erroneously removed in the
dead code pass.
2018-07-21 13:25:14 -04:00
bunnei
d85cfc94e2 Merge pull request #752 from Subv/vfs_load
Loader: Only print the module names and addresses if they actually exist.
2018-07-20 22:57:18 -07:00
Lioncash
459e158340 file_sys/errors: Remove redundant object constructor calls
Given we're already constructing the error code, we don't need to call
the constructor inside of it.
2018-07-20 22:37:54 -04:00
Lioncash
b46c0ed1fa vfs_real: Remove redundant copying of std::vector instances in GetFiles() and GetSubdirectories()
We already return by value, so we don't explicitly need to make the
copy.
2018-07-20 22:30:22 -04:00
Lioncash
ec71915ede partition_filesystem, vfs_real: Add missing standard includes 2018-07-20 22:28:35 -04:00
Lioncash
d36e327ba6 partition_filesystem, vfs_real: Use std::move in ReplaceFileWithSubdirectory() where applicable
Avoids unnecessary atomic increment and decrement operations.
2018-07-20 22:23:58 -04:00
Lioncash
2b91386e15 partition_filesystem, vfs_real: Use std::distance() instead of subtraction
This is a little bit more self-documenting on what is being done here.
2018-07-20 22:19:17 -04:00
Lioncash
3e0727df1b vfs_offset: Simplify TrimToFit()
We can simply use std::clamp() here, instead of using an equivalent
with std::max() and std::min().
2018-07-20 22:04:37 -04:00
Lioncash
894b0de0f2 vfs: Make WriteBytes() overload taking a std::vector pass the std::vector by const reference
Given the data is intended to be directly written, there's no need to
take the std::vector by value and copy the data.
2018-07-20 21:51:30 -04:00
Lioncash
dd09439fee vfs: Use variable template variants of std::is_trivially_copyable
Provides the same behavior, but with less writing
2018-07-20 21:47:19 -04:00
Lioncash
05231d8b08 vfs: Amend constness on pointers in WriteBytes() and WriteArrays() member functions to be const qualified
These functions don't modify the data being pointed to, so these can be
pointers to const data
2018-07-20 21:40:15 -04:00
Subv
966874e357 Loader: Only print the module names and addresses if they actually exist. 2018-07-20 19:59:15 -05:00
Subv
d84eb9dac6 CPU: Save and restore the TPIDR_EL0 system register on every context switch.
Note that there's currently a dynarmic bug preventing this register from being written.
2018-07-20 19:57:45 -05:00
bunnei
8afc21f175 Merge pull request #743 from lioncash/view
logging: Use std::string_view where applicable
2018-07-20 17:17:04 -07:00
bunnei
d4104c72aa Merge pull request #745 from lioncash/package
param_package: Minor changes
2018-07-20 17:16:54 -07:00
Lioncash
ae09adfcb3 arm_interface: Remove unused tls_address member of ThreadContext
Currently, the TLS address is set within the scheduler, making this
member unused.
2018-07-20 18:57:40 -04:00
Lioncash
863579736c gpu: Rename Get3DEngine() to Maxwell3D()
This makes it match its const qualified equivalent.
2018-07-20 18:34:49 -04:00
Lioncash
bb960c8cb4 video_core: Use nested namespaces where applicable
Compresses a few namespace specifiers to be more compact.
2018-07-20 18:23:54 -04:00
bunnei
2b7d862366 Merge pull request #742 from bunnei/misc-apm
apm: Improve stub for GetPerformanceConfiguration.
2018-07-20 15:01:19 -07:00
bunnei
3acd6fa086 Merge pull request #741 from lioncash/enum
ipc_helpers: Add PushEnum() member function to ResponseBuilder
2018-07-20 15:00:44 -07:00
Lioncash
48733744bb arm_test_common: Get rid of truncation warnings
Explicitly cast the value to a u8 to show that this is intentional.
2018-07-20 17:53:53 -04:00
Lioncash
a8bb1eb39f arm_test_common: Make file static variable a member variable of the testing environment
Gets rid of file-static behavior.
2018-07-20 17:52:37 -04:00
Lioncash
a44475207c arm_test_common: Add missing header guard 2018-07-20 17:48:43 -04:00
Lioncash
3268321f4b param_package: Take std::string by value in string-based Set() function
Allows avoiding string copies by letting the strings be moved into the
function calls.
2018-07-20 17:24:06 -04:00
Lioncash
6279c2dcf4 param_package: Use std::unordered_map's insert_or_assign instead of map indexing
This avoids a redundant std::string construction if a key doesn't exist
in the map already.

e.g.

data[key] requires constructing a new default instance of the value in
the map (but this is wasteful, since we're already setting something
into the map over top of it).
2018-07-20 17:24:06 -04:00
Lioncash
474ec2ee5f param_package: Get rid of file-static std::string construction
Avoids potential dynamic allocation occuring during program launch
2018-07-20 17:24:02 -04:00
Lioncash
f63ccbd936 logging/filter: Use std::string_view in ParseFilterString()
Allows avoiding constructing std::string instances, since this only
reads an arbitrary sequence of characters.

We can also make ParseFilterRule() internal, since it doesn't depend on
any private instance state of Filter
2018-07-20 15:58:46 -04:00
Lioncash
7a1a860abe logging/backend: Add missing standard includes
A few inclusions were being satisfied indirectly. To prevent breakages
in the future, include these directly.
2018-07-20 15:31:27 -04:00
Lioncash
457d1b4490 logging/backend: Use std::string_view in RemoveBackend() and GetBackend()
These can just use a view to a string since its only comparing against
two names in both cases for matches. This avoids constructing
std::string instances where they aren't necessary.
2018-07-20 15:27:20 -04:00
bunnei
dffd154d6d apm: Improve stub for GetPerformanceConfiguration. 2018-07-20 15:20:01 -04:00
Lioncash
0a0b3c4b9f ipc_helpers: Add PushEnum() member function to ResponseBuilder
Allows pushing strongly-typed enum members without the need to always
cast them at the call sites.

Note that we *only* allow strongly-typed enums in this case. The reason
for this is that strongly typed enums have a guaranteed defined size, so
the size of the data being pushed is always deterministic. With regular
enums this can be a little more error-prone, so we disallow them.

This function simply uses the underlying type of the enum to determine
the size of the data. For example, if an enum is defined as:

enum class SomeEnum : u16 {
  SomeEntry
};

if PushEnum(SomeEnum::SomeEntry); is called, then it will push a
u16-size amount of data.
2018-07-20 15:00:58 -04:00
60 changed files with 538 additions and 424 deletions

View File

@@ -63,7 +63,6 @@ add_library(common STATIC
string_util.cpp
string_util.h
swap.h
synchronized_wrapper.h
telemetry.cpp
telemetry.h
thread.cpp

View File

@@ -26,7 +26,7 @@
#define USA_DIR "USA"
#define JAP_DIR "JAP"
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
// Subdirs in the User dir returned by GetUserPath(UserPath::UserDir)
#define CONFIG_DIR "config"
#define CACHE_DIR "cache"
#define SDMC_DIR "sdmc"
@@ -35,11 +35,11 @@
#define LOG_DIR "log"
// Filenames
// Files in the directory returned by GetUserPath(D_CONFIG_IDX)
// Files in the directory returned by GetUserPath(UserPath::ConfigDir)
#define EMU_CONFIG "emu.ini"
#define DEBUGGER_CONFIG "debugger.ini"
#define LOGGER_CONFIG "logger.ini"
// Files in the directory returned by GetUserPath(D_LOGS_IDX)
// Files in the directory returned by GetUserPath(UserPath::LogDir)
#define LOG_FILE "yuzu_log.txt"
// Sys files

View File

@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#include <sstream>
#include <unordered_map>
#include "common/assert.h"
#include "common/common_funcs.h"
#include "common/common_paths.h"
@@ -681,67 +682,68 @@ std::string GetSysDirectory() {
// Returns a string with a yuzu data dir or file in the user's home
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) {
static std::string paths[NUM_PATH_INDICES];
const std::string& GetUserPath(UserPath path, const std::string& new_path) {
static std::unordered_map<UserPath, std::string> paths;
auto& user_path = paths[UserPath::UserDir];
// Set up all paths and files on the first run
if (paths[D_USER_IDX].empty()) {
if (user_path.empty()) {
#ifdef _WIN32
paths[D_USER_IDX] = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
if (!FileUtil::IsDirectory(paths[D_USER_IDX])) {
paths[D_USER_IDX] = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
if (!FileUtil::IsDirectory(user_path)) {
user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
} else {
LOG_INFO(Common_Filesystem, "Using the local user directory");
}
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
#else
if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
paths[D_USER_IDX] = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
} else {
std::string data_dir = GetUserDirectory("XDG_DATA_HOME");
std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME");
std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME");
paths[D_USER_IDX] = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
paths[D_CONFIG_IDX] = config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
paths[D_CACHE_IDX] = cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
user_path = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP;
paths.emplace(UserPath::ConfigDir, config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
paths.emplace(UserPath::CacheDir, cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP);
}
#endif
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
paths[D_SYSDATA_IDX] = paths[D_USER_IDX] + SYSDATA_DIR DIR_SEP;
paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP);
paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
// TODO: Put the logs in a better location for each OS
paths[D_LOGS_IDX] = paths[D_USER_IDX] + LOG_DIR DIR_SEP;
paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP);
}
if (!newPath.empty()) {
if (!FileUtil::IsDirectory(newPath)) {
LOG_ERROR(Common_Filesystem, "Invalid path specified {}", newPath);
return paths[DirIDX];
if (!new_path.empty()) {
if (!FileUtil::IsDirectory(new_path)) {
LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
return paths[path];
} else {
paths[DirIDX] = newPath;
paths[path] = new_path;
}
switch (DirIDX) {
case D_ROOT_IDX:
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
switch (path) {
case UserPath::RootDir:
user_path = paths[UserPath::RootDir] + DIR_SEP;
break;
case D_USER_IDX:
paths[D_USER_IDX] = paths[D_ROOT_IDX] + DIR_SEP;
paths[D_CONFIG_IDX] = paths[D_USER_IDX] + CONFIG_DIR DIR_SEP;
paths[D_CACHE_IDX] = paths[D_USER_IDX] + CACHE_DIR DIR_SEP;
paths[D_SDMC_IDX] = paths[D_USER_IDX] + SDMC_DIR DIR_SEP;
paths[D_NAND_IDX] = paths[D_USER_IDX] + NAND_DIR DIR_SEP;
case UserPath::UserDir:
user_path = paths[UserPath::RootDir] + DIR_SEP;
paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP;
paths[UserPath::CacheDir] = user_path + CACHE_DIR DIR_SEP;
paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP;
paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP;
break;
}
}
return paths[DirIDX];
return paths[path];
}
size_t WriteStringToFile(bool text_file, const std::string& str, const char* filename) {
@@ -836,8 +838,7 @@ std::string GetPathWithoutTop(std::string path) {
}
const auto name_bck_index = path.find_first_of('\\');
const auto name_fwd_index = path.find_first_of('/');
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1);
return path.substr(std::min(name_bck_index, name_fwd_index) + 1);
}
std::string GetFilename(std::string path) {

View File

@@ -16,21 +16,20 @@
#include "common/string_util.h"
#endif
// User directory indices for GetUserPath
enum {
D_USER_IDX,
D_ROOT_IDX,
D_CONFIG_IDX,
D_CACHE_IDX,
D_SDMC_IDX,
D_NAND_IDX,
D_SYSDATA_IDX,
D_LOGS_IDX,
NUM_PATH_INDICES
};
namespace FileUtil {
// User paths for GetUserPath
enum class UserPath {
CacheDir,
ConfigDir,
LogDir,
NANDDir,
RootDir,
SDMCDir,
SysDataDir,
UserDir,
};
// FileSystem tree node/
struct FSTEntry {
bool isDirectory;
@@ -123,7 +122,7 @@ bool SetCurrentDir(const std::string& directory);
// Returns a pointer to a string with a yuzu data dir in the user's home
// directory. To be used in "multi-user" mode (that is, installed).
const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath = "");
const std::string& GetUserPath(UserPath path, const std::string& new_path = "");
// Returns the path to where the sys file are
std::string GetSysDirectory();

View File

@@ -3,19 +3,20 @@
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <climits>
#include <condition_variable>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#ifdef _WIN32
#include <share.h> // For _SH_DENYWR
#else
#define _SH_DENYWR 0
#endif
#include "common/assert.h"
#include "common/common_funcs.h" // snprintf compatibility define
#include "common/logging/backend.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
@@ -48,11 +49,11 @@ public:
backends.push_back(std::move(backend));
}
void RemoveBackend(const std::string& backend_name) {
void RemoveBackend(std::string_view backend_name) {
std::lock_guard<std::mutex> lock(writing_mutex);
auto it = std::remove_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
return !strcmp(i->GetName(), backend_name.c_str());
});
const auto it =
std::remove_if(backends.begin(), backends.end(),
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
backends.erase(it, backends.end());
}
@@ -64,10 +65,10 @@ public:
filter = f;
}
Backend* GetBackend(const std::string& backend_name) {
auto it = std::find_if(backends.begin(), backends.end(), [&backend_name](const auto& i) {
return !strcmp(i->GetName(), backend_name.c_str());
});
Backend* GetBackend(std::string_view backend_name) {
const auto it =
std::find_if(backends.begin(), backends.end(),
[&backend_name](const auto& i) { return backend_name == i->GetName(); });
if (it == backends.end())
return nullptr;
return it->get();
@@ -265,11 +266,11 @@ void AddBackend(std::unique_ptr<Backend> backend) {
Impl::Instance().AddBackend(std::move(backend));
}
void RemoveBackend(const std::string& backend_name) {
void RemoveBackend(std::string_view backend_name) {
Impl::Instance().RemoveBackend(backend_name);
}
Backend* GetBackend(const std::string& backend_name) {
Backend* GetBackend(std::string_view backend_name) {
return Impl::Instance().GetBackend(backend_name);
}

View File

@@ -4,10 +4,9 @@
#pragma once
#include <chrono>
#include <cstdarg>
#include <memory>
#include <string>
#include <utility>
#include <string_view>
#include "common/file_util.h"
#include "common/logging/filter.h"
#include "common/logging/log.h"
@@ -106,9 +105,9 @@ private:
void AddBackend(std::unique_ptr<Backend> backend);
void RemoveBackend(const std::string& backend_name);
void RemoveBackend(std::string_view backend_name);
Backend* GetBackend(const std::string& backend_name);
Backend* GetBackend(std::string_view backend_name);
/**
* Returns the name of the passed log class as a C-string. Subclasses are separated by periods

View File

@@ -8,39 +8,9 @@
#include "common/string_util.h"
namespace Log {
Filter::Filter(Level default_level) {
ResetAll(default_level);
}
void Filter::ResetAll(Level level) {
class_levels.fill(level);
}
void Filter::SetClassLevel(Class log_class, Level level) {
class_levels[static_cast<size_t>(log_class)] = level;
}
void Filter::ParseFilterString(const std::string& filter_str) {
auto clause_begin = filter_str.cbegin();
while (clause_begin != filter_str.cend()) {
auto clause_end = std::find(clause_begin, filter_str.cend(), ' ');
// If clause isn't empty
if (clause_end != clause_begin) {
ParseFilterRule(clause_begin, clause_end);
}
if (clause_end != filter_str.cend()) {
// Skip over the whitespace
++clause_end;
}
clause_begin = clause_end;
}
}
namespace {
template <typename It>
static Level GetLevelByName(const It begin, const It end) {
Level GetLevelByName(const It begin, const It end) {
for (u8 i = 0; i < static_cast<u8>(Level::Count); ++i) {
const char* level_name = GetLevelName(static_cast<Level>(i));
if (Common::ComparePartialString(begin, end, level_name)) {
@@ -51,7 +21,7 @@ static Level GetLevelByName(const It begin, const It end) {
}
template <typename It>
static Class GetClassByName(const It begin, const It end) {
Class GetClassByName(const It begin, const It end) {
for (ClassType i = 0; i < static_cast<ClassType>(Class::Count); ++i) {
const char* level_name = GetLogClassName(static_cast<Class>(i));
if (Common::ComparePartialString(begin, end, level_name)) {
@@ -61,8 +31,8 @@ static Class GetClassByName(const It begin, const It end) {
return Class::Count;
}
bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const std::string::const_iterator end) {
template <typename Iterator>
bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) {
auto level_separator = std::find(begin, end, ':');
if (level_separator == end) {
LOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: {}",
@@ -77,7 +47,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
}
if (Common::ComparePartialString(begin, level_separator, "*")) {
ResetAll(level);
instance.ResetAll(level);
return true;
}
@@ -87,9 +57,40 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
return false;
}
SetClassLevel(log_class, level);
instance.SetClassLevel(log_class, level);
return true;
}
} // Anonymous namespace
Filter::Filter(Level default_level) {
ResetAll(default_level);
}
void Filter::ResetAll(Level level) {
class_levels.fill(level);
}
void Filter::SetClassLevel(Class log_class, Level level) {
class_levels[static_cast<size_t>(log_class)] = level;
}
void Filter::ParseFilterString(std::string_view filter_view) {
auto clause_begin = filter_view.cbegin();
while (clause_begin != filter_view.cend()) {
auto clause_end = std::find(clause_begin, filter_view.cend(), ' ');
// If clause isn't empty
if (clause_end != clause_begin) {
ParseFilterRule(*this, clause_begin, clause_end);
}
if (clause_end != filter_view.cend()) {
// Skip over the whitespace
++clause_end;
}
clause_begin = clause_end;
}
}
bool Filter::CheckMessage(Class log_class, Level level) const {
return static_cast<u8>(level) >= static_cast<u8>(class_levels[static_cast<size_t>(log_class)]);

View File

@@ -6,7 +6,7 @@
#include <array>
#include <cstddef>
#include <string>
#include <string_view>
#include "common/logging/log.h"
namespace Log {
@@ -40,9 +40,7 @@ public:
* - `Service:Info` -- Sets the level of Service to Info.
* - `Service.FS:Trace` -- Sets the level of the Service.FS class to Trace.
*/
void ParseFilterString(const std::string& filter_str);
bool ParseFilterRule(const std::string::const_iterator start,
const std::string::const_iterator end);
void ParseFilterString(std::string_view filter_view);
/// Matches class/level combination against the filter, returning true if it passed.
bool CheckMessage(Class log_class, Level level) const;

View File

@@ -3,7 +3,9 @@
// Refer to the license.txt file included.
#include <array>
#include <utility>
#include <vector>
#include "common/logging/log.h"
#include "common/param_package.h"
#include "common/string_util.h"
@@ -12,10 +14,11 @@ namespace Common {
constexpr char KEY_VALUE_SEPARATOR = ':';
constexpr char PARAM_SEPARATOR = ',';
constexpr char ESCAPE_CHARACTER = '$';
const std::string KEY_VALUE_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '0'};
const std::string PARAM_SEPARATOR_ESCAPE{ESCAPE_CHARACTER, '1'};
const std::string ESCAPE_CHARACTER_ESCAPE{ESCAPE_CHARACTER, '2'};
constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
ParamPackage::ParamPackage(const std::string& serialized) {
std::vector<std::string> pairs;
@@ -35,7 +38,7 @@ ParamPackage::ParamPackage(const std::string& serialized) {
part = Common::ReplaceAll(part, ESCAPE_CHARACTER_ESCAPE, {ESCAPE_CHARACTER});
}
Set(key_value[0], key_value[1]);
Set(key_value[0], std::move(key_value[1]));
}
}
@@ -101,16 +104,16 @@ float ParamPackage::Get(const std::string& key, float default_value) const {
}
}
void ParamPackage::Set(const std::string& key, const std::string& value) {
data[key] = value;
void ParamPackage::Set(const std::string& key, std::string value) {
data.insert_or_assign(key, std::move(value));
}
void ParamPackage::Set(const std::string& key, int value) {
data[key] = std::to_string(value);
data.insert_or_assign(key, std::to_string(value));
}
void ParamPackage::Set(const std::string& key, float value) {
data[key] = std::to_string(value);
data.insert_or_assign(key, std::to_string(value));
}
bool ParamPackage::Has(const std::string& key) const {

View File

@@ -28,7 +28,7 @@ public:
std::string Get(const std::string& key, const std::string& default_value) const;
int Get(const std::string& key, int default_value) const;
float Get(const std::string& key, float default_value) const;
void Set(const std::string& key, const std::string& value);
void Set(const std::string& key, std::string value);
void Set(const std::string& key, int value);
void Set(const std::string& key, float value);
bool Has(const std::string& key) const;

View File

@@ -1,85 +0,0 @@
// Copyright 2015 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <algorithm>
#include <mutex>
namespace Common {
template <typename T>
class SynchronizedWrapper;
/**
* Synchronized reference, that keeps a SynchronizedWrapper's mutex locked during its lifetime. This
* greatly reduces the chance that someone will access the wrapped resource without locking the
* mutex.
*/
template <typename T>
class SynchronizedRef {
public:
SynchronizedRef(SynchronizedWrapper<T>& wrapper) : wrapper(&wrapper) {
wrapper.mutex.lock();
}
SynchronizedRef(SynchronizedRef&) = delete;
SynchronizedRef(SynchronizedRef&& o) : wrapper(o.wrapper) {
o.wrapper = nullptr;
}
~SynchronizedRef() {
if (wrapper)
wrapper->mutex.unlock();
}
SynchronizedRef& operator=(SynchronizedRef&) = delete;
SynchronizedRef& operator=(SynchronizedRef&& o) {
std::swap(wrapper, o.wrapper);
return *this;
}
T& operator*() {
return wrapper->data;
}
const T& operator*() const {
return wrapper->data;
}
T* operator->() {
return &wrapper->data;
}
const T* operator->() const {
return &wrapper->data;
}
private:
SynchronizedWrapper<T>* wrapper;
};
/**
* Wraps an object, only allowing access to it via a locking reference wrapper. Good to ensure no
* one forgets to lock a mutex before acessing an object. To access the wrapped object construct a
* SyncronizedRef on this wrapper. Inspired by Rust's Mutex type
* (http://doc.rust-lang.org/std/sync/struct.Mutex.html).
*/
template <typename T>
class SynchronizedWrapper {
public:
template <typename... Args>
SynchronizedWrapper(Args&&... args) : data(std::forward<Args>(args)...) {}
SynchronizedRef<T> Lock() {
return {*this};
}
private:
template <typename U>
friend class SynchronizedRef;
std::mutex mutex;
T data;
};
} // namespace Common

View File

@@ -20,9 +20,6 @@ public:
u64 cpsr;
std::array<u128, 32> fpu_registers;
u64 fpscr;
// TODO(bunnei): Fix once we have proper support for tpidrro_el0, etc. in the JIT
VAddr tls_address;
};
/// Runs the CPU until an event happens
@@ -104,6 +101,10 @@ public:
virtual void SetTlsAddress(VAddr address) = 0;
virtual u64 GetTPIDR_EL0() const = 0;
virtual void SetTPIDR_EL0(u64 value) = 0;
/**
* Saves the current CPU context
* @param ctx Thread context to save

View File

@@ -196,6 +196,14 @@ void ARM_Dynarmic::SetTlsAddress(u64 address) {
cb->tpidrro_el0 = address;
}
u64 ARM_Dynarmic::GetTPIDR_EL0() const {
return cb->tpidr_el0;
}
void ARM_Dynarmic::SetTPIDR_EL0(u64 value) {
cb->tpidr_el0 = value;
}
void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.cpu_registers = jit->GetRegisters();
ctx.sp = jit->GetSP();
@@ -203,7 +211,6 @@ void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) {
ctx.cpsr = jit->GetPstate();
ctx.fpu_registers = jit->GetVectors();
ctx.fpscr = jit->GetFpcr();
ctx.tls_address = cb->tpidrro_el0;
}
void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
@@ -213,7 +220,6 @@ void ARM_Dynarmic::LoadContext(const ARM_Interface::ThreadContext& ctx) {
jit->SetPstate(static_cast<u32>(ctx.cpsr));
jit->SetVectors(ctx.fpu_registers);
jit->SetFpcr(static_cast<u32>(ctx.fpscr));
cb->tpidrro_el0 = ctx.tls_address;
}
void ARM_Dynarmic::PrepareReschedule() {

View File

@@ -34,6 +34,8 @@ public:
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;

View File

@@ -169,6 +169,16 @@ void ARM_Unicorn::SetTlsAddress(VAddr base) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
}
u64 ARM_Unicorn::GetTPIDR_EL0() const {
u64 value{};
CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
return value;
}
void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
}
void ARM_Unicorn::Run() {
if (GDBStub::IsServerEnabled()) {
ExecuteInstructions(std::max(4000000, 0));
@@ -220,8 +230,6 @@ void ARM_Unicorn::SaveContext(ARM_Interface::ThreadContext& ctx) {
CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31));
ctx.tls_address = GetTlsAddress();
for (int i = 0; i < 32; ++i) {
uregs[i] = UC_ARM64_REG_Q0 + i;
tregs[i] = &ctx.fpu_registers[i];
@@ -249,8 +257,6 @@ void ARM_Unicorn::LoadContext(const ARM_Interface::ThreadContext& ctx) {
CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31));
SetTlsAddress(ctx.tls_address);
for (auto i = 0; i < 32; ++i) {
uregs[i] = UC_ARM64_REG_Q0 + i;
tregs[i] = (void*)&ctx.fpu_registers[i];

View File

@@ -28,6 +28,8 @@ public:
void SetCPSR(u32 cpsr) override;
VAddr GetTlsAddress() const override;
void SetTlsAddress(VAddr address) override;
void SetTPIDR_EL0(u64 value) override;
u64 GetTPIDR_EL0() const override;
void SaveContext(ThreadContext& ctx) override;
void LoadContext(const ThreadContext& ctx) override;
void PrepareReschedule() override;

View File

@@ -20,13 +20,13 @@ enum {
constexpr ResultCode ERROR_PATH_NOT_FOUND(ErrorModule::FS, ErrCodes::NotFound);
// TODO(bunnei): Replace these with correct errors for Switch OS
constexpr ResultCode ERROR_INVALID_PATH(ResultCode(-1));
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(ResultCode(-1));
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(ResultCode(-1));
constexpr ResultCode ERROR_FILE_NOT_FOUND(ResultCode(-1));
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(ResultCode(-1));
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(ResultCode(-1));
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(ResultCode(-1));
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(ResultCode(-1));
constexpr ResultCode ERROR_INVALID_PATH(-1);
constexpr ResultCode ERROR_UNSUPPORTED_OPEN_FLAGS(-1);
constexpr ResultCode ERROR_INVALID_OPEN_FLAGS(-1);
constexpr ResultCode ERROR_FILE_NOT_FOUND(-1);
constexpr ResultCode ERROR_UNEXPECTED_FILE_OR_DIRECTORY(-1);
constexpr ResultCode ERROR_DIRECTORY_ALREADY_EXISTS(-1);
constexpr ResultCode ERROR_FILE_ALREADY_EXISTS(-1);
constexpr ResultCode ERROR_DIRECTORY_NOT_EMPTY(-1);
} // namespace FileSys

View File

@@ -2,7 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstddef>
#include <cstring>
#include <iterator>
#include <utility>
#include "common/file_util.h"
#include "common/logging/log.h"
#include "core/file_sys/partition_filesystem.h"
@@ -99,14 +104,15 @@ void PartitionFilesystem::PrintDebugInfo() const {
}
bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
const auto iter = std::find(pfs_files.begin(), pfs_files.end(), file);
if (iter == pfs_files.end())
return false;
pfs_files[iter - pfs_files.begin()] = pfs_files.back();
const std::ptrdiff_t offset = std::distance(pfs_files.begin(), iter);
pfs_files[offset] = pfs_files.back();
pfs_files.pop_back();
pfs_dirs.emplace_back(dir);
pfs_dirs.emplace_back(std::move(dir));
return true;
}

View File

@@ -42,7 +42,7 @@ bool VfsFile::WriteByte(u8 data, size_t offset) {
return Write(&data, 1, offset) == 1;
}
size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) {
size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
return Write(data.data(), data.size(), offset);
}

View File

@@ -59,8 +59,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
template <typename T>
size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
}
@@ -69,8 +68,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes read successfully.
template <typename T>
size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), size, offset);
}
@@ -78,8 +76,7 @@ struct VfsFile : NonCopyable {
// Returns the number of bytes read successfully (sizeof(T)).
template <typename T>
size_t ReadObject(T* data, size_t offset = 0) const {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
}
@@ -88,33 +85,29 @@ struct VfsFile : NonCopyable {
virtual bool WriteByte(u8 data, size_t offset = 0);
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
// written.
virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0);
virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0);
// Writes an array of type T, size number_elements to offset in file.
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
template <typename T>
size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(data, number_elements * sizeof(T), offset);
}
// Writes size bytes starting at memory location data to offset in file.
// Returns the number of bytes written successfully.
template <typename T>
size_t WriteBytes(T* data, size_t size, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
return Write(reinterpret_cast<u8*>(data), size, offset);
size_t WriteBytes(const T* data, size_t size, size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(reinterpret_cast<const u8*>(data), size, offset);
}
// Writes one object of type T to offset in file.
// Returns the number of bytes written successfully (sizeof(T)).
template <typename T>
size_t WriteObject(const T& data, size_t offset = 0) {
static_assert(std::is_trivially_copyable<T>::value,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(&data, sizeof(T), offset);
}

View File

@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <utility>
#include "core/file_sys/vfs_offset.h"
@@ -75,7 +76,7 @@ bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) {
return false;
}
size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) {
size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
}
@@ -88,7 +89,7 @@ size_t OffsetVfsFile::GetOffset() const {
}
size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0);
return std::clamp(r_size, size_t{0}, size - r_offset);
}
} // namespace FileSys

View File

@@ -28,7 +28,7 @@ struct OffsetVfsFile : public VfsFile {
std::vector<u8> ReadBytes(size_t size, size_t offset) const override;
std::vector<u8> ReadAllBytes() const override;
bool WriteByte(u8 data, size_t offset) override;
size_t WriteBytes(std::vector<u8> data, size_t offset) override;
size_t WriteBytes(const std::vector<u8>& data, size_t offset) override;
bool Rename(const std::string& name) override;

View File

@@ -2,6 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <cstddef>
#include <iterator>
#include <utility>
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "core/file_sys/vfs_real.h"
@@ -104,11 +109,11 @@ RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_)
}
std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const {
return std::vector<std::shared_ptr<VfsFile>>(files);
return files;
}
std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const {
return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories);
return subdirectories;
}
bool RealVfsDirectory::IsWritable() const {
@@ -163,14 +168,15 @@ bool RealVfsDirectory::Rename(const std::string& name) {
}
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
auto iter = std::find(files.begin(), files.end(), file);
const auto iter = std::find(files.begin(), files.end(), file);
if (iter == files.end())
return false;
files[iter - files.begin()] = files.back();
const std::ptrdiff_t offset = std::distance(files.begin(), iter);
files[offset] = files.back();
files.pop_back();
subdirectories.emplace_back(dir);
subdirectories.emplace_back(std::move(dir));
return true;
}

View File

@@ -174,6 +174,25 @@ public:
template <typename First, typename... Other>
void Push(const First& first_value, const Other&... other_values);
/**
* Helper function for pushing strongly-typed enumeration values.
*
* @tparam Enum The enumeration type to be pushed
*
* @param value The value to push.
*
* @note The underlying size of the enumeration type is the size of the
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
* push a u16-sized amount of data.
*/
template <typename Enum>
void PushEnum(Enum value) {
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
static_assert(!std::is_convertible_v<Enum, int>,
"enum type in PushEnum must be a strongly typed enum.");
Push(static_cast<std::underlying_type_t<Enum>>(value));
}
/**
* @brief Copies the content of the given trivially copyable class to the buffer as a normal
* param

View File

@@ -56,6 +56,8 @@ void Scheduler::SwitchContext(Thread* new_thread) {
if (previous_thread) {
previous_thread->last_running_ticks = CoreTiming::GetTicks();
cpu_core->SaveContext(previous_thread->context);
// Save the TPIDR_EL0 system register in case it was modified.
previous_thread->tpidr_el0 = cpu_core->GetTPIDR_EL0();
if (previous_thread->status == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
@@ -87,6 +89,7 @@ void Scheduler::SwitchContext(Thread* new_thread) {
cpu_core->LoadContext(new_thread->context);
cpu_core->SetTlsAddress(new_thread->GetTLSAddress());
cpu_core->SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core->ClearExclusiveState();
} else {
current_thread = nullptr;

View File

@@ -312,6 +312,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
thread->status = ThreadStatus::Dormant;
thread->entry_point = entry_point;
thread->stack_top = stack_top;
thread->tpidr_el0 = 0;
thread->nominal_priority = thread->current_priority = priority;
thread->last_running_ticks = CoreTiming::GetTicks();
thread->processor_id = processor_id;

View File

@@ -182,6 +182,14 @@ public:
return tls_address;
}
/*
* Returns the value of the TPIDR_EL0 Read/Write system register for this thread.
* @returns The value of the TPIDR_EL0 register.
*/
u64 GetTPIDR_EL0() const {
return tpidr_el0;
}
/*
* Returns the address of the current thread's command buffer, located in the TLS.
* @returns VAddr of the thread's command buffer.
@@ -213,6 +221,7 @@ public:
s32 processor_id;
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
u64 tpidr_el0; ///< TPIDR_EL0 read/write system register.
SharedPtr<Process> owner_process; ///< Process that owns this thread

View File

@@ -20,6 +20,21 @@ public:
}
private:
enum class PerformanceConfiguration : u32 {
Config1 = 0x00010000,
Config2 = 0x00010001,
Config3 = 0x00010002,
Config4 = 0x00020000,
Config5 = 0x00020001,
Config6 = 0x00020002,
Config7 = 0x00020003,
Config8 = 0x00020004,
Config9 = 0x00020005,
Config10 = 0x00020006,
Config11 = 0x92220007,
Config12 = 0x92220008,
};
void SetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -40,7 +55,7 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0); // Performance configuration
rb.Push<u32>(static_cast<u32>(PerformanceConfiguration::Config1));
LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode));
}

View File

@@ -272,9 +272,9 @@ void RegisterFileSystems() {
sdmc_factory = nullptr;
auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(D_NAND_IDX), FileSys::Mode::Write);
FileUtil::GetUserPath(FileUtil::UserPath::NANDDir), FileSys::Mode::Write);
auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>(
FileUtil::GetUserPath(D_SDMC_IDX), FileSys::Mode::Write);
FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir), FileSys::Mode::Write);
auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
save_data_factory = std::move(savedata);

View File

@@ -42,7 +42,7 @@ PL_U::PL_U() : ServiceFramework("pl:u") {
RegisterHandlers(functions);
// Attempt to load shared font data from disk
const std::string filepath{FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT};
const std::string filepath{FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir) + SHARED_FONT};
FileUtil::CreateFullPath(filepath); // Create path if not already created
FileUtil::IOFile file(filepath, "rb");

View File

@@ -42,6 +42,9 @@ u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u
if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) {
return SubmitGPFIFO(input, output);
}
if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
return KickoffPB(input, output);
}
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
@@ -127,14 +130,37 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
params.gpfifo, params.num_entries, params.flags);
params.address, params.num_entries, params.flags);
auto entries = std::vector<IoctlGpfifoEntry>();
entries.resize(params.num_entries);
std::memcpy(&entries[0], &input.data()[sizeof(IoctlSubmitGpfifo)],
params.num_entries * sizeof(IoctlGpfifoEntry));
for (auto entry : entries) {
VAddr va_addr = entry.Address();
Tegra::GPUVAddr va_addr = entry.Address();
Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
}
params.fence_out.id = 0;
params.fence_out.value = 0;
std::memcpy(output.data(), &params, output.size());
return 0;
}
u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
params.address, params.num_entries, params.flags);
std::vector<IoctlGpfifoEntry> entries(params.num_entries);
Memory::ReadBlock(params.address, entries.data(),
params.num_entries * sizeof(IoctlGpfifoEntry));
for (auto entry : entries) {
Tegra::GPUVAddr va_addr = entry.Address();
Core::System::GetInstance().GPU().ProcessCommandList(va_addr, entry.sz);
}
params.fence_out.id = 0;

View File

@@ -15,6 +15,7 @@ namespace Service::Nvidia::Devices {
class nvmap;
constexpr u32 NVGPU_IOCTL_MAGIC('H');
constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
class nvhost_gpu final : public nvdevice {
public:
@@ -158,14 +159,14 @@ private:
BitField<31, 1, u32_le> unk2;
};
VAddr Address() const {
return (static_cast<VAddr>(gpu_va_hi) << 32) | entry0;
Tegra::GPUVAddr Address() const {
return (static_cast<Tegra::GPUVAddr>(gpu_va_hi) << 32) | entry0;
}
};
static_assert(sizeof(IoctlGpfifoEntry) == 8, "IoctlGpfifoEntry is incorrect size");
struct IoctlSubmitGpfifo {
u64_le gpfifo; // (ignored) pointer to gpfifo fence structs
u64_le address; // pointer to gpfifo entry structs
u32_le num_entries; // number of fence objects being submitted
u32_le flags;
IoctlFence fence_out; // returned new fence object for others to wait on
@@ -193,6 +194,7 @@ private:
u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);

View File

@@ -101,7 +101,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
{8, &NVDRV::SetClientPID, "SetClientPID"},
{9, nullptr, "DumpGraphicsMemoryInfo"},
{10, nullptr, "InitializeDevtools"},
{11, nullptr, "Ioctl2"},
{11, &NVDRV::Ioctl, "Ioctl2"},
{12, nullptr, "Ioctl3"},
{13, &NVDRV::FinishInitialize, "FinishInitialize"},
};

View File

@@ -83,16 +83,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR};
for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3",
"subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) {
const VAddr load_addr = next_load_addr;
const FileSys::VirtualFile module_file = dir->GetFile(module);
if (module_file != nullptr)
if (module_file != nullptr) {
const VAddr load_addr = next_load_addr;
next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr);
if (next_load_addr) {
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr);
// Register module with GDBStub
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
} else {
next_load_addr = load_addr;
}
}

View File

@@ -37,7 +37,8 @@ static u64 GenerateTelemetryId() {
u64 GetTelemetryId() {
u64 telemetry_id{};
static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
static const std::string& filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
"telemetry_id"};
if (FileUtil::Exists(filename)) {
FileUtil::IOFile file(filename, "rb");
@@ -61,7 +62,8 @@ u64 GetTelemetryId() {
u64 RegenerateTelemetryId() {
const u64 new_telemetry_id{GenerateTelemetryId()};
static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"};
static const std::string& filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
"telemetry_id"};
FileUtil::IOFile file(filename, "wb");
if (!file.IsOpen()) {

View File

@@ -10,8 +10,6 @@
namespace ArmTests {
static Memory::PageTable* page_table = nullptr;
TestEnvironment::TestEnvironment(bool mutable_memory_)
: mutable_memory(mutable_memory_), test_memory(std::make_shared<TestMemory>(this)) {
@@ -67,10 +65,13 @@ boost::optional<bool> TestEnvironment::TestMemory::IsValidAddress(VAddr addr) {
}
boost::optional<u8> TestEnvironment::TestMemory::Read8(VAddr addr) {
auto iter = data.find(addr);
const auto iter = data.find(addr);
if (iter == data.end()) {
return addr; // Some arbitrary data
// Some arbitrary data
return static_cast<u8>(addr);
}
return iter->second;
}

View File

@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <tuple>
#include <unordered_map>
#include <vector>
@@ -9,6 +11,10 @@
#include "common/common_types.h"
#include "core/memory_hook.h"
namespace Memory {
struct PageTable;
}
namespace ArmTests {
struct WriteRecord {
@@ -79,6 +85,7 @@ private:
bool mutable_memory;
std::shared_ptr<TestMemory> test_memory;
std::vector<WriteRecord> write_records;
Memory::PageTable* page_table = nullptr;
};
} // namespace ArmTests

View File

@@ -6,8 +6,7 @@
#include "video_core/engines/fermi_2d.h"
#include "video_core/textures/decoders.h"
namespace Tegra {
namespace Engines {
namespace Tegra::Engines {
Fermi2D::Fermi2D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
@@ -69,5 +68,4 @@ void Fermi2D::HandleSurfaceCopy() {
}
}
} // namespace Engines
} // namespace Tegra
} // namespace Tegra::Engines

View File

@@ -12,8 +12,7 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace Tegra {
namespace Engines {
namespace Tegra::Engines {
#define FERMI2D_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::Fermi2D::Regs, field_name) / sizeof(u32))
@@ -110,5 +109,4 @@ ASSERT_REG_POSITION(operation, 0xAB);
ASSERT_REG_POSITION(trigger, 0xB5);
#undef ASSERT_REG_POSITION
} // namespace Engines
} // namespace Tegra
} // namespace Tegra::Engines

View File

@@ -17,8 +17,7 @@
#include "video_core/memory_manager.h"
#include "video_core/textures/texture.h"
namespace Tegra {
namespace Engines {
namespace Tegra::Engines {
#define MAXWELL3D_REG_INDEX(field_name) \
(offsetof(Tegra::Engines::Maxwell3D::Regs, field_name) / sizeof(u32))
@@ -488,7 +487,12 @@ public:
};
} rt_control;
INSERT_PADDING_WORDS(0x2B);
INSERT_PADDING_WORDS(0x2);
u32 zeta_width;
u32 zeta_height;
INSERT_PADDING_WORDS(0x27);
u32 depth_test_enable;
@@ -541,7 +545,11 @@ public:
u32 vb_element_base;
INSERT_PADDING_WORDS(0x49);
INSERT_PADDING_WORDS(0x40);
u32 zeta_enable;
INSERT_PADDING_WORDS(0x8);
struct {
u32 tsc_address_high;
@@ -866,6 +874,8 @@ ASSERT_REG_POSITION(clear_depth, 0x364);
ASSERT_REG_POSITION(zeta, 0x3F8);
ASSERT_REG_POSITION(vertex_attrib_format[0], 0x458);
ASSERT_REG_POSITION(rt_control, 0x487);
ASSERT_REG_POSITION(zeta_width, 0x48a);
ASSERT_REG_POSITION(zeta_height, 0x48b);
ASSERT_REG_POSITION(depth_test_enable, 0x4B3);
ASSERT_REG_POSITION(independent_blend_enable, 0x4B9);
ASSERT_REG_POSITION(depth_write_enabled, 0x4BA);
@@ -875,6 +885,7 @@ ASSERT_REG_POSITION(blend, 0x4CF);
ASSERT_REG_POSITION(stencil, 0x4E0);
ASSERT_REG_POSITION(screen_y_control, 0x4EB);
ASSERT_REG_POSITION(vb_element_base, 0x50D);
ASSERT_REG_POSITION(zeta_enable, 0x54E);
ASSERT_REG_POSITION(tsc, 0x557);
ASSERT_REG_POSITION(tic, 0x55D);
ASSERT_REG_POSITION(stencil_two_side, 0x565);
@@ -898,5 +909,4 @@ ASSERT_REG_POSITION(tex_info_buffers.size[0], 0xD2F);
#undef ASSERT_REG_POSITION
} // namespace Engines
} // namespace Tegra
} // namespace Tegra::Engines

View File

@@ -6,8 +6,7 @@
#include "common/common_types.h"
namespace Tegra {
namespace Engines {
namespace Tegra::Engines {
class MaxwellCompute final {
public:
@@ -18,5 +17,4 @@ public:
void WriteReg(u32 method, u32 value);
};
} // namespace Engines
} // namespace Tegra
} // namespace Tegra::Engines

View File

@@ -12,8 +12,7 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace Tegra {
namespace Engines {
namespace Tegra::Engines {
class MaxwellDMA final {
public:
@@ -151,5 +150,4 @@ ASSERT_REG_POSITION(src_params, 0x1CA);
#undef ASSERT_REG_POSITION
} // namespace Engines
} // namespace Tegra
} // namespace Tegra::Engines

View File

@@ -15,8 +15,7 @@
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Tegra {
namespace Shader {
namespace Tegra::Shader {
struct Register {
/// Number of registers
@@ -109,8 +108,7 @@ union Sampler {
u64 value{};
};
} // namespace Shader
} // namespace Tegra
} // namespace Tegra::Shader
namespace std {
@@ -127,8 +125,7 @@ struct make_unsigned<Tegra::Shader::Register> {
} // namespace std
namespace Tegra {
namespace Shader {
namespace Tegra::Shader {
enum class Pred : u64 {
UnusedIndex = 0x7,
@@ -291,6 +288,11 @@ union Instruction {
BitField<49, 1, u64> negate_a;
} alu_integer;
union {
BitField<39, 3, u64> pred;
BitField<42, 1, u64> neg_pred;
} sel;
union {
BitField<39, 3, u64> pred;
BitField<42, 1, u64> negate_pred;
@@ -516,6 +518,9 @@ public:
ISCADD_C, // Scale and Add
ISCADD_R,
ISCADD_IMM,
SEL_C,
SEL_R,
SEL_IMM,
MUFU, // Multi-Function Operator
RRO_C, // Range Reduction Operator
RRO_R,
@@ -716,6 +721,9 @@ private:
INST("0100110000011---", Id::ISCADD_C, Type::ArithmeticInteger, "ISCADD_C"),
INST("0101110000011---", Id::ISCADD_R, Type::ArithmeticInteger, "ISCADD_R"),
INST("0011100-00011---", Id::ISCADD_IMM, Type::ArithmeticInteger, "ISCADD_IMM"),
INST("0100110010100---", Id::SEL_C, Type::ArithmeticInteger, "SEL_C"),
INST("0101110010100---", Id::SEL_R, Type::ArithmeticInteger, "SEL_R"),
INST("0011100010100---", Id::SEL_IMM, Type::ArithmeticInteger, "SEL_IMM"),
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
@@ -784,5 +792,4 @@ private:
}
};
} // namespace Shader
} // namespace Tegra
} // namespace Tegra::Shader

View File

@@ -20,7 +20,11 @@ GPU::GPU() {
GPU::~GPU() = default;
const Tegra::Engines::Maxwell3D& GPU::Get3DEngine() const {
const Engines::Maxwell3D& GPU::Maxwell3D() const {
return *maxwell_3d;
}
Engines::Maxwell3D& GPU::Maxwell3D() {
return *maxwell_3d;
}

View File

@@ -93,15 +93,14 @@ public:
/// Processes a command list stored at the specified address in GPU memory.
void ProcessCommandList(GPUVAddr address, u32 size);
/// Returns a const reference to the Maxwell3D GPU engine.
const Engines::Maxwell3D& Maxwell3D() const;
/// Returns a reference to the Maxwell3D GPU engine.
const Engines::Maxwell3D& Get3DEngine() const;
Engines::Maxwell3D& Maxwell3D();
std::unique_ptr<MemoryManager> memory_manager;
Engines::Maxwell3D& Maxwell3D() {
return *maxwell_3d;
}
private:
/// Writes a single register in the engine bound to the specified subchannel
void WriteReg(u32 method, u32 subchannel, u32 value, u32 remaining_params);

View File

@@ -387,7 +387,7 @@ void RasterizerOpenGL::Clear() {
}
if (regs.clear_buffers.Z) {
clear_mask |= GL_DEPTH_BUFFER_BIT;
use_depth_fb = true;
use_depth_fb = regs.zeta_enable != 0;
// Always enable the depth write when clearing the depth buffer. The depth write mask is
// ignored when clearing the buffer in the Switch, but OpenGL obeys it so we set it to true.
@@ -413,11 +413,13 @@ void RasterizerOpenGL::Clear() {
glClear(clear_mask);
// Mark framebuffer surfaces as dirty
if (dirty_color_surface != nullptr) {
res_cache.MarkSurfaceAsDirty(dirty_color_surface);
}
if (dirty_depth_surface != nullptr) {
res_cache.MarkSurfaceAsDirty(dirty_depth_surface);
if (Settings::values.use_accurate_framebuffers) {
if (dirty_color_surface != nullptr) {
res_cache.FlushSurface(dirty_color_surface);
}
if (dirty_depth_surface != nullptr) {
res_cache.FlushSurface(dirty_depth_surface);
}
}
}
@@ -431,7 +433,7 @@ void RasterizerOpenGL::DrawArrays() {
ScopeAcquireGLContext acquire_context;
auto [dirty_color_surface, dirty_depth_surface] =
ConfigureFramebuffers(true, regs.zeta.Address() != 0);
ConfigureFramebuffers(true, regs.zeta.Address() != 0 && regs.zeta_enable != 0);
SyncDepthTestState();
SyncBlendState();
@@ -520,11 +522,13 @@ void RasterizerOpenGL::DrawArrays() {
state.Apply();
// Mark framebuffer surfaces as dirty
if (dirty_color_surface != nullptr) {
res_cache.MarkSurfaceAsDirty(dirty_color_surface);
}
if (dirty_depth_surface != nullptr) {
res_cache.MarkSurfaceAsDirty(dirty_depth_surface);
if (Settings::values.use_accurate_framebuffers) {
if (dirty_color_surface != nullptr) {
res_cache.FlushSurface(dirty_color_surface);
}
if (dirty_depth_surface != nullptr) {
res_cache.FlushSurface(dirty_depth_surface);
}
}
}
@@ -634,8 +638,8 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program,
u32 current_bindpoint,
const std::vector<GLShader::ConstBufferEntry>& entries) {
auto& gpu = Core::System::GetInstance().GPU();
auto& maxwell3d = gpu.Get3DEngine();
const auto& gpu = Core::System::GetInstance().GPU();
const auto& maxwell3d = gpu.Maxwell3D();
// Reset all buffer draw state for this stage.
for (auto& buffer : state.draw.const_buffers[static_cast<size_t>(stage)]) {
@@ -644,7 +648,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
}
// Upload only the enabled buffers from the 16 constbuffers of each shader stage
auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<size_t>(stage)];
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
const auto& used_buffer = entries[bindpoint];
@@ -700,8 +704,8 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit,
const std::vector<GLShader::SamplerEntry>& entries) {
auto& gpu = Core::System::GetInstance().GPU();
auto& maxwell3d = gpu.Get3DEngine();
const auto& gpu = Core::System::GetInstance().GPU();
const auto& maxwell3d = gpu.Maxwell3D();
ASSERT_MSG(current_unit + entries.size() <= std::size(state.texture_units),
"Exceeded the number of active textures.");

View File

@@ -65,9 +65,9 @@ struct FormatTuple {
return params;
}
/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(
const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config, Tegra::GPUVAddr zeta_address,
Tegra::DepthFormat format) {
/*static*/ SurfaceParams SurfaceParams::CreateForDepthBuffer(u32 zeta_width, u32 zeta_height,
Tegra::GPUVAddr zeta_address,
Tegra::DepthFormat format) {
SurfaceParams params{};
params.addr = zeta_address;
@@ -77,9 +77,9 @@ struct FormatTuple {
params.component_type = ComponentTypeFromDepthFormat(format);
params.type = GetFormatType(params.pixel_format);
params.size_in_bytes = params.SizeInBytes();
params.width = config.width;
params.height = config.height;
params.unaligned_height = config.height;
params.width = zeta_width;
params.height = zeta_height;
params.unaligned_height = zeta_height;
params.size_in_bytes = params.SizeInBytes();
return params;
}
@@ -254,6 +254,60 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup
cur_state.Apply();
}
static bool BlitTextures(GLuint src_tex, const MathUtil::Rectangle<u32>& src_rect, GLuint dst_tex,
const MathUtil::Rectangle<u32>& dst_rect, SurfaceType type,
GLuint read_fb_handle, GLuint draw_fb_handle) {
OpenGLState prev_state{OpenGLState::GetCurState()};
SCOPE_EXIT({ prev_state.Apply(); });
OpenGLState state;
state.draw.read_framebuffer = read_fb_handle;
state.draw.draw_framebuffer = draw_fb_handle;
state.Apply();
u32 buffers{};
if (type == SurfaceType::ColorTexture) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, src_tex,
0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_tex,
0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0,
0);
buffers = GL_COLOR_BUFFER_BIT;
} else if (type == SurfaceType::Depth) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, src_tex, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, dst_tex, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_TEXTURE_2D, 0, 0);
buffers = GL_DEPTH_BUFFER_BIT;
} else if (type == SurfaceType::DepthStencil) {
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
src_tex, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
dst_tex, 0);
buffers = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
}
glBlitFramebuffer(src_rect.left, src_rect.bottom, src_rect.right, src_rect.top, dst_rect.left,
dst_rect.bottom, dst_rect.right, dst_rect.top, buffers,
buffers == GL_COLOR_BUFFER_BIT ? GL_LINEAR : GL_NEAREST);
return true;
}
CachedSurface::CachedSurface(const SurfaceParams& params) : params(params) {
texture.Create();
const auto& rect{params.GetRect()};
@@ -519,8 +573,8 @@ SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
}
if (using_depth_fb) {
depth_params =
SurfaceParams::CreateForDepthBuffer(regs.rt[0], regs.zeta.Address(), regs.zeta.format);
depth_params = SurfaceParams::CreateForDepthBuffer(regs.zeta_width, regs.zeta_height,
regs.zeta.Address(), regs.zeta.format);
}
MathUtil::Rectangle<u32> color_rect{};
@@ -565,17 +619,9 @@ void RasterizerCacheOpenGL::LoadSurface(const Surface& surface) {
surface->UploadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
}
void RasterizerCacheOpenGL::MarkSurfaceAsDirty(const Surface& surface) {
if (Settings::values.use_accurate_framebuffers) {
// If enabled, always flush dirty surfaces
surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
surface->FlushGLBuffer();
} else {
// Otherwise, don't mark surfaces that we write to as cached, because the resulting loads
// and flushes are very slow and do not seem to improve accuracy
const auto& params{surface->GetSurfaceParams()};
Memory::RasterizerMarkRegionCached(params.addr, params.size_in_bytes, false);
}
void RasterizerCacheOpenGL::FlushSurface(const Surface& surface) {
surface->DownloadGLTexture(read_framebuffer.handle, draw_framebuffer.handle);
surface->FlushGLBuffer();
}
Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
@@ -588,25 +634,53 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
if (gpu.memory_manager->GpuToCpuAddress(params.addr) == boost::none)
return {};
// Check for an exact match in existing surfaces
const auto& surface_key{SurfaceKey::Create(params)};
const auto& search{surface_cache.find(surface_key)};
// Look up surface in the cache based on address
const auto& search{surface_cache.find(params.addr)};
Surface surface;
if (search != surface_cache.end()) {
surface = search->second;
if (Settings::values.use_accurate_framebuffers) {
// Reload the surface from Switch memory
LoadSurface(surface);
// If use_accurate_framebuffers is enabled, always load from memory
FlushSurface(surface);
UnregisterSurface(surface);
} else if (surface->GetSurfaceParams() != params) {
// If surface parameters changed, recreate the surface from the old one
return RecreateSurface(surface, params);
} else {
// Use the cached surface as-is
return surface;
}
} else {
surface = std::make_shared<CachedSurface>(params);
RegisterSurface(surface);
LoadSurface(surface);
}
// No surface found - create a new one
surface = std::make_shared<CachedSurface>(params);
RegisterSurface(surface);
LoadSurface(surface);
return surface;
}
Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
const SurfaceParams& new_params) {
// Verify surface is compatible for blitting
const auto& params{surface->GetSurfaceParams()};
ASSERT(params.type == new_params.type);
ASSERT(params.pixel_format == new_params.pixel_format);
ASSERT(params.component_type == new_params.component_type);
// Create a new surface with the new parameters, and blit the previous surface to it
Surface new_surface{std::make_shared<CachedSurface>(new_params)};
BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle,
new_surface->GetSurfaceParams().GetRect(), params.type, read_framebuffer.handle,
draw_framebuffer.handle);
// Update cache accordingly
UnregisterSurface(surface);
RegisterSurface(new_surface);
return new_surface;
}
Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr cpu_addr) const {
// Tries to find the GPU address of a framebuffer based on the CPU address. This is because
// final output framebuffers are specified by CPU address, but internally our GPU cache uses
@@ -652,22 +726,20 @@ void RasterizerCacheOpenGL::InvalidateRegion(Tegra::GPUVAddr addr, size_t size)
void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) {
const auto& params{surface->GetSurfaceParams()};
const auto& surface_key{SurfaceKey::Create(params)};
const auto& search{surface_cache.find(surface_key)};
const auto& search{surface_cache.find(params.addr)};
if (search != surface_cache.end()) {
// Registered already
return;
}
surface_cache[surface_key] = surface;
surface_cache[params.addr] = surface;
UpdatePagesCachedCount(params.addr, params.size_in_bytes, 1);
}
void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) {
const auto& params{surface->GetSurfaceParams()};
const auto& surface_key{SurfaceKey::Create(params)};
const auto& search{surface_cache.find(surface_key)};
const auto& search{surface_cache.find(params.addr)};
if (search == surface_cache.end()) {
// Unregistered already

View File

@@ -10,7 +10,6 @@
#include <vector>
#include <boost/icl/interval_map.hpp>
#include "common/common_types.h"
#include "common/hash.h"
#include "common/math_util.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -137,6 +136,7 @@ struct SurfaceParams {
ASSERT(static_cast<size_t>(format) < bpp_table.size());
return bpp_table[static_cast<size_t>(format)];
}
u32 GetFormatBpp() const {
return GetFormatBpp(pixel_format);
}
@@ -365,9 +365,21 @@ struct SurfaceParams {
const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config);
/// Creates SurfaceParams for a depth buffer configuration
static SurfaceParams CreateForDepthBuffer(
const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& config,
Tegra::GPUVAddr zeta_address, Tegra::DepthFormat format);
static SurfaceParams CreateForDepthBuffer(u32 zeta_width, u32 zeta_height,
Tegra::GPUVAddr zeta_address,
Tegra::DepthFormat format);
bool operator==(const SurfaceParams& other) const {
return std::tie(addr, is_tiled, block_height, pixel_format, component_type, type, width,
height, unaligned_height, size_in_bytes) ==
std::tie(other.addr, other.is_tiled, other.block_height, other.pixel_format,
other.component_type, other.type, other.width, other.height,
other.unaligned_height, other.size_in_bytes);
}
bool operator!=(const SurfaceParams& other) const {
return !operator==(other);
}
Tegra::GPUVAddr addr;
bool is_tiled;
@@ -381,24 +393,6 @@ struct SurfaceParams {
size_t size_in_bytes;
};
/// Hashable variation of SurfaceParams, used for a key in the surface cache
struct SurfaceKey : Common::HashableStruct<SurfaceParams> {
static SurfaceKey Create(const SurfaceParams& params) {
SurfaceKey res;
res.state = params;
return res;
}
};
namespace std {
template <>
struct hash<SurfaceKey> {
size_t operator()(const SurfaceKey& k) const {
return k.Hash();
}
};
} // namespace std
class CachedSurface final {
public:
CachedSurface(const SurfaceParams& params);
@@ -444,8 +438,8 @@ public:
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
const MathUtil::Rectangle<s32>& viewport);
/// Marks the specified surface as "dirty", in that it is out of sync with Switch memory
void MarkSurfaceAsDirty(const Surface& surface);
/// Flushes the surface to Switch memory
void FlushSurface(const Surface& surface);
/// Tries to find a framebuffer GPU address based on the provided CPU address
Surface TryFindFramebufferSurface(VAddr cpu_addr) const;
@@ -460,6 +454,9 @@ private:
void LoadSurface(const Surface& surface);
Surface GetSurface(const SurfaceParams& params);
/// Recreates a surface with new parameters
Surface RecreateSurface(const Surface& surface, const SurfaceParams& new_params);
/// Register surface into the cache
void RegisterSurface(const Surface& surface);
@@ -469,7 +466,7 @@ private:
/// Increase/decrease the number of surface in pages touching the specified region
void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta);
std::unordered_map<SurfaceKey, Surface> surface_cache;
std::unordered_map<Tegra::GPUVAddr, Surface> surface_cache;
PageMap cached_pages;
OGLFramebuffer read_framebuffer;

View File

@@ -12,8 +12,7 @@
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
namespace GLShader {
namespace Decompiler {
namespace GLShader::Decompiler {
using Tegra::Shader::Attribute;
using Tegra::Shader::Instruction;
@@ -1140,6 +1139,15 @@ private:
"((" + op_a + " << " + shift + ") + " + op_b + ')', 1, 1);
break;
}
case OpCode::Id::SEL_C:
case OpCode::Id::SEL_R:
case OpCode::Id::SEL_IMM: {
std::string condition =
GetPredicateCondition(instr.sel.pred, instr.sel.neg_pred != 0);
regs.SetRegisterToInteger(instr.gpr0, true, 0,
'(' + condition + ") ? " + op_a + " : " + op_b, 1, 1);
break;
}
case OpCode::Id::LOP_C:
case OpCode::Id::LOP_R:
case OpCode::Id::LOP_IMM: {
@@ -1845,5 +1853,4 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
return boost::none;
}
} // namespace Decompiler
} // namespace GLShader
} // namespace GLShader::Decompiler

View File

@@ -12,8 +12,7 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_shader_gen.h"
namespace GLShader {
namespace Decompiler {
namespace GLShader::Decompiler {
using Tegra::Engines::Maxwell3D;
@@ -23,5 +22,4 @@ boost::optional<ProgramResult> DecompileProgram(const ProgramCode& program_code,
Maxwell3D::Regs::ShaderStage stage,
const std::string& suffix);
} // namespace Decompiler
} // namespace GLShader
} // namespace GLShader::Decompiler

View File

@@ -10,8 +10,7 @@
#include "video_core/textures/decoders.h"
#include "video_core/textures/texture.h"
namespace Tegra {
namespace Texture {
namespace Tegra::Texture {
/**
* Calculates the offset of an (x, y) position within a swizzled texture.
@@ -186,5 +185,4 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat
return rgba_data;
}
} // namespace Texture
} // namespace Tegra
} // namespace Tegra::Texture

View File

@@ -8,8 +8,7 @@
#include "common/common_types.h"
#include "video_core/textures/texture.h"
namespace Tegra {
namespace Texture {
namespace Tegra::Texture {
/**
* Unswizzles a swizzled texture without changing its format.
@@ -33,5 +32,4 @@ void CopySwizzledData(u32 width, u32 height, u32 bytes_per_pixel, u32 out_bytes_
std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat format, u32 width,
u32 height);
} // namespace Texture
} // namespace Tegra
} // namespace Tegra::Texture

View File

@@ -10,8 +10,7 @@
#include "common/common_types.h"
#include "video_core/memory_manager.h"
namespace Tegra {
namespace Texture {
namespace Tegra::Texture {
enum class TextureFormat : u32 {
R32_G32_B32_A32 = 0x01,
@@ -260,5 +259,4 @@ struct FullTextureInfo {
/// Returns the number of bytes per pixel of the input texture format.
u32 BytesPerPixel(TextureFormat format);
} // namespace Texture
} // namespace Tegra
} // namespace Tegra::Texture

View File

@@ -10,7 +10,7 @@
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
qt_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "qt-config.ini";
qt_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "qt-config.ini";
FileUtil::CreateFullPath(qt_config_loc);
qt_config = new QSettings(QString::fromStdString(qt_config_loc), QSettings::IniFormat);

View File

@@ -19,7 +19,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co
ui->setupUi(this);
this->setConfiguration();
connect(ui->open_log_button, &QPushButton::pressed, []() {
QString path = QString::fromStdString(FileUtil::GetUserPath(D_LOGS_IDX));
QString path = QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LogDir));
QDesktopServices::openUrl(QUrl::fromLocalFile(path));
});
}

View File

@@ -336,9 +336,9 @@ void GraphicsSurfaceWidget::OnUpdate() {
// TODO: Store a reference to the registers in the debug context instead of accessing them
// directly...
auto& registers = gpu.Get3DEngine().regs;
auto& rt = registers.rt[static_cast<size_t>(surface_source) -
static_cast<size_t>(Source::RenderTarget0)];
const auto& registers = gpu.Maxwell3D().regs;
const auto& rt = registers.rt[static_cast<size_t>(surface_source) -
static_cast<size_t>(Source::RenderTarget0)];
surface_address = rt.Address();
surface_width = rt.width;

View File

@@ -909,6 +909,16 @@ void GMainWindow::UpdateUITheme() {
#undef main
#endif
static void InitializeLogging() {
Log::Filter log_filter;
log_filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(log_filter);
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
}
int main(int argc, char* argv[]) {
MicroProfileOnThreadCreate("Frontend");
SCOPE_EXIT({ MicroProfileShutdown(); });
@@ -927,13 +937,7 @@ int main(int argc, char* argv[]) {
GMainWindow main_window;
// After settings have been loaded by GMainWindow, apply the filter
Log::Filter log_filter;
log_filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(log_filter);
FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
Log::AddBackend(
std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
InitializeLogging();
main_window.show();
return app.exec();
}

View File

@@ -15,7 +15,7 @@
Config::Config() {
// TODO: Don't hardcode the path; let the frontend decide where to put the config files.
sdl2_config_loc = FileUtil::GetUserPath(D_CONFIG_IDX) + "sdl2-config.ini";
sdl2_config_loc = FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) + "sdl2-config.ini";
sdl2_config = std::make_unique<INIReader>(sdl2_config_loc);
Reload();

View File

@@ -56,6 +56,18 @@ static void PrintVersion() {
std::cout << "yuzu " << Common::g_scm_branch << " " << Common::g_scm_desc << std::endl;
}
static void InitializeLogging() {
Log::Filter log_filter(Log::Level::Debug);
log_filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(log_filter);
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
const std::string& log_dir = FileUtil::GetUserPath(FileUtil::UserPath::LogDir);
FileUtil::CreateFullPath(log_dir);
Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE));
}
/// Application entry point
int main(int argc, char** argv) {
Config config;
@@ -124,14 +136,7 @@ int main(int argc, char** argv) {
LocalFree(argv_w);
#endif
Log::Filter log_filter(Log::Level::Debug);
log_filter.ParseFilterString(Settings::values.log_filter);
Log::SetGlobalFilter(log_filter);
Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>());
FileUtil::CreateFullPath(FileUtil::GetUserPath(D_LOGS_IDX));
Log::AddBackend(
std::make_unique<Log::FileBackend>(FileUtil::GetUserPath(D_LOGS_IDX) + LOG_FILE));
InitializeLogging();
MicroProfileOnThreadCreate("EmuThread");
SCOPE_EXIT({ MicroProfileShutdown(); });