Compare commits

..

57 Commits

Author SHA1 Message Date
Sebastian Valle
1f780a0d18 Merge ac4904983e into 68183e7b5a 2018-04-06 14:51:48 +00:00
bunnei
68183e7b5a Merge pull request #312 from jroweboy/update-fmtlib
Update fmtlib to fix msvc warnings
2018-04-06 10:25:30 -04:00
James Rowe
f9945f8a3b Update fmtlib to fix msvc warnings
Additionally, when updating fmtlib, there was a change in fmtlib broke
how the old logging macro was overloaded, so this works around that by
just naming the fmtlib macro impl something different
2018-04-05 22:42:09 -06:00
bunnei
20bd26dc7d Merge pull request #308 from bunnei/misc-fixes-2
Implement and stub several SVC/VI/Audio/Friend/etc. funcs
2018-04-04 16:50:12 -04:00
Subv
ac4904983e GPU: Assert when finding a texture with a format type other than UNORM. 2018-04-03 00:00:17 -05:00
Subv
1812b5700e GL: Set up the textures used for each draw call.
Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a certain number in OpenGL. We try to only use the minimum amount of host textures by not keeping a 1:1 relation between guest texture ids and host texture ids, ie, guest texture id 8 can be host texture id 0 if it's the only texture used in the guest shader program.
This mapping will have to be passed to the shader decompiler so it can rewrite the texture accesses.
2018-04-03 00:00:17 -05:00
Subv
5ff240952a GL: Bind the textures to the shaders used for drawing. 2018-04-03 00:00:17 -05:00
Subv
7445896bce GLCache: Specialize the MortonCopy function for the DXT1 texture format.
It will now use the UnswizzleTexture function instead of the MortonCopyPixels128, which doesn't seem to work for textures.
2018-04-03 00:00:16 -05:00
bunnei
40bccd74d3 svc: Stub out SetThreadActivity, GetThreadContext. 2018-04-02 23:51:01 -04:00
bunnei
4c0cf3d5ff audren_u: Stub out GetActiveAudioDeviceName. 2018-04-02 23:51:00 -04:00
bunnei
3d4dfefaec audout_u: Implement GetAudioOutState. 2018-04-02 23:51:00 -04:00
bunnei
910b02d74b nifm: GetResult does not return a data field. 2018-04-02 23:50:59 -04:00
bunnei
9d08a11c1d vi: Implement GetDisplayResolution. 2018-04-02 23:50:59 -04:00
bunnei
99ae9dbf49 shared_memory: Remove incorrect 3ds-specific check. 2018-04-02 23:50:58 -04:00
bunnei
9eb485702f service: Add friend:u interface. 2018-04-02 23:50:57 -04:00
Subv
1596747934 GLCache: Implemented GetTextureSurface. 2018-04-02 22:49:38 -05:00
Subv
7468d90e29 GLCache: Support uploading compressed textures to the GPU.
Compressed texture formats like DXT1, DXT2, DXT3, etc will use this to ease the load on the CPU.
2018-04-02 22:49:38 -05:00
Subv
47ba834cfa GL: Remove remaining references to 3DS-specific pixel formats 2018-04-02 22:49:38 -05:00
Subv
d269c696ba RasterizerCache: Remove 3DS-specific pixel formats.
We're only left with RGB8 and DXT1 for now. More will be added as they are needed.
2018-04-02 22:49:38 -05:00
Subv
c5b2e943bb GL: Create the sampler objects when starting up the GL rasterizer. 2018-04-02 22:49:37 -05:00
Subv
5d53ec9fb1 GL: Ported the SamplerInfo struct from citra. 2018-04-02 22:49:37 -05:00
Subv
f236229b49 GL: Rename PicaTexture to MaxwellTexture. 2018-04-02 22:49:36 -05:00
Subv
52cc13b722 GL: Added functions to convert Maxwell tex filters and wrap modes to OpenGL. 2018-04-02 22:49:36 -05:00
Subv
3e62c20ae6 Textures: Added a helper function to know if a texture is blocklinear or pitch. 2018-04-02 22:49:36 -05:00
bunnei
b87a588c37 Merge pull request #306 from daniellimws/new-fmt-macros
logging: Use variadic template instead of FMT_VARIADIC
2018-04-02 22:48:17 -04:00
Daniel Lim Wee Soong
bb9093ed57 logging: Change FmtLogMessage to use variadic template instead of FMT_VARIADIC
Due to premature merging of #262 I think the build may be failing right now. Should merge this ASAP to fix it.
2018-04-03 10:31:54 +08:00
bunnei
c2e0820ac2 Merge pull request #262 from daniellimws/fmtlib-macros
Logging: Add fmtlib-based macros
2018-04-02 21:19:20 -04:00
bunnei
c824648db5 Merge pull request #267 from N00byKing/patch-1
Update Dialog from citra to yuzu
2018-04-02 18:33:52 -04:00
bunnei
6cd1482354 Merge pull request #276 from N00byKing/acctoyuzu
Change Telemetry Names to yuzu and remove links to citra
2018-04-02 18:30:04 -04:00
bunnei
c82a4df000 Merge pull request #304 from daniellimws/fix-openbsd
Fix build on OpenBSD
2018-04-02 18:27:19 -04:00
James Rowe
467858633f Merge pull request #305 from N00byKing/patch-2
deconstructed_rom_directory.cpp: Fix Typo
2018-04-02 16:21:03 -06:00
N00byKing
1aafb0f3a3 deconstructed_rom_directory.cpp: Fix Typo 2018-04-03 00:20:35 +02:00
bunnei
2863e1edb9 Merge pull request #66 from RiverCityRansomware/qtUpdate
Updates CMakeLists to use Qt 5.10.0 instead of Qt 5.7
2018-04-02 16:06:58 -04:00
Daniel Lim Wee Soong
c9845c486e externals: Update fmt to 4d35f94
Versions prior to this didn't compile on OpenBSD due to unconditional
use of the non-standard strtod_l() function.

The fmt::MemoryWriter API has been removed in the intervening
versions, so replace its use with fmt::memory_buffer and fmt::format_to.

The library also no longer provides the fmt::fmt ALIAS, so define
it in externals/CMakeLists.txt.
2018-04-03 02:49:58 +08:00
Daniel Lim Wee Soong
a66204eb5c common: fix swap functions on Bitrig and OpenBSD
swap{16,32,64} are defined as macros on the two, but client code
tries to invoke them as Common::swap{16,32,64}, which naturally
doesn't work. This hack redefines the macros as inline functions
in the Common namespace: the bodies of the functions are the
same as the original macros, but relying on OS-specific
implementation details like this is of course brittle.
2018-04-03 02:25:11 +08:00
bunnei
6022bc8394 Merge pull request #297 from bunnei/hid-touch-state
hid: Write empty touch screen state.
2018-04-02 13:05:34 -04:00
bunnei
f92594d744 Merge pull request #296 from bunnei/misc-mem-fsp-fixes
Fix stack region, implement FSP GetSize/SetSize, and some stubs
2018-04-02 13:05:25 -04:00
bunnei
3413f1f7ce Merge pull request #288 from Subv/macro_interpreter
GPU: Implemented a gpu macro interpreter
2018-04-02 10:04:19 -04:00
Subv
11b4ab9685 GPU: Use the MacroInterpreter class to execute the GPU macros instead of HLEing them. 2018-04-01 12:07:26 -05:00
Subv
1ec8d2123d GPU: Implemented a gpu macro interpreter.
The Ryujinx macro interpreter and envydis were used as reference.

Macros are programs that are uploaded by the games during boot and can later be called by writing to their method id in a GPU command buffer.
2018-04-01 12:07:26 -05:00
bunnei
72b90494e7 hid: Write empty touch screen state. 2018-04-01 00:12:07 -04:00
bunnei
f4ba523992 hle_ipc, fsp_srv: Cleanup logging. 2018-03-31 23:30:00 -04:00
bunnei
b6b7d78ded hid: Stub out GetSupportedNpadStyleSet. 2018-03-31 16:06:46 -04:00
bunnei
eef097bdc7 hle_ipc: Do not ensure write buffer size. 2018-03-31 16:06:46 -04:00
bunnei
88582b84a5 fsp_srv: Implement GetSize and SetSize. 2018-03-31 16:06:45 -04:00
bunnei
b27ab46bde memory: Fix stack region. 2018-03-31 16:06:45 -04:00
N00byKing
aa0f596a6e telemetry.h: Reword comment from citra to yuzu 2018-03-27 11:16:59 +02:00
N00byKing
98f0352728 telemetry_session.h: Reword Documentation Comment from citra to yuzu 2018-03-27 11:16:59 +02:00
N00byKing
10738588a4 Remove Links to citra Services 2018-03-27 11:16:59 +02:00
N00byKing
8004af0d05 Change Telemetry Names to yuzu 2018-03-27 11:16:59 +02:00
N00byKing
10d6b07161 yuzu.cpp: Update Link from citra to yuzu 2018-03-26 21:48:18 +02:00
N00byKing
91e67ed430 main.cpp: Replace Citra with yuzu Wiki Links 2018-03-25 11:44:04 +02:00
N00byKing
d248b90c85 main.cpp: Update Dialog from citra to yuzu 2018-03-25 11:42:46 +02:00
Daniel Lim Wee Soong
8529d84f31 Remove dependency chrono
Earlier chrono was included but after some code changed it was no longer needed

Forgot to remove it so I'm removing it now
2018-03-22 21:53:51 +08:00
Daniel Lim Wee Soong
47f96fe13a Change "yuzu starting..." to be logged with the new macro
Just as a proof that it works
2018-03-22 18:26:43 +08:00
Daniel Lim Wee Soong
3b558eebee Logging: Create logging macros based on fmtlib
Add a new set of logging macros based on fmtlib
Similar but not exactly the same as https://github.com/citra-emu/citra/pull/3533

Citra currently uses a different version of fmt, which does not support FMT_VARIADIC so
make_args is used instead. On the other hand, yuzu uses fmt 4.1.0 which doesn't have make_args yet
so FMT_VARIADIC is used.
2018-03-22 18:21:29 +08:00
River City Ransomware
ec204a27dc Update qt
Updates qt from 5.7 to 5.10.0, fixing some errors relating to setting the process dpi
2018-01-17 09:20:53 -05:00
58 changed files with 1065 additions and 476 deletions

View File

@@ -278,7 +278,7 @@ endif()
if (ENABLE_QT)
if (YUZU_USE_BUNDLED_QT)
if (MSVC14 AND ARCHITECTURE_x86_64)
set(QT_VER qt-5.7-msvc2015_64)
set(QT_VER qt-5.10.0-msvc2015_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()

View File

@@ -17,6 +17,7 @@ endif()
# libfmt
add_subdirectory(fmt)
add_library(fmt::fmt ALIAS fmt)
# getopt
if (MSVC)

2
externals/fmt vendored

View File

@@ -89,7 +89,7 @@ endif()
create_target_directory_groups(common)
target_link_libraries(common PUBLIC Boost::boost microprofile)
target_link_libraries(common PUBLIC Boost::boost fmt microprofile)
if (ARCHITECTURE_x86_64)
target_link_libraries(common PRIVATE xbyak)
endif()

View File

@@ -11,6 +11,7 @@
#include "common/logging/filter.h"
#include "common/logging/log.h"
#include "common/logging/text_formatter.h"
#include "common/string_util.h"
namespace Log {
@@ -106,25 +107,20 @@ const char* GetLevelName(Level log_level) {
}
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, const char* format, va_list args) {
const char* function, std::string message) {
using std::chrono::duration_cast;
using std::chrono::steady_clock;
static steady_clock::time_point time_origin = steady_clock::now();
std::array<char, 4 * 1024> formatting_buffer;
Entry entry;
entry.timestamp = duration_cast<std::chrono::microseconds>(steady_clock::now() - time_origin);
entry.log_class = log_class;
entry.log_level = log_level;
snprintf(formatting_buffer.data(), formatting_buffer.size(), "%s:%s:%u", filename, function,
line_nr);
entry.location = std::string(formatting_buffer.data());
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
entry.message = std::string(formatting_buffer.data());
entry.filename = Common::TrimSourcePath(filename);
entry.line_num = line_nr;
entry.function = function;
entry.message = std::move(message);
return entry;
}
@@ -135,15 +131,28 @@ void SetFilter(Filter* new_filter) {
filter = new_filter;
}
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, ...) {
if (filter != nullptr && !filter->CheckMessage(log_class, log_level))
if (filter && !filter->CheckMessage(log_class, log_level))
return;
std::array<char, 4 * 1024> formatting_buffer;
va_list args;
va_start(args, format);
Entry entry = CreateEntry(log_class, log_level, filename, line_nr, function, format, args);
vsnprintf(formatting_buffer.data(), formatting_buffer.size(), format, args);
va_end(args);
Entry entry = CreateEntry(log_class, log_level, filename, line_num, function,
std::string(formatting_buffer.data()));
PrintColoredMessage(entry);
}
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args) {
if (filter && !filter->CheckMessage(log_class, log_level))
return;
Entry entry =
CreateEntry(log_class, log_level, filename, line_num, function, fmt::vformat(format, args));
PrintColoredMessage(entry);
}

View File

@@ -22,13 +22,16 @@ struct Entry {
std::chrono::microseconds timestamp;
Class log_class;
Level log_level;
std::string location;
std::string filename;
unsigned int line_num;
std::string function;
std::string message;
Entry() = default;
Entry(Entry&& o) = default;
Entry& operator=(Entry&& o) = default;
Entry& operator=(const Entry& o) = default;
};
/**
@@ -44,7 +47,7 @@ const char* GetLevelName(Level log_level);
/// Creates a log entry by formatting the given source location, and message.
Entry CreateEntry(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
const char* function, const char* format, va_list args);
const char* function, std::string message);
void SetFilter(Filter* filter);
} // namespace Log

View File

@@ -65,14 +65,14 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const std::string::const_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 `:`: %s",
std::string(begin, end).c_str());
NGLOG_ERROR(Log, "Invalid log filter. Must specify a log level after `:`: %s",
std::string(begin, end).c_str());
return false;
}
const Level level = GetLevelByName(level_separator + 1, end);
if (level == Level::Count) {
LOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
NGLOG_ERROR(Log, "Unknown log level in filter: %s", std::string(begin, end).c_str());
return false;
}
@@ -83,7 +83,7 @@ bool Filter::ParseFilterRule(const std::string::const_iterator begin,
const Class log_class = GetClassByName(begin, level_separator);
if (log_class == Class::Count) {
LOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
NGLOG_ERROR(Log, "Unknown log class in filter: %s", std::string(begin, end).c_str());
return false;
}

View File

@@ -19,7 +19,7 @@ namespace Log {
class Filter {
public:
/// Initializes the filter with all classes having `default_level` as the minimum level.
Filter(Level default_level);
Filter(Level default_level = Level::Info);
/// Resets the filter so that all classes have `level` as the minimum displayed level.
void ResetAll(Level level);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <fmt/format.h>
#include "common/common_types.h"
namespace Log {
@@ -91,7 +92,7 @@ enum class Class : ClassType {
};
/// Logs a message to the global logger.
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_nr,
void LogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function,
#ifdef _MSC_VER
_Printf_format_string_
@@ -103,6 +104,18 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
#endif
;
/// Logs a message to the global logger, using fmt
void FmtLogMessageImpl(Class log_class, Level log_level, const char* filename,
unsigned int line_num, const char* function, const char* format,
const fmt::format_args& args);
template <typename... Args>
void FmtLogMessage(Class log_class, Level log_level, const char* filename, unsigned int line_num,
const char* function, const char* format, const Args&... args) {
FmtLogMessageImpl(log_class, log_level, filename, line_num, function, format,
fmt::make_args(args...));
}
} // namespace Log
#define LOG_GENERIC(log_class, log_level, ...) \
@@ -125,3 +138,28 @@ void LogMessage(Class log_class, Level log_level, const char* filename, unsigned
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Error, __VA_ARGS__)
#define LOG_CRITICAL(log_class, ...) \
LOG_GENERIC(::Log::Class::log_class, ::Log::Level::Critical, __VA_ARGS__)
// Define the fmt lib macros
#ifdef _DEBUG
#define NGLOG_TRACE(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Trace, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#else
#define NGLOG_TRACE(log_class, fmt, ...) (void(0))
#endif
#define NGLOG_DEBUG(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Debug, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_INFO(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Info, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_WARNING(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Warning, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_ERROR(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Error, __FILE__, __LINE__, \
__func__, __VA_ARGS__)
#define NGLOG_CRITICAL(log_class, ...) \
::Log::FmtLogMessage(::Log::Class::log_class, ::Log::Level::Critical, __FILE__, __LINE__, \
__func__, __VA_ARGS__)

View File

@@ -18,50 +18,29 @@
namespace Log {
// TODO(bunnei): This should be moved to a generic path manipulation library
const char* TrimSourcePath(const char* path, const char* root) {
const char* p = path;
while (*p != '\0') {
const char* next_slash = p;
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
++next_slash;
}
bool is_src = Common::ComparePartialString(p, next_slash, root);
p = next_slash;
if (*p != '\0') {
++p;
}
if (is_src) {
path = p;
}
}
return path;
}
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len) {
std::string FormatLogMessage(const Entry& entry) {
unsigned int time_seconds = static_cast<unsigned int>(entry.timestamp.count() / 1000000);
unsigned int time_fractional = static_cast<unsigned int>(entry.timestamp.count() % 1000000);
const char* class_name = GetLogClassName(entry.log_class);
const char* level_name = GetLevelName(entry.log_level);
snprintf(out_text, text_len, "[%4u.%06u] %s <%s> %s: %s", time_seconds, time_fractional,
class_name, level_name, TrimSourcePath(entry.location.c_str()), entry.message.c_str());
return fmt::format("[{:4d}.{:06d}] {} <{}> {}:{}:{}: {}", time_seconds, time_fractional,
class_name, level_name, entry.filename, entry.function, entry.line_num,
entry.message);
}
void PrintMessage(const Entry& entry) {
std::array<char, 4 * 1024> format_buffer;
FormatLogMessage(entry, format_buffer.data(), format_buffer.size());
fputs(format_buffer.data(), stderr);
fputc('\n', stderr);
auto str = FormatLogMessage(entry) + '\n';
fputs(str.c_str(), stderr);
}
void PrintColoredMessage(const Entry& entry) {
#ifdef _WIN32
static HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
HANDLE console_handle = GetStdHandle(STD_ERROR_HANDLE);
if (console_handle == INVALID_HANDLE_VALUE) {
return;
}
CONSOLE_SCREEN_BUFFER_INFO original_info = {0};
GetConsoleScreenBufferInfo(console_handle, &original_info);

View File

@@ -10,20 +10,8 @@ namespace Log {
struct Entry;
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
* leaving only the path relative to the sources root.
*
* @param path The input file path as a null-terminated string
* @param root The name of the root source directory as a null-terminated string. Path up to and
* including the last occurrence of this name will be stripped
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
*/
const char* TrimSourcePath(const char* path, const char* root = "src");
/// Formats a log entry into the provided text buffer.
void FormatLogMessage(const Entry& entry, char* out_text, size_t text_len);
std::string FormatLogMessage(const Entry& entry);
/// Formats and prints a log entry to stderr.
void PrintMessage(const Entry& entry);
/// Prints the same message as `PrintMessage`, but colored acoording to the severity level.

View File

@@ -462,4 +462,27 @@ std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_l
return std::string(buffer, len);
}
const char* TrimSourcePath(const char* path, const char* root) {
const char* p = path;
while (*p != '\0') {
const char* next_slash = p;
while (*next_slash != '\0' && *next_slash != '/' && *next_slash != '\\') {
++next_slash;
}
bool is_src = Common::ComparePartialString(p, next_slash, root);
p = next_slash;
if (*p != '\0') {
++p;
}
if (is_src) {
path = p;
}
}
return path;
}
} // namespace Common

View File

@@ -134,4 +134,17 @@ bool ComparePartialString(InIt begin, InIt end, const char* other) {
* NUL-terminated then the string ends at max_len characters.
*/
std::string StringFromFixedZeroTerminatedBuffer(const char* buffer, size_t max_len);
/**
* Attempts to trim an arbitrary prefix from `path`, leaving only the part starting at `root`. It's
* intended to be used to strip a system-specific build directory from the `__FILE__` macro,
* leaving only the path relative to the sources root.
*
* @param path The input file path as a null-terminated string
* @param root The name of the root source directory as a null-terminated string. Path up to and
* including the last occurrence of this name will be stripped
* @return A pointer to the same string passed as `path`, but starting at the trimmed portion
*/
const char* TrimSourcePath(const char* path, const char* root = "src");
} // namespace Common

View File

@@ -103,7 +103,19 @@ inline __attribute__((always_inline)) u64 swap64(u64 _data) {
return __builtin_bswap64(_data);
}
#elif defined(__Bitrig__) || defined(__OpenBSD__)
// swap16, swap32, swap64 are left as is
// redefine swap16, swap32, swap64 as inline functions
#undef swap16
#undef swap32
#undef swap64
inline u16 swap16(u16 _data) {
return __swap16(_data);
}
inline u32 swap32(u32 _data) {
return __swap32(_data);
}
inline u64 swap64(u64 _data) {
return __swap64(_data);
}
#elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__)
inline u16 swap16(u16 _data) {
return bswap16(_data);

View File

@@ -15,7 +15,7 @@ namespace Telemetry {
/// Field type, used for grouping fields together in the final submitted telemetry log
enum class FieldType : u8 {
None = 0, ///< No specified field group
App, ///< Citra application fields (e.g. version, branch, etc.)
App, ///< yuzu application fields (e.g. version, branch, etc.)
Session, ///< Emulated session fields (e.g. title ID, log, etc.)
Performance, ///< Emulated performance (e.g. fps, emulated CPU speed, etc.)
UserFeedback, ///< User submitted feedback (e.g. star rating, user notes, etc.)

View File

@@ -130,6 +130,8 @@ add_library(core STATIC
hle/service/friend/friend.h
hle/service/friend/friend_a.cpp
hle/service/friend/friend_a.h
hle/service/friend/friend_u.cpp
hle/service/friend/friend_u.h
hle/service/hid/hid.cpp
hle/service/hid/hid.h
hle/service/lm/lm.cpp

View File

@@ -174,8 +174,9 @@ u64 Disk_Storage::GetSize() const {
}
bool Disk_Storage::SetSize(const u64 size) const {
LOG_WARNING(Service_FS, "(STUBBED) called");
return false;
file->Resize(size);
file->Flush();
return true;
}
Disk_Directory::Disk_Directory(const std::string& path) : directory() {

View File

@@ -268,8 +268,11 @@ std::vector<u8> HLERequestContext::ReadBuffer() const {
size_t HLERequestContext::WriteBuffer(const void* buffer, size_t size) const {
const bool is_buffer_b{BufferDescriptorB().size() && BufferDescriptorB()[0].Size()};
ASSERT_MSG(size <= GetWriteBufferSize(), "Size %lx is too big", size);
const size_t buffer_size{GetWriteBufferSize()};
if (size > buffer_size) {
LOG_CRITICAL(Core, "size (%016zx) is greater than buffer_size (%016zx)", size, buffer_size);
size = buffer_size; // TODO(bunnei): This needs to be HW tested
}
if (is_buffer_b) {
Memory::WriteBlock(BufferDescriptorB()[0].Address(), buffer, size);

View File

@@ -121,8 +121,9 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
// TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part
// of the user address space.
vm_manager
.MapMemoryBlock(Memory::STACK_VADDR, std::make_shared<std::vector<u8>>(stack_size, 0), 0,
stack_size, MemoryState::Mapped)
.MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
MemoryState::Mapped)
.Unwrap();
misc_memory_used += stack_size;
memory_region->used += stack_size;

View File

@@ -120,18 +120,6 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return ERR_WRONG_PERMISSION;
}
// TODO(Subv): The same process that created a SharedMemory object
// can not map it in its own address space unless it was created with addr=0, result 0xD900182C.
if (address != 0) {
// TODO(shinyquagsire23): Check for virtual/mappable memory here too?
if (address >= Memory::HEAP_VADDR && address < Memory::HEAP_VADDR_END) {
LOG_ERROR(Kernel, "cannot map id=%u, address=0x%lx name=%s, invalid address",
GetObjectId(), address, name.c_str());
return ERR_INVALID_ADDRESS;
}
}
VAddr target_address = address;
if (base_address == 0 && target_address == 0) {

View File

@@ -371,6 +371,18 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
return RESULT_SUCCESS;
}
/// Sets the thread activity
static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x%08X, unknown=0x%08X", handle, unknown);
return RESULT_SUCCESS;
}
/// Gets the thread context
static ResultCode GetThreadContext(Handle handle, VAddr addr) {
LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x%08X, addr=0x%" PRIx64, handle, addr);
return RESULT_SUCCESS;
}
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
const SharedPtr<Thread> thread = g_handle_table.Get<Thread>(handle);
@@ -853,8 +865,8 @@ static const FunctionDef SVC_Table[] = {
{0x2F, nullptr, "GetLastThreadInfo"},
{0x30, nullptr, "GetResourceLimitLimitValue"},
{0x31, nullptr, "GetResourceLimitCurrentValue"},
{0x32, nullptr, "SetThreadActivity"},
{0x33, nullptr, "GetThreadContext"},
{0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
{0x34, nullptr, "Unknown"},
{0x35, nullptr, "Unknown"},
{0x36, nullptr, "Unknown"},

View File

@@ -70,6 +70,11 @@ void SvcWrap() {
FuncReturn(retval);
}
template <ResultCode func(u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), PARAM(1)).raw);
}
template <ResultCode func(u32, u32, u64)>
void SvcWrap() {
FuncReturn(func((u32)(PARAM(0) & 0xFFFFFFFF), (u32)(PARAM(1) & 0xFFFFFFFF), PARAM(2)).raw);

View File

@@ -342,7 +342,7 @@ SharedPtr<Thread> SetupMainThread(VAddr entry_point, u32 priority,
// Initialize new "main" thread
auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0,
Memory::STACK_VADDR_END, owner_process);
Memory::STACK_AREA_VADDR_END, owner_process);
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();

View File

@@ -25,7 +25,7 @@ class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
IAudioOut() : ServiceFramework("IAudioOut"), audio_out_state(AudioState::Stopped) {
static const FunctionInfo functions[] = {
{0x0, nullptr, "GetAudioOutState"},
{0x0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
{0x1, &IAudioOut::StartAudioOut, "StartAudioOut"},
{0x2, &IAudioOut::StopAudioOut, "StopAudioOut"},
{0x3, &IAudioOut::AppendAudioOutBuffer_1, "AppendAudioOutBuffer_1"},
@@ -57,6 +57,13 @@ public:
}
private:
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(audio_out_state));
}
void StartAudioOut(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");

View File

@@ -158,7 +158,7 @@ public:
{0x0, &IAudioDevice::ListAudioDeviceName, "ListAudioDeviceName"},
{0x1, &IAudioDevice::SetAudioDeviceOutputVolume, "SetAudioDeviceOutputVolume"},
{0x2, nullptr, "GetAudioDeviceOutputVolume"},
{0x3, nullptr, "GetActiveAudioDeviceName"},
{0x3, &IAudioDevice::GetActiveAudioDeviceName, "GetActiveAudioDeviceName"},
{0x4, &IAudioDevice::QueryAudioDeviceSystemEvent, "QueryAudioDeviceSystemEvent"},
{0x5, &IAudioDevice::GetActiveChannelCount, "GetActiveChannelCount"},
{0x6, nullptr, "ListAudioDeviceNameAuto"},
@@ -199,6 +199,18 @@ private:
rb.Push(RESULT_SUCCESS);
}
void GetActiveAudioDeviceName(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
IPC::RequestParser rp{ctx};
const std::string audio_interface = "AudioDevice";
ctx.WriteBuffer(audio_interface.c_str(), audio_interface.size());
IPC::ResponseBuilder rb = rp.MakeBuilder(3, 0, 0);
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
}
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");

View File

@@ -72,8 +72,8 @@ public:
explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend)
: ServiceFramework("IFile"), backend(std::move(backend)) {
static const FunctionInfo functions[] = {
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"},
{3, nullptr, "SetSize"}, {4, nullptr, "GetSize"},
{0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, {2, nullptr, "Flush"},
{3, &IFile::SetSize, "SetSize"}, {4, &IFile::GetSize, "GetSize"},
};
RegisterHandlers(functions);
}
@@ -150,6 +150,25 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
backend->SetSize(size);
LOG_DEBUG(Service_FS, "called, size=%" PRIu64, size);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetSize(Kernel::HLERequestContext& ctx) {
const u64 size = backend->GetSize();
LOG_DEBUG(Service_FS, "called, size=%" PRIu64, size);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(size);
}
};
class IDirectory final : public ServiceFramework<IDirectory> {

View File

@@ -6,6 +6,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/friend/friend.h"
#include "core/hle/service/friend/friend_a.h"
#include "core/hle/service/friend/friend_u.h"
namespace Service {
namespace Friend {
@@ -22,6 +23,7 @@ Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
void InstallInterfaces(SM::ServiceManager& service_manager) {
auto module = std::make_shared<Module>();
std::make_shared<Friend_A>(module)->InstallAsService(service_manager);
std::make_shared<Friend_U>(module)->InstallAsService(service_manager);
}
} // namespace Friend

View File

@@ -0,0 +1,19 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "core/hle/service/friend/friend_u.h"
namespace Service {
namespace Friend {
Friend_U::Friend_U(std::shared_ptr<Module> module)
: Module::Interface(std::move(module), "friend:u") {
static const FunctionInfo functions[] = {
{0, &Friend_U::Unknown, "Unknown"},
};
RegisterHandlers(functions);
}
} // namespace Friend
} // namespace Service

View File

@@ -0,0 +1,18 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "core/hle/service/friend/friend.h"
namespace Service {
namespace Friend {
class Friend_U final : public Module::Interface {
public:
explicit Friend_U(std::shared_ptr<Module> module);
};
} // namespace Friend
} // namespace Service

View File

@@ -65,13 +65,14 @@ private:
}
void UpdatePadCallback(u64 userdata, int cycles_late) {
SharedMemory* mem = reinterpret_cast<SharedMemory*>(shared_mem->GetPointer());
SharedMemory mem{};
std::memcpy(&mem, shared_mem->GetPointer(), sizeof(SharedMemory));
if (is_device_reload_pending.exchange(false))
LoadInputDevices();
// Set up controllers as neon red+blue Joy-Con attached to console
ControllerHeader& controller_header = mem->controllers[Controller_Handheld].header;
ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header;
controller_header.type = ControllerType_Handheld | ControllerType_JoyconPair;
controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent;
controller_header.right_color_body = JOYCON_BODY_NEON_RED;
@@ -79,8 +80,8 @@ private:
controller_header.left_color_body = JOYCON_BODY_NEON_BLUE;
controller_header.left_color_buttons = JOYCON_BUTTONS_NEON_BLUE;
for (int layoutIdx = 0; layoutIdx < HID_NUM_LAYOUTS; layoutIdx++) {
ControllerLayout& layout = mem->controllers[Controller_Handheld].layouts[layoutIdx];
for (int index = 0; index < HID_NUM_LAYOUTS; index++) {
ControllerLayout& layout = mem.controllers[Controller_Handheld].layouts[index];
layout.header.num_entries = HID_NUM_ENTRIES;
layout.header.max_entry_index = HID_NUM_ENTRIES - 1;
@@ -136,10 +137,25 @@ private:
// layouts)
}
// TODO(shinyquagsire23): Update touch info
// TODO(bunnei): Properly implement the touch screen, the below will just write empty data
TouchScreen& touchscreen = mem.touchscreen;
const u64 last_entry = touchscreen.header.latest_entry;
const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size();
const u64 timestamp = CoreTiming::GetTicks();
const u64 sample_counter = touchscreen.entries[last_entry].header.timestamp + 1;
touchscreen.header.timestamp_ticks = timestamp;
touchscreen.header.num_entries = touchscreen.entries.size();
touchscreen.header.latest_entry = curr_entry;
touchscreen.header.max_entry_index = touchscreen.entries.size();
touchscreen.header.timestamp = timestamp;
touchscreen.entries[curr_entry].header.timestamp = sample_counter;
touchscreen.entries[curr_entry].header.num_touches = 0;
// TODO(shinyquagsire23): Signal events
std::memcpy(shared_mem->GetPointer(), &mem, sizeof(SharedMemory));
// Reschedule recurrent event
CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
}
@@ -185,6 +201,7 @@ public:
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
{100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
{101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
{102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
{103, &Hid::ActivateNpad, "ActivateNpad"},
{106, &Hid::AcquireNpadStyleSetUpdateEventHandle,
@@ -265,6 +282,13 @@ private:
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);

View File

@@ -70,9 +70,8 @@ private:
}
void GetResult(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
}
void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");

View File

@@ -112,15 +112,15 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
auto cmd_buf = ctx.CommandBuffer();
std::string function_name = info == nullptr ? fmt::format("{}", ctx.GetCommand()) : info->name;
fmt::MemoryWriter w;
w.write("function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name,
cmd_buf[0]);
fmt::memory_buffer buf;
fmt::format_to(buf, "function '{}': port='{}' cmd_buf={{[0]={:#x}", function_name, service_name,
cmd_buf[0]);
for (int i = 1; i <= 8; ++i) {
w.write(", [{}]={:#x}", i, cmd_buf[i]);
fmt::format_to(buf, ", [{}]={:#x}", i, cmd_buf[i]);
}
w << '}';
buf.push_back('}');
LOG_ERROR(Service, "unknown / unimplemented %s", w.c_str());
LOG_ERROR(Service, "unknown / unimplemented %s", fmt::to_string(buf).c_str());
UNIMPLEMENTED();
}

View File

@@ -17,6 +17,7 @@
#include "core/hle/service/vi/vi_m.h"
#include "core/hle/service/vi/vi_s.h"
#include "core/hle/service/vi/vi_u.h"
#include "core/settings.h"
#include "video_core/renderer_base.h"
#include "video_core/video_core.h"
@@ -711,6 +712,23 @@ private:
rb.Push(RESULT_SUCCESS);
}
void GetDisplayResolution(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
u64 display_id = rp.Pop<u64>();
IPC::ResponseBuilder rb = rp.MakeBuilder(6, 0, 0);
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
rb.Push(static_cast<u32>(DisplayResolution::DockedWidth));
rb.Push(static_cast<u32>(DisplayResolution::DockedHeight));
} else {
rb.Push(static_cast<u32>(DisplayResolution::UndockedWidth));
rb.Push(static_cast<u32>(DisplayResolution::UndockedHeight));
}
}
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
@@ -808,6 +826,7 @@ IApplicationDisplayService::IApplicationDisplayService(
{1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
{1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
{1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
{1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
{2101, &IApplicationDisplayService::SetLayerScalingMode, "SetLayerScalingMode"},
{2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
{2030, &IApplicationDisplayService::CreateStrayLayer, "CreateStrayLayer"},

View File

@@ -14,6 +14,13 @@ struct EventType;
namespace Service {
namespace VI {
enum class DisplayResolution : u32 {
DockedWidth = 1920,
DockedHeight = 1080,
UndockedWidth = 1280,
UndockedHeight = 720,
};
class Module final {
public:
class Interface : public ServiceFramework<Interface> {

View File

@@ -76,7 +76,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& fil
} else if (Common::ToLower(virtual_name) == "sdk") {
is_sdk_found = true;
} else {
// Contrinue searching
// Continue searching
return true;
}

View File

@@ -414,7 +414,7 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(codeset->entrypoint, 48, Memory::STACK_SIZE);
process->Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -137,7 +137,7 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(base_addr, 48, Memory::STACK_SIZE);
process->Run(base_addr, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -165,7 +165,7 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) {
process->address_mappings = default_address_mappings;
process->resource_limit =
Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION);
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::STACK_SIZE);
process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
return ResultStatus::Success;

View File

@@ -1,40 +0,0 @@
// Copyright 2018 yuzu emulator team
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
#include "common/common_types.h"
#include "core/hle/kernel/kernel.h"
#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace Loader {
/// Loads an XCI file
class AppLoader_XCI final : public AppLoader, Linker {
public:
AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath);
/**
* Returns the type of the file
* @param file FileUtil::IOFile open file
* @param filepath Path of the file that we are opening.
* @return FileType found, or FileType::Error if this loader doesn't know it
*/
static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath);
FileType GetFileType() override {
return IdentifyType(file, filepath);
}
ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
private:
bool LoadNro(const std::string& path, VAddr load_base);
std::string filepath;
};
} // namespace Loader

View File

@@ -162,12 +162,13 @@ enum : VAddr {
TLS_AREA_VADDR = NEW_LINEAR_HEAP_VADDR_END,
TLS_ENTRY_SIZE = 0x200,
TLS_AREA_SIZE = 0x10000000,
TLS_ADREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE,
/// Application stack
STACK_VADDR = TLS_ADREA_VADDR_END,
STACK_SIZE = 0x10000,
STACK_VADDR_END = STACK_VADDR + STACK_SIZE,
STACK_AREA_VADDR = TLS_AREA_VADDR_END,
STACK_AREA_SIZE = 0x10000000,
STACK_AREA_VADDR_END = STACK_AREA_VADDR + STACK_AREA_SIZE,
DEFAULT_STACK_SIZE = 0x100000,
/// Application heap
/// Size is confirmed to be a static value on fw 3.0.0

View File

@@ -87,8 +87,8 @@ TelemetrySession::TelemetrySession() {
#ifdef ENABLE_WEB_SERVICE
if (Settings::values.enable_telemetry) {
backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.telemetry_endpoint_url, Settings::values.citra_username,
Settings::values.citra_token);
Settings::values.telemetry_endpoint_url, Settings::values.yuzu_username,
Settings::values.yuzu_token);
} else {
backend = std::make_unique<Telemetry::NullVisitor>();
}

View File

@@ -50,8 +50,8 @@ u64 RegenerateTelemetryId();
/**
* Verifies the username and token.
* @param username Citra username to use for authentication.
* @param token Citra token to use for authentication.
* @param username yuzu username to use for authentication.
* @param token yuzu token to use for authentication.
* @param func A function that gets exectued when the verification is finished
* @returns Future with bool indicating whether the verification succeeded
*/

View File

@@ -11,6 +11,8 @@ add_library(video_core STATIC
engines/maxwell_compute.h
gpu.cpp
gpu.h
macro_interpreter.cpp
macro_interpreter.h
memory_manager.cpp
memory_manager.h
rasterizer_interface.h

View File

@@ -19,35 +19,21 @@ namespace Engines {
/// First register id that is actually a Macro call.
constexpr u32 MacroRegistersStart = 0xE00;
const std::unordered_map<u32, Maxwell3D::MethodInfo> Maxwell3D::method_handlers = {
{0xE1A, {"BindTextureInfoBuffer", 1, &Maxwell3D::BindTextureInfoBuffer}},
{0xE24, {"SetShader", 5, &Maxwell3D::SetShader}},
{0xE2A, {"BindStorageBuffer", 1, &Maxwell3D::BindStorageBuffer}},
};
Maxwell3D::Maxwell3D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
Maxwell3D::Maxwell3D(MemoryManager& memory_manager)
: memory_manager(memory_manager), macro_interpreter(*this) {}
void Maxwell3D::SubmitMacroCode(u32 entry, std::vector<u32> code) {
uploaded_macros[entry * 2 + MacroRegistersStart] = std::move(code);
}
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
// TODO(Subv): Write an interpreter for the macros uploaded via registers 0x45 and 0x47
void Maxwell3D::CallMacroMethod(u32 method, std::vector<u32> parameters) {
auto macro_code = uploaded_macros.find(method);
// The requested macro must have been uploaded already.
ASSERT_MSG(uploaded_macros.find(method) != uploaded_macros.end(), "Macro %08X was not uploaded",
method);
ASSERT_MSG(macro_code != uploaded_macros.end(), "Macro %08X was not uploaded", method);
auto itr = method_handlers.find(method);
ASSERT_MSG(itr != method_handlers.end(), "Unhandled method call %08X", method);
ASSERT(itr->second.arguments == parameters.size());
(this->*itr->second.handler)(parameters);
// Reset the current macro and its parameters.
// Reset the current macro and execute it.
executing_macro = 0;
macro_params.clear();
macro_interpreter.Execute(macro_code->second, std::move(parameters));
}
void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
@@ -77,7 +63,7 @@ void Maxwell3D::WriteReg(u32 method, u32 value, u32 remaining_params) {
// Call the macro when there are no more parameters in the command buffer
if (remaining_params == 0) {
CallMacroMethod(executing_macro, macro_params);
CallMacroMethod(executing_macro, std::move(macro_params));
}
return;
}
@@ -193,84 +179,6 @@ void Maxwell3D::DrawArrays() {
VideoCore::g_renderer->Rasterizer()->AccelerateDrawBatch(false /*is_indexed*/);
}
void Maxwell3D::BindTextureInfoBuffer(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Shader stage, usually 4 for FragmentShader
*/
u32 stage = parameters[0];
// Perform the same operations as the real macro code.
GPUVAddr address = static_cast<GPUVAddr>(regs.tex_info_buffers.address[stage]) << 8;
u32 size = regs.tex_info_buffers.size[stage];
regs.const_buffer.cb_size = size;
regs.const_buffer.cb_address_high = address >> 32;
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
}
void Maxwell3D::SetShader(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Shader Program.
* [1] = Unknown, presumably the shader id.
* [2] = Offset to the start of the shader, after the 0x30 bytes header.
* [3] = Shader Stage.
* [4] = Const Buffer Address >> 8.
*/
auto shader_program = static_cast<Regs::ShaderProgram>(parameters[0]);
// TODO(Subv): This address is probably an offset from the CODE_ADDRESS register.
GPUVAddr address = parameters[2];
auto shader_stage = static_cast<Regs::ShaderStage>(parameters[3]);
GPUVAddr cb_address = parameters[4] << 8;
auto& shader = state.shader_programs[static_cast<size_t>(shader_program)];
shader.program = shader_program;
shader.stage = shader_stage;
shader.address = address;
// Perform the same operations as the real macro code.
// TODO(Subv): Early exit if register 0xD1C + shader_program contains the same as params[1].
auto& shader_regs = regs.shader_config[static_cast<size_t>(shader_program)];
shader_regs.start_id = address;
// TODO(Subv): Write params[1] to register 0xD1C + shader_program.
// TODO(Subv): Write params[2] to register 0xD22 + shader_program.
// Note: This value is hardcoded in the macro's code.
static constexpr u32 DefaultCBSize = 0x10000;
regs.const_buffer.cb_size = DefaultCBSize;
regs.const_buffer.cb_address_high = cb_address >> 32;
regs.const_buffer.cb_address_low = cb_address & 0xFFFFFFFF;
// Write a hardcoded 0x11 to CB_BIND, this binds the current const buffer to buffer c1[] in the
// shader. It's likely that these are the constants for the shader.
regs.cb_bind[static_cast<size_t>(shader_stage)].valid.Assign(1);
regs.cb_bind[static_cast<size_t>(shader_stage)].index.Assign(1);
ProcessCBBind(shader_stage);
}
void Maxwell3D::BindStorageBuffer(const std::vector<u32>& parameters) {
/**
* Parameters description:
* [0] = Buffer offset >> 2
*/
u32 buffer_offset = parameters[0] << 2;
// Perform the same operations as the real macro code.
// Note: This value is hardcoded in the macro's code.
static constexpr u32 DefaultCBSize = 0x5F00;
regs.const_buffer.cb_size = DefaultCBSize;
GPUVAddr address = regs.ssbo_info.BufferAddress();
regs.const_buffer.cb_address_high = address >> 32;
regs.const_buffer.cb_address_low = address & 0xFFFFFFFF;
regs.const_buffer.cb_pos = buffer_offset;
}
void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
// Bind the buffer currently in CB_ADDRESS to the specified index in the desired shader stage.
auto& shader = state.shader_stages[static_cast<size_t>(stage)];
@@ -323,6 +231,8 @@ Texture::TICEntry Maxwell3D::GetTICEntry(u32 tic_index) const {
// TODO(Subv): Different data types for separate components are not supported
ASSERT(r_type == g_type && r_type == b_type && r_type == a_type);
// TODO(Subv): Only UNORM formats are supported for now.
ASSERT(r_type == Texture::ComponentType::UNORM);
return tic_entry;
}
@@ -386,5 +296,10 @@ std::vector<Texture::FullTextureInfo> Maxwell3D::GetStageTextures(Regs::ShaderSt
return textures;
}
u32 Maxwell3D::GetRegisterValue(u32 method) const {
ASSERT_MSG(method < Regs::NUM_REGS, "Invalid Maxwell3D register");
return regs.reg_array[method];
}
} // namespace Engines
} // namespace Tegra

View File

@@ -13,6 +13,7 @@
#include "common/common_types.h"
#include "common/math_util.h"
#include "video_core/gpu.h"
#include "video_core/macro_interpreter.h"
#include "video_core/memory_manager.h"
#include "video_core/textures/texture.h"
@@ -498,22 +499,18 @@ public:
bool enabled;
};
struct ShaderProgramInfo {
Regs::ShaderStage stage;
Regs::ShaderProgram program;
GPUVAddr address;
};
struct ShaderStageInfo {
std::array<ConstBufferInfo, Regs::MaxConstBuffers> const_buffers;
};
std::array<ShaderStageInfo, Regs::MaxShaderStage> shader_stages;
std::array<ShaderProgramInfo, Regs::MaxShaderProgram> shader_programs;
};
State state{};
/// Reads a register value located at the input method address
u32 GetRegisterValue(u32 method) const;
/// Write the value to the register identified by method.
void WriteReg(u32 method, u32 value, u32 remaining_params);
@@ -533,6 +530,9 @@ private:
/// Parameters that have been submitted to the macro call so far.
std::vector<u32> macro_params;
/// Interpreter for the macro codes uploaded to the GPU.
MacroInterpreter macro_interpreter;
/// Retrieves information about a specific TIC entry from the TIC buffer.
Texture::TICEntry GetTICEntry(u32 tic_index) const;
@@ -544,7 +544,7 @@ private:
* @param method Method to call
* @param parameters Arguments to the method call
*/
void CallMacroMethod(u32 method, const std::vector<u32>& parameters);
void CallMacroMethod(u32 method, std::vector<u32> parameters);
/// Handles a write to the QUERY_GET register.
void ProcessQueryGet();
@@ -557,19 +557,6 @@ private:
/// Handles a write to the VERTEX_END_GL register, triggering a draw.
void DrawArrays();
/// Method call handlers
void BindTextureInfoBuffer(const std::vector<u32>& parameters);
void SetShader(const std::vector<u32>& parameters);
void BindStorageBuffer(const std::vector<u32>& parameters);
struct MethodInfo {
const char* name;
u32 arguments;
void (Maxwell3D::*handler)(const std::vector<u32>& parameters);
};
static const std::unordered_map<u32, MethodInfo> method_handlers;
};
#define ASSERT_REG_POSITION(field_name, position) \

View File

@@ -0,0 +1,257 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/macro_interpreter.h"
namespace Tegra {
MacroInterpreter::MacroInterpreter(Engines::Maxwell3D& maxwell3d) : maxwell3d(maxwell3d) {}
void MacroInterpreter::Execute(const std::vector<u32>& code, std::vector<u32> parameters) {
Reset();
registers[1] = parameters[0];
this->parameters = std::move(parameters);
// Execute the code until we hit an exit condition.
bool keep_executing = true;
while (keep_executing) {
keep_executing = Step(code, false);
}
// Assert the the macro used all the input parameters
ASSERT(next_parameter_index == this->parameters.size());
}
void MacroInterpreter::Reset() {
registers = {};
pc = 0;
delayed_pc = boost::none;
method_address.raw = 0;
parameters.clear();
// The next parameter index starts at 1, because $r1 already has the value of the first
// parameter.
next_parameter_index = 1;
}
bool MacroInterpreter::Step(const std::vector<u32>& code, bool is_delay_slot) {
u32 base_address = pc;
Opcode opcode = GetOpcode(code);
pc += 4;
// Update the program counter if we were delayed
if (delayed_pc != boost::none) {
ASSERT(is_delay_slot);
pc = *delayed_pc;
delayed_pc = boost::none;
}
switch (opcode.operation) {
case Operation::ALU: {
u32 result = GetALUResult(opcode.alu_operation, GetRegister(opcode.src_a),
GetRegister(opcode.src_b));
ProcessResult(opcode.result_operation, opcode.dst, result);
break;
}
case Operation::AddImmediate: {
ProcessResult(opcode.result_operation, opcode.dst,
GetRegister(opcode.src_a) + opcode.immediate);
break;
}
case Operation::ExtractInsert: {
u32 dst = GetRegister(opcode.src_a);
u32 src = GetRegister(opcode.src_b);
src = (src >> opcode.bf_src_bit) & opcode.GetBitfieldMask();
dst &= ~(opcode.GetBitfieldMask() << opcode.bf_dst_bit);
dst |= src << opcode.bf_dst_bit;
ProcessResult(opcode.result_operation, opcode.dst, dst);
break;
}
case Operation::ExtractShiftLeftImmediate: {
u32 dst = GetRegister(opcode.src_a);
u32 src = GetRegister(opcode.src_b);
u32 result = ((src >> dst) & opcode.GetBitfieldMask()) << opcode.bf_dst_bit;
ProcessResult(opcode.result_operation, opcode.dst, result);
break;
}
case Operation::ExtractShiftLeftRegister: {
u32 dst = GetRegister(opcode.src_a);
u32 src = GetRegister(opcode.src_b);
u32 result = ((src >> opcode.bf_src_bit) & opcode.GetBitfieldMask()) << dst;
ProcessResult(opcode.result_operation, opcode.dst, result);
break;
}
case Operation::Read: {
u32 result = Read(GetRegister(opcode.src_a) + opcode.immediate);
ProcessResult(opcode.result_operation, opcode.dst, result);
break;
}
case Operation::Branch: {
ASSERT_MSG(!is_delay_slot, "Executing a branch in a delay slot is not valid");
u32 value = GetRegister(opcode.src_a);
bool taken = EvaluateBranchCondition(opcode.branch_condition, value);
if (taken) {
// Ignore the delay slot if the branch has the annul bit.
if (opcode.branch_annul) {
pc = base_address + (opcode.immediate << 2);
return true;
}
delayed_pc = base_address + (opcode.immediate << 2);
// Execute one more instruction due to the delay slot.
return Step(code, true);
}
break;
}
default:
UNIMPLEMENTED_MSG("Unimplemented macro operation %u",
static_cast<u32>(opcode.operation.Value()));
}
if (opcode.is_exit) {
// Exit has a delay slot, execute the next instruction
// Note: Executing an exit during a branch delay slot will cause the instruction at the
// branch target to be executed before exiting.
Step(code, true);
return false;
}
return true;
}
MacroInterpreter::Opcode MacroInterpreter::GetOpcode(const std::vector<u32>& code) const {
ASSERT((pc % sizeof(u32)) == 0);
ASSERT(pc < code.size() * sizeof(u32));
return {code[pc / sizeof(u32)]};
}
u32 MacroInterpreter::GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const {
switch (operation) {
case ALUOperation::Add:
return src_a + src_b;
// TODO(Subv): Implement AddWithCarry
case ALUOperation::Subtract:
return src_a - src_b;
// TODO(Subv): Implement SubtractWithBorrow
case ALUOperation::Xor:
return src_a ^ src_b;
case ALUOperation::Or:
return src_a | src_b;
case ALUOperation::And:
return src_a & src_b;
case ALUOperation::AndNot:
return src_a & ~src_b;
case ALUOperation::Nand:
return ~(src_a & src_b);
default:
UNIMPLEMENTED_MSG("Unimplemented ALU operation %u", static_cast<u32>(operation));
}
}
void MacroInterpreter::ProcessResult(ResultOperation operation, u32 reg, u32 result) {
switch (operation) {
case ResultOperation::IgnoreAndFetch:
// Fetch parameter and ignore result.
SetRegister(reg, FetchParameter());
break;
case ResultOperation::Move:
// Move result.
SetRegister(reg, result);
break;
case ResultOperation::MoveAndSetMethod:
// Move result and use as Method Address.
SetRegister(reg, result);
SetMethodAddress(result);
break;
case ResultOperation::FetchAndSend:
// Fetch parameter and send result.
SetRegister(reg, FetchParameter());
Send(result);
break;
case ResultOperation::MoveAndSend:
// Move and send result.
SetRegister(reg, result);
Send(result);
break;
case ResultOperation::FetchAndSetMethod:
// Fetch parameter and use result as Method Address.
SetRegister(reg, FetchParameter());
SetMethodAddress(result);
break;
case ResultOperation::MoveAndSetMethodFetchAndSend:
// Move result and use as Method Address, then fetch and send parameter.
SetRegister(reg, result);
SetMethodAddress(result);
Send(FetchParameter());
break;
case ResultOperation::MoveAndSetMethodSend:
// Move result and use as Method Address, then send bits 12:17 of result.
SetRegister(reg, result);
SetMethodAddress(result);
Send((result >> 12) & 0b111111);
break;
default:
UNIMPLEMENTED_MSG("Unimplemented result operation %u", static_cast<u32>(operation));
}
}
u32 MacroInterpreter::FetchParameter() {
ASSERT(next_parameter_index < parameters.size());
return parameters[next_parameter_index++];
}
u32 MacroInterpreter::GetRegister(u32 register_id) const {
// Register 0 is supposed to always return 0.
if (register_id == 0)
return 0;
ASSERT(register_id < registers.size());
return registers[register_id];
}
void MacroInterpreter::SetRegister(u32 register_id, u32 value) {
// Register 0 is supposed to always return 0. NOP is implemented as a store to the zero
// register.
if (register_id == 0)
return;
ASSERT(register_id < registers.size());
registers[register_id] = value;
}
void MacroInterpreter::SetMethodAddress(u32 address) {
method_address.raw = address;
}
void MacroInterpreter::Send(u32 value) {
maxwell3d.WriteReg(method_address.address, value, 0);
// Increment the method address by the method increment.
method_address.address.Assign(method_address.address.Value() +
method_address.increment.Value());
}
u32 MacroInterpreter::Read(u32 method) const {
return maxwell3d.GetRegisterValue(method);
}
bool MacroInterpreter::EvaluateBranchCondition(BranchCondition cond, u32 value) const {
switch (cond) {
case BranchCondition::Zero:
return value == 0;
case BranchCondition::NotZero:
return value != 0;
}
UNREACHABLE();
}
} // namespace Tegra

View File

@@ -0,0 +1,164 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <vector>
#include <boost/optional.hpp>
#include "common/bit_field.h"
#include "common/common_types.h"
namespace Tegra {
namespace Engines {
class Maxwell3D;
}
class MacroInterpreter final {
public:
explicit MacroInterpreter(Engines::Maxwell3D& maxwell3d);
/**
* Executes the macro code with the specified input parameters.
* @param code The macro byte code to execute
* @param parameters The parameters of the macro
*/
void Execute(const std::vector<u32>& code, std::vector<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 {
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;
}
};
union MethodAddress {
u32 raw;
BitField<0, 12, u32> address;
BitField<12, 6, u32> increment;
};
/// Resets the execution engine state, zeroing registers, etc.
void Reset();
/**
* Executes a single macro instruction located at the current program counter. Returns whether
* the interpreter should keep running.
* @param code The macro code to execute.
* @param is_delay_slot Whether the current step is being executed due to a delay slot in a
* previous instruction.
*/
bool Step(const std::vector<u32>& code, bool is_delay_slot);
/// Calculates the result of an ALU operation. src_a OP src_b;
u32 GetALUResult(ALUOperation operation, u32 src_a, u32 src_b) const;
/// Performs the result operation on the input result and stores it in the specified register
/// (if necessary).
void ProcessResult(ResultOperation operation, u32 reg, u32 result);
/// Evaluates the branch condition and returns whether the branch should be taken or not.
bool EvaluateBranchCondition(BranchCondition cond, u32 value) const;
/// Reads an opcode at the current program counter location.
Opcode GetOpcode(const std::vector<u32>& code) const;
/// Returns the specified register's value. Register 0 is hardcoded to always return 0.
u32 GetRegister(u32 register_id) const;
/// Sets the register to the input value.
void SetRegister(u32 register_id, u32 value);
/// Sets the method address to use for the next Send instruction.
void SetMethodAddress(u32 address);
/// Calls a GPU Engine method with the input parameter.
void Send(u32 value);
/// Reads a GPU register located at the method address.
u32 Read(u32 method) const;
/// Returns the next parameter in the parameter queue.
u32 FetchParameter();
Engines::Maxwell3D& maxwell3d;
u32 pc; ///< Current program counter
boost::optional<u32>
delayed_pc; ///< Program counter to execute at after the delay slot is executed.
static constexpr size_t NumMacroRegisters = 8;
/// General purpose macro registers.
std::array<u32, NumMacroRegisters> registers = {};
/// Method address to use for the next Send instruction.
MethodAddress method_address = {};
/// Input parameters of the current macro.
std::vector<u32> parameters;
/// Index of the next parameter that will be fetched by the 'parm' instruction.
u32 next_parameter_index = 0;
};
} // namespace Tegra

View File

@@ -66,6 +66,12 @@ RasterizerOpenGL::RasterizerOpenGL() {
has_ARB_separate_shader_objects = false;
has_ARB_vertex_attrib_binding = false;
// Create sampler objects
for (size_t i = 0; i < texture_samplers.size(); ++i) {
texture_samplers[i].Create();
state.texture_units[i].sampler = texture_samplers[i].sampler.handle;
}
GLint ext_num;
glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num);
for (GLint i = 0; i < ext_num; i++) {
@@ -270,7 +276,9 @@ void RasterizerOpenGL::DrawArrays() {
// TODO(bunnei): Sync framebuffer_scale uniform here
// TODO(bunnei): Sync scissorbox uniform(s) here
// TODO(bunnei): Sync and bind the texture surfaces
// Sync and bind the texture surfaces
BindTextures();
// Sync and bind the shader
if (shader_dirty) {
@@ -374,6 +382,39 @@ void RasterizerOpenGL::DrawArrays() {
}
}
void RasterizerOpenGL::BindTextures() {
using Regs = Tegra::Engines::Maxwell3D::Regs;
auto maxwell3d = Core::System::GetInstance().GPU().Get3DEngine();
// Each Maxwell shader stage can have an arbitrary number of textures, but we're limited to a
// certain number in OpenGL. We try to only use the minimum amount of host textures by not
// keeping a 1:1 relation between guest texture ids and host texture ids, ie, guest texture id 8
// can be host texture id 0 if it's the only texture used in the guest shader program.
u32 host_texture_index = 0;
for (u32 stage = 0; stage < Regs::MaxShaderStage; ++stage) {
ASSERT(host_texture_index < texture_samplers.size());
const auto textures = maxwell3d.GetStageTextures(static_cast<Regs::ShaderStage>(stage));
for (unsigned texture_index = 0; texture_index < textures.size(); ++texture_index) {
const auto& texture = textures[texture_index];
if (texture.enabled) {
texture_samplers[host_texture_index].SyncWithConfig(texture.tsc);
Surface surface = res_cache.GetTextureSurface(texture);
if (surface != nullptr) {
state.texture_units[host_texture_index].texture_2d = surface->texture.handle;
} else {
// Can occur when texture addr is null or its memory is unmapped/invalid
state.texture_units[texture_index].texture_2d = 0;
}
++host_texture_index;
} else {
state.texture_units[texture_index].texture_2d = 0;
}
}
}
}
void RasterizerOpenGL::NotifyMaxwellRegisterChanged(u32 id) {}
void RasterizerOpenGL::FlushAll() {
@@ -452,6 +493,44 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebu
return true;
}
void RasterizerOpenGL::SamplerInfo::Create() {
sampler.Create();
mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
wrap_u = wrap_v = Tegra::Texture::WrapMode::Wrap;
border_color_r = border_color_g = border_color_b = border_color_a = 0;
// default is GL_LINEAR_MIPMAP_LINEAR
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Other attributes have correct defaults
}
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
GLuint s = sampler.handle;
if (mag_filter != config.mag_filter) {
mag_filter = config.mag_filter;
glSamplerParameteri(s, GL_TEXTURE_MAG_FILTER, MaxwellToGL::TextureFilterMode(mag_filter));
}
if (min_filter != config.min_filter) {
min_filter = config.min_filter;
glSamplerParameteri(s, GL_TEXTURE_MIN_FILTER, MaxwellToGL::TextureFilterMode(min_filter));
}
if (wrap_u != config.wrap_u) {
wrap_u = config.wrap_u;
glSamplerParameteri(s, GL_TEXTURE_WRAP_S, MaxwellToGL::WrapMode(wrap_u));
}
if (wrap_v != config.wrap_v) {
wrap_v = config.wrap_v;
glSamplerParameteri(s, GL_TEXTURE_WRAP_T, MaxwellToGL::WrapMode(wrap_v));
}
if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border) {
// TODO(Subv): Implement border color
ASSERT(false);
}
}
void RasterizerOpenGL::SetShader() {
// TODO(bunnei): The below sets up a static test shader for passing untransformed vertices to
// OpenGL for rendering. This should be removed/replaced when we start emulating Maxwell
@@ -479,10 +558,10 @@ void main() {
in vec2 frag_tex_coord;
out vec4 color;
uniform sampler2D color_texture;
uniform sampler2D tex[32];
void main() {
color = vec4(1.0, 0.0, 1.0, 0.0);
color = texture(tex[0], frag_tex_coord);
}
)";
@@ -503,6 +582,15 @@ void main() {
state.draw.shader_program = test_shader.shader.handle;
state.Apply();
for (u32 texture = 0; texture < texture_samplers.size(); ++texture) {
// Set the texture samplers to correspond to different texture units
std::string uniform_name = "tex[" + std::to_string(texture) + "]";
GLint uniform_tex = glGetUniformLocation(test_shader.shader.handle, uniform_name.c_str());
if (uniform_tex != -1) {
glUniform1i(uniform_tex, TextureUnits::MaxwellTexture(texture).id);
}
}
if (has_ARB_separate_shader_objects) {
state.draw.shader_program = 0;
state.Apply();

View File

@@ -85,12 +85,34 @@ public:
"FSUniformData structure must be less than 16kb as per the OpenGL spec");
private:
struct SamplerInfo {};
class SamplerInfo {
public:
OGLSampler sampler;
/// Creates the sampler object, initializing its state so that it's in sync with the
/// SamplerInfo struct.
void Create();
/// Syncs the sampler object with the config, updating any necessary state.
void SyncWithConfig(const Tegra::Texture::TSCEntry& config);
private:
Tegra::Texture::TextureFilter mag_filter;
Tegra::Texture::TextureFilter min_filter;
Tegra::Texture::WrapMode wrap_u;
Tegra::Texture::WrapMode wrap_v;
u32 border_color_r;
u32 border_color_g;
u32 border_color_b;
u32 border_color_a;
};
/// Binds the framebuffer color and depth surface
void BindFramebufferSurfaces(const Surface& color_surface, const Surface& depth_surface,
bool has_stencil);
/// Binds the required textures to OpenGL before drawing a batch.
void BindTextures();
/// Syncs the viewport to match the guest state
void SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect, u16 res_scale);

View File

@@ -30,6 +30,7 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_state.h"
#include "video_core/textures/decoders.h"
#include "video_core/utils.h"
#include "video_core/video_core.h"
@@ -40,36 +41,36 @@ struct FormatTuple {
GLint internal_format;
GLenum format;
GLenum type;
bool compressed;
// How many pixels in the original texture are equivalent to one pixel in the compressed
// texture.
u32 compression_factor;
};
static constexpr std::array<FormatTuple, 5> fb_format_tuples = {{
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}, // RGBA8
{GL_RGB8, GL_BGR, GL_UNSIGNED_BYTE}, // RGB8
{GL_RGB5_A1, GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1}, // RGB5A1
{GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}, // RGB565
{GL_RGBA4, GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4}, // RGBA4
static constexpr std::array<FormatTuple, 1> fb_format_tuples = {{
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8
}};
static constexpr std::array<FormatTuple, 4> depth_format_tuples = {{
{GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT}, // D16
{},
{GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT}, // D24
{GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8}, // D24S8
static constexpr std::array<FormatTuple, 2> tex_format_tuples = {{
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, false, 1}, // RGBA8
{GL_COMPRESSED_RGB_S3TC_DXT1_EXT, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, true, 16}, // DXT1
}};
static constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE};
static const FormatTuple& GetFormatTuple(PixelFormat pixel_format) {
const SurfaceType type = SurfaceParams::GetFormatType(pixel_format);
if (type == SurfaceType::Color) {
ASSERT(static_cast<size_t>(pixel_format) < fb_format_tuples.size());
return fb_format_tuples[static_cast<unsigned int>(pixel_format)];
} else if (type == SurfaceType::Depth || type == SurfaceType::DepthStencil) {
size_t tuple_idx = static_cast<size_t>(pixel_format) - 14;
ASSERT(tuple_idx < depth_format_tuples.size());
return depth_format_tuples[tuple_idx];
// TODO(Subv): Implement depth formats
ASSERT_MSG(false, "Unimplemented");
} else if (type == SurfaceType::Texture) {
ASSERT(static_cast<size_t>(pixel_format) < tex_format_tuples.size());
return tex_format_tuples[static_cast<unsigned int>(pixel_format)];
}
return tex_tuple;
UNREACHABLE();
return {};
}
template <typename Map, typename Interval>
@@ -92,26 +93,16 @@ static void MortonCopyTile(u32 stride, u8* tile_buffer, u8* gl_buffer) {
u8* tile_ptr = tile_buffer + VideoCore::MortonInterleave(x, y) * bytes_per_pixel;
u8* gl_ptr = gl_buffer + ((7 - y) * stride + x) * gl_bytes_per_pixel;
if (morton_to_gl) {
if (format == PixelFormat::D24S8) {
gl_ptr[0] = tile_ptr[3];
std::memcpy(gl_ptr + 1, tile_ptr, 3);
} else {
std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
}
std::memcpy(gl_ptr, tile_ptr, bytes_per_pixel);
} else {
if (format == PixelFormat::D24S8) {
std::memcpy(tile_ptr, gl_ptr + 1, 3);
tile_ptr[3] = gl_ptr[0];
} else {
std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
}
std::memcpy(tile_ptr, gl_ptr, bytes_per_pixel);
}
}
}
}
template <bool morton_to_gl, PixelFormat format>
static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) {
void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr start, VAddr end) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(format) / 8;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(format);
@@ -122,46 +113,28 @@ static void MortonCopy(u32 stride, u32 height, u8* gl_buffer, VAddr base, VAddr
Memory::GetPointer(base), gl_buffer, morton_to_gl);
}
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 18> morton_to_gl_fns = {
template <>
void MortonCopy<true, PixelFormat::DXT1>(u32 stride, u32 height, u8* gl_buffer, VAddr base,
VAddr start, VAddr end) {
constexpr u32 bytes_per_pixel = SurfaceParams::GetFormatBpp(PixelFormat::DXT1) / 8;
constexpr u32 gl_bytes_per_pixel = CachedSurface::GetGLBytesPerPixel(PixelFormat::DXT1);
// TODO(bunnei): Assumes the default rendering GOB size of 16 (128 lines). We should check the
// configuration for this and perform more generic un/swizzle
LOG_WARNING(Render_OpenGL, "need to use correct swizzle/GOB parameters!");
auto data =
Tegra::Texture::UnswizzleTexture(base, Tegra::Texture::TextureFormat::DXT1, stride, height);
std::memcpy(gl_buffer, data.data(), data.size());
}
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 2> morton_to_gl_fns = {
MortonCopy<true, PixelFormat::RGBA8>,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
MortonCopy<true, PixelFormat::DXT1>,
};
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 18> gl_to_morton_fns = {
static constexpr std::array<void (*)(u32, u32, u8*, VAddr, VAddr, VAddr), 2> gl_to_morton_fns = {
MortonCopy<false, PixelFormat::RGBA8>,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
MortonCopy<false, PixelFormat::DXT1>,
};
// Allocate an uninitialized texture of appropriate size and format for the surface
@@ -175,8 +148,11 @@ static void AllocateSurfaceTexture(GLuint texture, const FormatTuple& format_tup
cur_state.Apply();
glActiveTexture(GL_TEXTURE0);
glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
format_tuple.format, format_tuple.type, nullptr);
if (!format_tuple.compressed) {
// Only pre-create the texture for non-compressed textures.
glTexImage2D(GL_TEXTURE_2D, 0, format_tuple.internal_format, width, height, 0,
format_tuple.format, format_tuple.type, nullptr);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@@ -606,9 +582,18 @@ void CachedSurface::UploadGLTexture(const MathUtil::Rectangle<u32>& rect, GLuint
glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(stride));
glActiveTexture(GL_TEXTURE0);
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]);
if (tuple.compressed) {
glCompressedTexImage2D(GL_TEXTURE_2D, 0, tuple.internal_format,
static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), 0,
rect.GetWidth() * rect.GetHeight() *
GetGLBytesPerPixel(pixel_format) / tuple.compression_factor,
&gl_buffer[buffer_offset]);
} else {
glTexSubImage2D(GL_TEXTURE_2D, 0, x0, y0, static_cast<GLsizei>(rect.GetWidth()),
static_cast<GLsizei>(rect.GetHeight()), tuple.format, tuple.type,
&gl_buffer[buffer_offset]);
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
@@ -954,15 +939,6 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, ScaleMatc
if (expandable != nullptr && expandable->res_scale > target_res_scale) {
target_res_scale = expandable->res_scale;
}
// Keep res_scale when reinterpreting d24s8 -> rgba8
if (params.pixel_format == PixelFormat::RGBA8) {
find_params.pixel_format = PixelFormat::D24S8;
expandable = FindMatch<MatchFlags::Expand | MatchFlags::Invalid>(
surface_cache, find_params, match_res_scale);
if (expandable != nullptr && expandable->res_scale > target_res_scale) {
target_res_scale = expandable->res_scale;
}
}
}
SurfaceParams new_params = params;
new_params.res_scale = target_res_scale;
@@ -1056,9 +1032,34 @@ SurfaceRect_Tuple RasterizerCacheOpenGL::GetSurfaceSubRect(const SurfaceParams&
return std::make_tuple(surface, surface->GetScaledSubRect(params));
}
Surface RasterizerCacheOpenGL::GetTextureSurface(const void* config) {
UNREACHABLE();
return {};
Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
auto& gpu = Core::System::GetInstance().GPU();
SurfaceParams params;
params.addr = gpu.memory_manager->PhysicalToVirtualAddress(config.tic.Address());
params.width = config.tic.Width();
params.height = config.tic.Height();
params.is_tiled = config.tic.IsTiled();
params.pixel_format = SurfaceParams::PixelFormatFromTextureFormat(config.tic.format);
params.UpdateParams();
if (config.tic.Width() % 8 != 0 || config.tic.Height() % 8 != 0) {
Surface src_surface;
MathUtil::Rectangle<u32> rect;
std::tie(src_surface, rect) = GetSurfaceSubRect(params, ScaleMatch::Ignore, true);
params.res_scale = src_surface->res_scale;
Surface tmp_surface = CreateSurface(params);
BlitTextures(src_surface->texture.handle, rect, tmp_surface->texture.handle,
tmp_surface->GetScaledRect(),
SurfaceParams::GetFormatType(params.pixel_format), read_framebuffer.handle,
draw_framebuffer.handle);
remove_surfaces.emplace(tmp_surface);
return tmp_surface;
}
return GetSurface(params, ScaleMatch::Ignore, true);
}
SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces(
@@ -1240,28 +1241,7 @@ void RasterizerCacheOpenGL::ValidateSurface(const Surface& surface, VAddr addr,
continue;
}
// D24S8 to RGBA8
if (surface->pixel_format == PixelFormat::RGBA8) {
params.pixel_format = PixelFormat::D24S8;
Surface reinterpret_surface =
FindMatch<MatchFlags::Copy>(surface_cache, params, ScaleMatch::Ignore, interval);
if (reinterpret_surface != nullptr) {
ASSERT(reinterpret_surface->pixel_format == PixelFormat::D24S8);
SurfaceInterval convert_interval = params.GetCopyableInterval(reinterpret_surface);
SurfaceParams convert_params = surface->FromInterval(convert_interval);
auto src_rect = reinterpret_surface->GetScaledSubRect(convert_params);
auto dest_rect = surface->GetScaledSubRect(convert_params);
ConvertD24S8toABGR(reinterpret_surface->texture.handle, src_rect,
surface->texture.handle, dest_rect);
surface->invalid_regions.erase(convert_interval);
continue;
}
}
// Load data from 3DS memory
// Load data from Switch memory
FlushRegion(params.addr, params.size);
surface->LoadGLBuffer(params.addr, params.end);
surface->UploadGLTexture(surface->GetSubRect(params), read_framebuffer.handle,

View File

@@ -24,6 +24,7 @@
#include "common/math_util.h"
#include "video_core/gpu.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/textures/texture.h"
struct CachedSurface;
using Surface = std::shared_ptr<CachedSurface>;
@@ -51,30 +52,8 @@ enum class ScaleMatch {
struct SurfaceParams {
enum class PixelFormat {
// First 5 formats are shared between textures and color buffers
RGBA8 = 0,
RGB8 = 1,
RGB5A1 = 2,
RGB565 = 3,
RGBA4 = 4,
// Texture-only formats
IA8 = 5,
RG8 = 6,
I8 = 7,
A8 = 8,
IA4 = 9,
I4 = 10,
A4 = 11,
ETC1 = 12,
ETC1A4 = 13,
// Depth buffer-only formats
D16 = 14,
// gap
D24 = 16,
D24S8 = 17,
DXT1 = 1,
Invalid = 255,
};
@@ -88,28 +67,15 @@ struct SurfaceParams {
};
static constexpr unsigned int GetFormatBpp(PixelFormat format) {
constexpr std::array<unsigned int, 18> bpp_table = {
if (format == PixelFormat::Invalid)
return 0;
constexpr std::array<unsigned int, 2> bpp_table = {
32, // RGBA8
24, // RGB8
16, // RGB5A1
16, // RGB565
16, // RGBA4
16, // IA8
16, // RG8
8, // I8
8, // A8
8, // IA4
4, // I4
4, // A4
4, // ETC1
8, // ETC1A4
16, // D16
0,
24, // D24
32, // D24S8
64, // DXT1
};
assert(static_cast<size_t>(format) < bpp_table.size());
ASSERT(static_cast<size_t>(format) < bpp_table.size());
return bpp_table[static_cast<size_t>(format)];
}
unsigned int GetFormatBpp() const {
@@ -134,6 +100,18 @@ struct SurfaceParams {
}
}
static PixelFormat PixelFormatFromTextureFormat(Tegra::Texture::TextureFormat format) {
// TODO(Subv): Properly implement this
switch (format) {
case Tegra::Texture::TextureFormat::A8R8G8B8:
return PixelFormat::RGBA8;
case Tegra::Texture::TextureFormat::DXT1:
return PixelFormat::DXT1;
default:
UNREACHABLE();
}
}
static bool CheckFormatsBlittable(PixelFormat pixel_format_a, PixelFormat pixel_format_b) {
SurfaceType a_type = GetFormatType(pixel_format_a);
SurfaceType b_type = GetFormatType(pixel_format_b);
@@ -154,22 +132,17 @@ struct SurfaceParams {
return false;
}
static constexpr SurfaceType GetFormatType(PixelFormat pixel_format) {
if ((unsigned int)pixel_format < 5) {
static SurfaceType GetFormatType(PixelFormat pixel_format) {
if ((unsigned int)pixel_format <= static_cast<unsigned int>(PixelFormat::RGBA8)) {
return SurfaceType::Color;
}
if ((unsigned int)pixel_format < 14) {
if ((unsigned int)pixel_format <= static_cast<unsigned int>(PixelFormat::DXT1)) {
return SurfaceType::Texture;
}
if (pixel_format == PixelFormat::D16 || pixel_format == PixelFormat::D24) {
return SurfaceType::Depth;
}
if (pixel_format == PixelFormat::D24S8) {
return SurfaceType::DepthStencil;
}
// TODO(Subv): Implement the other formats
ASSERT(false);
return SurfaceType::Invalid;
}
@@ -265,12 +238,10 @@ struct CachedSurface : SurfaceParams {
OGLTexture texture;
static constexpr unsigned int GetGLBytesPerPixel(PixelFormat format) {
// OpenGL needs 4 bpp alignment for D24 since using GL_UNSIGNED_INT as type
return format == PixelFormat::Invalid
? 0
: (format == PixelFormat::D24 || GetFormatType(format) == SurfaceType::Texture)
? 4
: SurfaceParams::GetFormatBpp(format) / 8;
if (format == PixelFormat::Invalid)
return 0;
return SurfaceParams::GetFormatBpp(format) / 8;
}
std::unique_ptr<u8[]> gl_buffer;
@@ -313,7 +284,7 @@ public:
bool load_if_create);
/// Get a surface based on the texture configuration
Surface GetTextureSurface(const void* config);
Surface GetTextureSurface(const Tegra::Texture::FullTextureInfo& config);
/// Get the color and depth surfaces based on the framebuffer configuration
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,

View File

@@ -194,7 +194,7 @@ void OpenGLState::Apply() const {
// Textures
for (unsigned i = 0; i < ARRAY_SIZE(texture_units); ++i) {
if (texture_units[i].texture_2d != cur_state.texture_units[i].texture_2d) {
glActiveTexture(TextureUnits::PicaTexture(i).Enum());
glActiveTexture(TextureUnits::MaxwellTexture(i).Enum());
glBindTexture(GL_TEXTURE_2D, texture_units[i].texture_2d);
}
if (texture_units[i].sampler != cur_state.texture_units[i].sampler) {

View File

@@ -16,7 +16,7 @@ struct TextureUnit {
}
};
constexpr TextureUnit PicaTexture(int unit) {
constexpr TextureUnit MaxwellTexture(int unit) {
return TextureUnit{unit};
}

View File

@@ -47,4 +47,27 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
return {};
}
inline GLenum TextureFilterMode(Tegra::Texture::TextureFilter filter_mode) {
switch (filter_mode) {
case Tegra::Texture::TextureFilter::Linear:
return GL_LINEAR;
case Tegra::Texture::TextureFilter::Nearest:
return GL_NEAREST;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture filter mode=%u",
static_cast<u32>(filter_mode));
UNREACHABLE();
return {};
}
inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
switch (wrap_mode) {
case Tegra::Texture::WrapMode::ClampToEdge:
return GL_CLAMP_TO_EDGE;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture wrap mode=%u", static_cast<u32>(wrap_mode));
UNREACHABLE();
return {};
}
} // namespace MaxwellToGL

View File

@@ -37,6 +37,16 @@ enum class TICHeaderVersion : u32 {
BlockLinearColorKey = 4,
};
enum class ComponentType : u32 {
SNORM = 1,
UNORM = 2,
SINT = 3,
UINT = 4,
SNORM_FORCE_FP16 = 5,
UNORM_FORCE_FP16 = 6,
FLOAT = 7
};
union TextureHandle {
u32 raw;
BitField<0, 20, u32> tic_id;
@@ -48,10 +58,10 @@ struct TICEntry {
union {
u32 raw;
BitField<0, 7, TextureFormat> format;
BitField<7, 3, u32> r_type;
BitField<10, 3, u32> g_type;
BitField<13, 3, u32> b_type;
BitField<16, 3, u32> a_type;
BitField<7, 3, ComponentType> r_type;
BitField<10, 3, ComponentType> g_type;
BitField<13, 3, ComponentType> b_type;
BitField<16, 3, ComponentType> a_type;
};
u32 address_low;
union {
@@ -77,6 +87,11 @@ struct TICEntry {
u32 Height() const {
return height_minus_1 + 1;
}
bool IsTiled() const {
return header_version == TICHeaderVersion::BlockLinear ||
header_version == TICHeaderVersion::BlockLinearColorKey;
}
};
static_assert(sizeof(TICEntry) == 0x20, "TICEntry has wrong size");

View File

@@ -371,9 +371,9 @@ bool GMainWindow::LoadROM(const QString& filename) {
"yuzu. A real Switch is required.<br/><br/>"
"For more information on dumping and decrypting games, please see the following "
"wiki pages: <ul>"
"<li><a href='https://citra-emu.org/wiki/dumping-game-cartridges/'>Dumping Game "
"<li><a href='https://yuzu-emu.org/wiki/dumping-game-cartridges/'>Dumping Game "
"Cartridges</a></li>"
"<li><a href='https://citra-emu.org/wiki/dumping-installed-titles/'>Dumping "
"<li><a href='https://yuzu-emu.org/wiki/dumping-installed-titles/'>Dumping "
"Installed Titles</a></li>"
"</ul>"));
break;
@@ -408,7 +408,7 @@ bool GMainWindow::LoadROM(const QString& filename) {
}
void GMainWindow::BootGame(const QString& filename) {
LOG_INFO(Frontend, "yuzu starting...");
NGLOG_INFO(Frontend, "yuzu starting...");
StoreRecentFile(filename); // Put the filename on top of the list
if (!LoadROM(filename))
@@ -695,18 +695,18 @@ void GMainWindow::UpdateStatusBar() {
void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string details) {
QMessageBox::StandardButton answer;
QString status_message;
const QString common_message =
tr("The game you are trying to load requires additional files from your 3DS to be dumped "
"before playing.<br/><br/>For more information on dumping these files, please see the "
"following wiki page: <a "
"href='https://citra-emu.org/wiki/"
"dumping-system-archives-and-the-shared-fonts-from-a-3ds-console/'>Dumping System "
"Archives and the Shared Fonts from a 3DS Console</a>.<br/><br/>Would you like to quit "
"back to the game list? Continuing emulation may result in crashes, corrupted save "
"data, or other bugs.");
const QString common_message = tr(
"The game you are trying to load requires additional files from your Switch to be dumped "
"before playing.<br/><br/>For more information on dumping these files, please see the "
"following wiki page: <a "
"href='https://yuzu-emu.org/wiki/"
"dumping-system-archives-and-the-shared-fonts-from-a-switch-console/'>Dumping System "
"Archives and the Shared Fonts from a Switch Console</a>.<br/><br/>Would you like to quit "
"back to the game list? Continuing emulation may result in crashes, corrupted save "
"data, or other bugs.");
switch (result) {
case Core::System::ResultStatus::ErrorSystemFiles: {
QString message = "Citra was unable to locate a 3DS system archive";
QString message = "yuzu was unable to locate a Switch system archive";
if (!details.empty()) {
message.append(tr(": %1. ").arg(details.c_str()));
} else {
@@ -721,7 +721,7 @@ void GMainWindow::OnCoreError(Core::System::ResultStatus result, std::string det
}
case Core::System::ResultStatus::ErrorSharedFont: {
QString message = tr("Citra was unable to locate the 3DS shared fonts. ");
QString message = tr("yuzu was unable to locate the Switch shared fonts. ");
message.append(common_message);
answer = QMessageBox::question(this, tr("Shared Fonts Not Found"), message,
QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

View File

@@ -177,12 +177,12 @@ gdbstub_port=24689
# 0: No, 1 (default): Yes
enable_telemetry =
# Endpoint URL for submitting telemetry data
telemetry_endpoint_url = https://services.citra-emu.org/api/telemetry
telemetry_endpoint_url =
# Endpoint URL to verify the username and token
verify_endpoint_url = https://services.citra-emu.org/api/profile
# Username and token for Citra Web Service
verify_endpoint_url =
# Username and token for yuzu Web Service
# See https://services.citra-emu.org/ for more info
citra_username =
citra_token =
yuzu_username =
yuzu_token =
)";
}

View File

@@ -147,7 +147,7 @@ int main(int argc, char** argv) {
LOG_CRITICAL(Frontend, "The game that you are trying to load must be decrypted before "
"being used with yuzu. \n\n For more information on dumping and "
"decrypting games, please refer to: "
"https://citra-emu.org/wiki/dumping-game-cartridges/");
"https://yuzu-emu.org/wiki/dumping-game-cartridges/");
return -1;
case Core::System::ResultStatus::ErrorLoader_ErrorInvalidFormat:
LOG_CRITICAL(Frontend, "Error while loading ROM: The ROM format is not supported.");