Compare commits

..

6 Commits

Author SHA1 Message Date
Zach Hilman
110d578470 ips_layer: Fix inaccuracies with comments and flags
Specifically bugs/crashes that arise when putting them in positions that are legal but not typical, such as midline, between patch data, or between patch records.
2018-10-04 12:23:27 -04:00
Zach Hilman
70bd2bb1d3 ips_layer: Deduplicate resource usage 2018-10-04 11:34:36 -04:00
Zach Hilman
9669cdb710 ips_layer: Add support for escape sequences and midline comments
More accurately follows IPSwitch specification.
2018-10-04 11:34:30 -04:00
Zach Hilman
8886f2e55e patch_manager: Add support for IPSwitch format patches 2018-10-04 11:34:06 -04:00
Zach Hilman
306739c2c4 ips_layer: Add IPSwitchCompiler to process IPSwitch format 2018-10-04 11:32:10 -04:00
Zach Hilman
f62227aa95 hex_util: Add HexVectorToString and HexStringToVector
Converts between bytes and strings when the size is not known at compile time.
2018-10-04 11:32:04 -04:00
130 changed files with 920 additions and 23552 deletions

6
.gitmodules vendored
View File

@@ -34,9 +34,3 @@
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git
[submodule "libressl"]
path = externals/libressl
url = https://github.com/citra-emu/ext-libressl-portable.git
[submodule "discord-rpc"]
path = externals/discord-rpc
url = https://github.com/discordapp/discord-rpc.git

View File

@@ -10,7 +10,3 @@ TRAVIS_JOB_ID
TRAVIS_JOB_NUMBER
TRAVIS_REPO_SLUG
TRAVIS_TAG
# yuzu specific flags
ENABLE_COMPATIBILITY_REPORTING
USE_DISCORD_PRESENCE

View File

@@ -1,4 +1,4 @@
#!/bin/bash -ex
mkdir -p "$HOME/.ccache"
docker run -e ENABLE_COMPATIBILITY_REPORTING --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache ubuntu:18.04 /bin/bash /yuzu/.travis/linux/docker.sh

View File

@@ -6,7 +6,7 @@ apt-get install --no-install-recommends -y build-essential git libqt5opengl5-dev
cd /yuzu
mkdir build && cd build
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON -G Ninja
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -G Ninja
ninja
ccache -s

View File

@@ -9,7 +9,7 @@ export PATH="/usr/local/opt/ccache/libexec:$PATH"
mkdir build && cd build
cmake --version
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON
make -j4
ccache -s

View File

@@ -15,14 +15,10 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON
option(ENABLE_QT "Enable the Qt frontend" ON)
CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_QT;MSVC" OFF)
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
option(ENABLE_CUBEB "Enables the cubeb audio backend" ON)
option(USE_DISCORD_PRESENCE "Enables Discord Rich Presence" OFF)
if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit)
message(STATUS "Copying pre-commit hook")
file(COPY hooks/pre-commit

View File

@@ -22,7 +22,7 @@ If clang format is found, then cmake will add a custom build target that can be
* Don't ever introduce new external dependencies into Core
* Don't use any platform specific code in Core
* Use namespaces often
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`.
* Avoid the use of C-style casts and instead prefer C++-style `static_cast` and `reinterpret_cast`. Try to avoid using `dynamic_cast`. Never use `const_cast`. The only exception to this rule is for casting between two numeric types, where C-style casts are encouraged for brevity and readability.
### Naming Rules
* Functions: `PascalCase`

View File

@@ -39,12 +39,11 @@ before_build:
- mkdir %BUILD_TYPE%_build
- cd %BUILD_TYPE%_build
- ps: |
$COMPAT = if ($env:ENABLE_COMPATIBILITY_REPORTING -eq $null) {0} else {$env:ENABLE_COMPATIBILITY_REPORTING}
if ($env:BUILD_TYPE -eq 'msvc') {
# redirect stderr and change the exit code to prevent powershell from cancelling the build if cmake prints a warning
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1 && exit 0'
cmd /C 'cmake -G "Visual Studio 15 2017 Win64" -DYUZU_USE_BUNDLED_QT=1 -DYUZU_USE_BUNDLED_SDL2=1 -DYUZU_USE_BUNDLED_UNICORN=1 -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1 && exit 0'
} else {
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${COMPAT} -DUSE_DISCORD_PRESENCE=ON .. 2>&1"
C:\msys64\usr\bin\bash.exe -lc "cmake -G 'MSYS Makefiles' -DYUZU_BUILD_UNICORN=1 -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON .. 2>&1"
}
- cd ..

View File

@@ -70,28 +70,3 @@ if(ENABLE_CUBEB)
set(BUILD_TESTS OFF CACHE BOOL "")
add_subdirectory(cubeb EXCLUDE_FROM_ALL)
endif()
# DiscordRPC
if (USE_DISCORD_PRESENCE)
add_subdirectory(discord-rpc EXCLUDE_FROM_ALL)
target_include_directories(discord-rpc INTERFACE ./discord-rpc/include)
endif()
if (ENABLE_WEB_SERVICE)
# LibreSSL
set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
add_subdirectory(libressl EXCLUDE_FROM_ALL)
target_include_directories(ssl INTERFACE ./libressl/include)
target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
# lurlparser
add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
# httplib
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ./httplib)
# JSON
add_library(json-headers INTERFACE)
target_include_directories(json-headers INTERFACE ./json)
endif()

Submodule externals/discord-rpc deleted from e32d001809

2
externals/fmt vendored

View File

@@ -1,15 +0,0 @@
From https://github.com/yhirose/cpp-httplib/commit/d9479bc0b12e8a1e8bce2d34da4feeef488581f3
MIT License
===
cpp-httplib
A C++11 header-only HTTP library.
It's extremely easy to setup. Just include httplib.h file in your code!
Inspired by Sinatra and express.
© 2017 Yuji Hirose

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
JSON for Modern C++
===================
v3.1.2
This is a mirror providing the single required header file.
The original repository can be found at:
https://github.com/nlohmann/json/commit/d2dd27dc3b8472dbaa7d66f83619b3ebcd9185fe

17300
externals/json/json.hpp vendored

File diff suppressed because it is too large Load Diff

1
externals/libressl vendored

Submodule externals/libressl deleted from 7d01cb01cb

View File

@@ -1,8 +0,0 @@
add_library(lurlparser
LUrlParser.cpp
LUrlParser.h
)
create_target_directory_groups(lurlparser)
target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -1,265 +0,0 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "LUrlParser.h"
#include <algorithm>
#include <cstring>
#include <stdlib.h>
// check if the scheme name is valid
static bool IsSchemeValid( const std::string& SchemeName )
{
for ( auto c : SchemeName )
{
if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
}
return true;
}
bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
{
if ( !IsValid() ) { return false; }
int Port = atoi( m_Port.c_str() );
if ( Port <= 0 || Port > 65535 ) { return false; }
if ( OutPort ) { *OutPort = Port; }
return true;
}
// based on RFC 1738 and RFC 3986
LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
{
LUrlParser::clParseURL Result;
const char* CurrentString = URL.c_str();
/*
* <scheme>:<scheme-specific-part>
* <scheme> := [a-z\+\-\.]+
* For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
*/
// try to read scheme
{
const char* LocalString = strchr( CurrentString, ':' );
if ( !LocalString )
{
return clParseURL( LUrlParserError_NoUrlCharacter );
}
// save the scheme name
Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
if ( !IsSchemeValid( Result.m_Scheme ) )
{
return clParseURL( LUrlParserError_InvalidSchemeName );
}
// scheme should be lowercase
std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
// skip ':'
CurrentString = LocalString+1;
}
/*
* //<user>:<password>@<host>:<port>/<url-path>
* any ":", "@" and "/" must be normalized
*/
// skip "//"
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
// check if the user name and password are specified
bool bHasUserName = false;
const char* LocalString = CurrentString;
while ( *LocalString )
{
if ( *LocalString == '@' )
{
// user name and password are specified
bHasUserName = true;
break;
}
else if ( *LocalString == '/' )
{
// end of <host>:<port> specification
bHasUserName = false;
break;
}
LocalString++;
}
// user name and password
LocalString = CurrentString;
if ( bHasUserName )
{
// read user name
while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
// proceed with the current pointer
CurrentString = LocalString;
if ( *CurrentString == ':' )
{
// skip ':'
CurrentString++;
// read password
LocalString = CurrentString;
while ( *LocalString && *LocalString != '@' ) LocalString++;
Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// skip '@'
if ( *CurrentString != '@' )
{
return clParseURL( LUrlParserError_NoAtSign );
}
CurrentString++;
}
bool bHasBracket = ( *CurrentString == '[' );
// go ahead, read the host name
LocalString = CurrentString;
while ( *LocalString )
{
if ( bHasBracket && *LocalString == ']' )
{
// end of IPv6 address
LocalString++;
break;
}
else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
{
// port number is specified
break;
}
LocalString++;
}
Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
// is port number specified?
if ( *CurrentString == ':' )
{
CurrentString++;
// read port number
LocalString = CurrentString;
while ( *LocalString && *LocalString != '/' ) LocalString++;
Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// end of string
if ( !*CurrentString )
{
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}
// skip '/'
if ( *CurrentString != '/' )
{
return clParseURL( LUrlParserError_NoSlash );
}
CurrentString++;
// parse the path
LocalString = CurrentString;
while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
// check for query
if ( *CurrentString == '?' )
{
// skip '?'
CurrentString++;
// read query
LocalString = CurrentString;
while ( *LocalString && *LocalString != '#' ) LocalString++;
Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
// check for fragment
if ( *CurrentString == '#' )
{
// skip '#'
CurrentString++;
// read fragment
LocalString = CurrentString;
while ( *LocalString ) LocalString++;
Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
CurrentString = LocalString;
}
Result.m_ErrorCode = LUrlParserError_Ok;
return Result;
}

View File

@@ -1,78 +0,0 @@
/*
* Lightweight URL & URI parser (RFC 1738, RFC 3986)
* https://github.com/corporateshark/LUrlParser
*
* The MIT License (MIT)
*
* Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#pragma once
#include <string>
namespace LUrlParser
{
enum LUrlParserError
{
LUrlParserError_Ok = 0,
LUrlParserError_Uninitialized = 1,
LUrlParserError_NoUrlCharacter = 2,
LUrlParserError_InvalidSchemeName = 3,
LUrlParserError_NoDoubleSlash = 4,
LUrlParserError_NoAtSign = 5,
LUrlParserError_UnexpectedEndOfLine = 6,
LUrlParserError_NoSlash = 7,
};
class clParseURL
{
public:
LUrlParserError m_ErrorCode;
std::string m_Scheme;
std::string m_Host;
std::string m_Port;
std::string m_Path;
std::string m_Query;
std::string m_Fragment;
std::string m_UserName;
std::string m_Password;
clParseURL()
: m_ErrorCode( LUrlParserError_Uninitialized )
{}
/// return 'true' if the parsing was successful
bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
/// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
bool GetPort( int* OutPort ) const;
/// parse the URL
static clParseURL ParseURL( const std::string& URL );
private:
explicit clParseURL( LUrlParserError ErrorCode )
: m_ErrorCode( ErrorCode )
{}
};
} // namespace LUrlParser

View File

@@ -1,19 +0,0 @@
From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
MIT License
===
Lightweight URL & URI parser (RFC 1738, RFC 3986)
(C) Sergey Kosarevsky, 2015
@corporateshark sk@linderdaum.com
http://www.linderdaum.com
http://blog.linderdaum.com
=============================
A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.

0
git
View File

View File

@@ -13,6 +13,3 @@ endif()
if (ENABLE_QT)
add_subdirectory(yuzu)
endif()
if (ENABLE_WEB_SERVICE)
add_subdirectory(web_service)
endif()

View File

@@ -29,7 +29,7 @@ if ($ENV{CI})
if (BUILD_VERSION)
# This leaves a trailing space on the last word, but we actually want that
# because of how it's styled in the title bar.
set(BUILD_FULLNAME "${REPO_NAME} ${BUILD_VERSION} ")
set(BUILD_FULLNAME "${REPO_NAME} #${BUILD_VERSION} ")
else()
set(BUILD_FULLNAME "")
endif()
@@ -41,8 +41,6 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/scm_rev.cpp.in" "${CMAKE_CURRENT_SOU
add_library(common STATIC
alignment.h
assert.h
detached_tasks.cpp
detached_tasks.h
bit_field.h
bit_set.h
cityhash.cpp
@@ -89,7 +87,6 @@ add_library(common STATIC
timer.cpp
timer.h
vector_math.h
web_result.h
)
if(ARCHITECTURE_x86_64)

View File

@@ -1,41 +0,0 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <thread>
#include "common/assert.h"
#include "common/detached_tasks.h"
namespace Common {
DetachedTasks* DetachedTasks::instance = nullptr;
DetachedTasks::DetachedTasks() {
ASSERT(instance == nullptr);
instance = this;
}
void DetachedTasks::WaitForAllTasks() {
std::unique_lock<std::mutex> lock(mutex);
cv.wait(lock, [this]() { return count == 0; });
}
DetachedTasks::~DetachedTasks() {
std::unique_lock<std::mutex> lock(mutex);
ASSERT(count == 0);
instance = nullptr;
}
void DetachedTasks::AddTask(std::function<void()> task) {
std::unique_lock<std::mutex> lock(instance->mutex);
++instance->count;
std::thread([task{std::move(task)}]() {
task();
std::unique_lock<std::mutex> lock(instance->mutex);
--instance->count;
std::notify_all_at_thread_exit(instance->cv, std::move(lock));
})
.detach();
}
} // namespace Common

View File

@@ -1,40 +0,0 @@
// Copyright 2018 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <condition_variable>
#include <functional>
namespace Common {
/**
* A background manager which ensures that all detached task is finished before program exits.
*
* Some tasks, telemetry submission for example, prefer executing asynchronously and don't care
* about the result. These tasks are suitable for std::thread::detach(). However, this is unsafe if
* the task is launched just before the program exits (which is a common case for telemetry), so we
* need to block on these tasks on program exit.
*
* To make detached task safe, a single DetachedTasks object should be placed in the main(), and
* call WaitForAllTasks() after all program execution but before global/static variable destruction.
* Any potentially unsafe detached task should be executed via DetachedTasks::AddTask.
*/
class DetachedTasks {
public:
DetachedTasks();
~DetachedTasks();
void WaitForAllTasks();
static void AddTask(std::function<void()> task);
private:
static DetachedTasks* instance;
std::condition_variable cv;
std::mutex mutex;
int count = 0;
};
} // namespace Common

View File

@@ -18,6 +18,25 @@ u8 ToHexNibble(char c1) {
return 0;
}
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
std::vector<u8> out(str.size() / 2);
if (little_endian) {
for (std::size_t i = str.size() - 2; i <= str.size(); i -= 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
} else {
for (std::size_t i = 0; i < str.size(); i += 2)
out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
}
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper) {
std::string out;
for (u8 c : vector)
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
return out;
}
std::array<u8, 16> operator""_array16(const char* str, std::size_t len) {
if (len != 32) {
LOG_ERROR(Common,

View File

@@ -7,6 +7,7 @@
#include <array>
#include <cstddef>
#include <string>
#include <vector>
#include <fmt/format.h>
#include "common/common_types.h"
@@ -14,6 +15,8 @@ namespace Common {
u8 ToHexNibble(char c1);
std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
template <std::size_t Size, bool le = false>
std::array<u8, Size> HexStringToArray(std::string_view str) {
std::array<u8, Size> out{};
@@ -27,6 +30,8 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
return out;
}
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
template <std::size_t Size>
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
std::string out;

View File

@@ -31,7 +31,7 @@ std::string FormatLogMessage(const Entry& entry) {
}
void PrintMessage(const Entry& entry) {
const auto str = FormatLogMessage(entry).append(1, '\n');
auto str = FormatLogMessage(entry) + '\n';
fputs(str.c_str(), stderr);
}

View File

@@ -20,15 +20,7 @@ constexpr char KEY_VALUE_SEPARATOR_ESCAPE[] = "$0";
constexpr char PARAM_SEPARATOR_ESCAPE[] = "$1";
constexpr char ESCAPE_CHARACTER_ESCAPE[] = "$2";
/// A placeholder for empty param packages to avoid empty strings
/// (they may be recognized as "not set" by some frontend libraries like qt)
constexpr char EMPTY_PLACEHOLDER[] = "[empty]";
ParamPackage::ParamPackage(const std::string& serialized) {
if (serialized == EMPTY_PLACEHOLDER) {
return;
}
std::vector<std::string> pairs;
Common::SplitString(serialized, PARAM_SEPARATOR, pairs);
@@ -54,7 +46,7 @@ ParamPackage::ParamPackage(std::initializer_list<DataType::value_type> list) : d
std::string ParamPackage::Serialize() const {
if (data.empty())
return EMPTY_PLACEHOLDER;
return "";
std::string result;
@@ -128,12 +120,4 @@ bool ParamPackage::Has(const std::string& key) const {
return data.find(key) != data.end();
}
void ParamPackage::Erase(const std::string& key) {
data.erase(key);
}
void ParamPackage::Clear() {
data.clear();
}
} // namespace Common

View File

@@ -32,8 +32,6 @@ public:
void Set(const std::string& key, int value);
void Set(const std::string& key, float value);
bool Has(const std::string& key) const;
void Erase(const std::string& key);
void Clear();
private:
DataType data;

View File

@@ -1,24 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <string>
namespace Common {
struct WebResult {
enum class Code : u32 {
Success,
InvalidURL,
CredentialsMissing,
LibError,
HttpError,
WrongContent,
NoWebservice,
};
Code result_code;
std::string result_string;
std::string returned_data;
};
} // namespace Common

View File

@@ -396,10 +396,6 @@ create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt lz4_static mbedtls opus unicorn open_source_archives)
if (ENABLE_WEB_SERVICE)
add_definitions(-DENABLE_WEB_SERVICE)
target_link_libraries(core PUBLIC json-headers web_service)
endif()
if (ARCHITECTURE_x86_64)
target_sources(core PRIVATE

View File

@@ -86,7 +86,7 @@ public:
parent.jit->HaltExecution();
parent.SetPC(pc);
Kernel::Thread* thread = Kernel::GetCurrentThread();
parent.SaveContext(thread->GetContext());
parent.SaveContext(thread->context);
GDBStub::Break();
GDBStub::SendTrap(thread, 5);
return;

View File

@@ -195,7 +195,7 @@ void ARM_Unicorn::ExecuteInstructions(int num_instructions) {
uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
}
Kernel::Thread* thread = Kernel::GetCurrentThread();
SaveContext(thread->GetContext());
SaveContext(thread->context);
if (last_bkpt_hit || GDBStub::GetCpuStepFlag()) {
last_bkpt_hit = false;
GDBStub::Break();

View File

@@ -2,7 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <sstream>
#include "common/assert.h"
#include "common/hex_util.h"
#include "common/swap.h"
#include "core/file_sys/ips_layer.h"
#include "core/file_sys/vfs_vector.h"
@@ -15,6 +17,12 @@ enum class IPSFileType {
Error,
};
constexpr std::array<std::pair<const char*, const char*>, 11> ESCAPE_CHARACTER_MAP{
std::pair{"\\a", "\a"}, {"\\b", "\b"}, {"\\f", "\f"}, {"\\n", "\n"},
{"\\r", "\r"}, {"\\t", "\t"}, {"\\v", "\v"}, {"\\\\", "\\"},
{"\\\'", "\'"}, {"\\\"", "\""}, {"\\\?", "\?"},
};
static IPSFileType IdentifyMagic(const std::vector<u8>& magic) {
if (magic.size() != 5)
return IPSFileType::Error;
@@ -85,4 +93,205 @@ VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips) {
return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory());
}
IPSwitchCompiler::IPSwitchCompiler(VirtualFile patch_text_) : patch_text(std::move(patch_text_)) {
Parse();
}
IPSwitchCompiler::~IPSwitchCompiler() = default;
std::array<u8, 32> IPSwitchCompiler::GetBuildID() const {
return nso_build_id;
}
bool IPSwitchCompiler::IsValid() const {
return valid;
}
static bool StartsWith(std::string_view base, std::string_view check) {
return base.size() >= check.size() && base.substr(0, check.size()) == check;
}
static std::string EscapeStringSequences(std::string in) {
for (const auto& seq : ESCAPE_CHARACTER_MAP) {
for (auto index = in.find(seq.first); index != std::string::npos;
index = in.find(seq.first, index)) {
in.replace(index, std::strlen(seq.first), seq.second);
index += std::strlen(seq.second);
}
}
return in;
}
void IPSwitchCompiler::ParseFlag(const std::string& line) {
if (StartsWith(line, "@flag offset_shift ")) {
// Offset Shift Flag
offset_shift = std::stoll(line.substr(19), nullptr, 0);
} else if (StartsWith(line, "@little-endian")) {
// Set values to read as little endian
is_little_endian = true;
} else if (StartsWith(line, "@big-endian")) {
// Set values to read as big endian
is_little_endian = false;
} else if (StartsWith(line, "@flag print_values")) {
// Force printing of applied values
print_values = true;
}
}
void IPSwitchCompiler::Parse() {
const auto bytes = patch_text->ReadAllBytes();
std::stringstream s;
s.write(reinterpret_cast<const char*>(bytes.data()), bytes.size());
std::vector<std::string> lines;
std::string stream_line;
while (std::getline(s, stream_line)) {
// Remove a trailing \r
if (!stream_line.empty() && stream_line.back() == '\r')
stream_line.pop_back();
lines.push_back(std::move(stream_line));
}
for (std::size_t i = 0; i < lines.size(); ++i) {
auto line = lines[i];
// Remove midline comments
std::size_t comment_index = std::string::npos;
bool within_string = false;
for (std::size_t k = 0; k < line.size(); ++k) {
if (line[k] == '\"' && (k > 0 && line[k - 1] != '\\')) {
within_string = !within_string;
} else if (line[k] == '\\' && (k < line.size() - 1 && line[k + 1] == '\\')) {
comment_index = k;
break;
}
}
if (!StartsWith(line, "//") && comment_index != std::string::npos) {
last_comment = line.substr(comment_index + 2);
line = line.substr(0, comment_index);
}
if (StartsWith(line, "@stop")) {
// Force stop
break;
} else if (StartsWith(line, "@nsobid-")) {
// NSO Build ID Specifier
auto raw_build_id = line.substr(8);
if (raw_build_id.size() != 0x40)
raw_build_id.resize(0x40, '0');
nso_build_id = Common::HexStringToArray<0x20>(raw_build_id);
} else if (StartsWith(line, "#")) {
// Mandatory Comment
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Forced output comment: {}",
patch_text->GetName(), line.substr(1));
} else if (StartsWith(line, "//")) {
// Normal Comment
last_comment = line.substr(2);
if (last_comment.find_first_not_of(' ') == std::string::npos)
continue;
if (last_comment.find_first_not_of(' ') != 0)
last_comment = last_comment.substr(last_comment.find_first_not_of(' '));
} else if (StartsWith(line, "@enabled") || StartsWith(line, "@disabled")) {
// Start of patch
const auto enabled = StartsWith(line, "@enabled");
if (i == 0)
return;
LOG_INFO(Loader, "[IPSwitchCompiler ('{}')] Parsing patch '{}' ({})",
patch_text->GetName(), last_comment, line.substr(1));
IPSwitchPatch patch{last_comment, enabled, {}};
// Read rest of patch
while (true) {
if (i + 1 >= lines.size())
break;
const auto patch_line = lines[++i];
// Start of new patch
if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
--i;
break;
}
// Check for a flag
if (StartsWith(patch_line, "@")) {
ParseFlag(patch_line);
continue;
}
// 11 - 8 hex digit offset + space + minimum two digit overwrite val
if (patch_line.length() < 11)
break;
auto offset = std::stoul(patch_line.substr(0, 8), nullptr, 16);
offset += offset_shift;
std::vector<u8> replace;
// 9 - first char of replacement val
if (patch_line[9] == '\"') {
// string replacement
auto end_index = patch_line.find('\"', 10);
if (end_index == std::string::npos || end_index < 10)
return;
while (patch_line[end_index - 1] == '\\') {
end_index = patch_line.find('\"', end_index + 1);
if (end_index == std::string::npos || end_index < 10)
return;
}
auto value = patch_line.substr(10, end_index - 10);
value = EscapeStringSequences(value);
replace.reserve(value.size());
std::copy(value.begin(), value.end(), std::back_inserter(replace));
} else {
// hex replacement
const auto value = patch_line.substr(9);
replace.reserve(value.size() / 2);
replace = Common::HexStringToVector(value, is_little_endian);
}
if (print_values) {
LOG_INFO(Loader,
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
"with byte string '{}'",
patch_text->GetName(), offset, Common::HexVectorToString(replace));
}
patch.records.insert_or_assign(offset, std::move(replace));
}
patches.push_back(std::move(patch));
} else if (StartsWith(line, "@")) {
ParseFlag(line);
}
}
valid = true;
}
VirtualFile IPSwitchCompiler::Apply(const VirtualFile& in) const {
if (in == nullptr || !valid)
return nullptr;
auto in_data = in->ReadAllBytes();
for (const auto& patch : patches) {
if (!patch.enabled)
continue;
for (const auto& record : patch.records) {
if (record.first >= in_data.size())
continue;
auto replace_size = record.second.size();
if (record.first + replace_size > in_data.size())
replace_size = in_data.size() - record.first;
for (std::size_t i = 0; i < replace_size; ++i)
in_data[i + record.first] = record.second[i];
}
}
return std::make_shared<VectorVfsFile>(in_data, in->GetName(), in->GetContainingDirectory());
}
} // namespace FileSys

View File

@@ -12,4 +12,34 @@ namespace FileSys {
VirtualFile PatchIPS(const VirtualFile& in, const VirtualFile& ips);
class IPSwitchCompiler {
public:
explicit IPSwitchCompiler(VirtualFile patch_text);
~IPSwitchCompiler();
std::array<u8, 0x20> GetBuildID() const;
bool IsValid() const;
VirtualFile Apply(const VirtualFile& in) const;
private:
void ParseFlag(const std::string& flag);
void Parse();
bool valid = false;
struct IPSwitchPatch {
std::string name;
bool enabled;
std::map<u32, std::vector<u8>> records;
};
VirtualFile patch_text;
std::vector<IPSwitchPatch> patches;
std::array<u8, 0x20> nso_build_id{};
bool is_little_endian = false;
s64 offset_shift = 0;
bool print_values = false;
std::string last_comment = "";
};
} // namespace FileSys

View File

@@ -73,27 +73,38 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
return exefs;
}
static std::vector<VirtualFile> CollectIPSPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) {
std::vector<VirtualFile> ips;
ips.reserve(patch_dirs.size());
static std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
const std::string& build_id) {
std::vector<VirtualFile> out;
out.reserve(patch_dirs.size());
for (const auto& subdir : patch_dirs) {
auto exefs_dir = subdir->GetSubdirectory("exefs");
if (exefs_dir != nullptr) {
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() != "ips")
continue;
auto name = file->GetName();
const auto p1 = name.substr(0, name.find('.'));
const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
if (file->GetExtension() == "ips") {
auto name = file->GetName();
const auto p1 = name.substr(0, name.find('.'));
const auto this_build_id = p1.substr(0, p1.find_last_not_of('0') + 1);
if (build_id == this_build_id)
ips.push_back(file);
if (build_id == this_build_id)
out.push_back(file);
} else if (file->GetExtension() == "pchtxt") {
IPSwitchCompiler compiler{file};
if (!compiler.IsValid())
continue;
auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
this_build_id =
this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
if (build_id == this_build_id)
out.push_back(file);
}
}
}
}
return ips;
return out;
}
std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
@@ -115,15 +126,24 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso) const {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
const auto ips = CollectIPSPatches(patch_dirs, build_id);
const auto patches = CollectPatches(patch_dirs, build_id);
auto out = nso;
for (const auto& ips_file : ips) {
LOG_INFO(Loader, " - Appling IPS patch from mod \"{}\"",
ips_file->GetContainingDirectory()->GetParentDirectory()->GetName());
const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), ips_file);
if (patched != nullptr)
out = patched->ReadAllBytes();
for (const auto& patch_file : patches) {
if (patch_file->GetExtension() == "ips") {
LOG_INFO(Loader, " - Applying IPS patch from mod \"{}\"",
patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
const auto patched = PatchIPS(std::make_shared<VectorVfsFile>(out), patch_file);
if (patched != nullptr)
out = patched->ReadAllBytes();
} else if (patch_file->GetExtension() == "pchtxt") {
LOG_INFO(Loader, " - Applying IPSwitch patch from mod \"{}\"",
patch_file->GetContainingDirectory()->GetParentDirectory()->GetName());
const IPSwitchCompiler compiler{patch_file};
const auto patched = compiler.Apply(std::make_shared<VectorVfsFile>(out));
if (patched != nullptr)
out = patched->ReadAllBytes();
}
}
if (out.size() < 0x100)
@@ -143,7 +163,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
std::sort(patch_dirs.begin(), patch_dirs.end(),
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
return !CollectIPSPatches(patch_dirs, build_id).empty();
return !CollectPatches(patch_dirs, build_id).empty();
}
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
@@ -184,8 +204,8 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
romfs = std::move(packed);
}
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
VirtualFile update_raw) const {
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
ContentRecordType type) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
static_cast<u8>(type));
@@ -205,13 +225,6 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
romfs = new_nca->GetRomFS();
}
} else if (update_raw != nullptr) {
const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update (PACKED) applied successfully");
romfs = new_nca->GetRomFS();
}
}
// LayeredFS
@@ -231,8 +244,7 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
}
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames() const {
std::map<std::string, std::string, std::less<>> out;
const auto installed = Service::FileSystem::GetUnionContents();
@@ -253,8 +265,6 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
"Update",
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
}
} else if (update_raw != nullptr) {
out.insert_or_assign("Update", "PACKED");
}
}
@@ -263,8 +273,24 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
for (const auto& mod : mod_dir->GetSubdirectories()) {
std::string types;
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("exefs")))
AppendCommaIfNotEmpty(types, "IPS");
const auto exefs_dir = mod->GetSubdirectory("exefs");
if (IsDirValidAndNonEmpty(exefs_dir)) {
bool ips = false;
bool ipswitch = false;
for (const auto& file : exefs_dir->GetFiles()) {
if (file->GetExtension() == "ips")
ips = true;
else if (file->GetExtension() == "pchtxt")
ipswitch = true;
}
if (ips)
AppendCommaIfNotEmpty(types, "IPS");
if (ipswitch)
AppendCommaIfNotEmpty(types, "IPSwitch");
}
if (IsDirValidAndNonEmpty(mod->GetSubdirectory("romfs")))
AppendCommaIfNotEmpty(types, "LayeredFS");

View File

@@ -36,6 +36,7 @@ public:
// Currently tracked NSO patches:
// - IPS
// - IPSwitch
std::vector<u8> PatchNSO(const std::vector<u8>& nso) const;
// Checks to see if PatchNSO() will have any effect given the NSO's build ID.
@@ -46,13 +47,11 @@ public:
// - Game Updates
// - LayeredFS
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program,
VirtualFile update_raw = nullptr) const;
ContentRecordType type = ContentRecordType::Program) const;
// Returns a vector of pairs between patch names and patch versions.
// i.e. Update 3.2.2 will return {"Update", "3.2.2"}
std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
VirtualFile update_raw = nullptr) const;
std::map<std::string, std::string, std::less<>> GetPatchVersionNames() const;
// Given title_id of the program, attempts to get the control data of the update and parse it,
// falling back to the base control data.

View File

@@ -30,17 +30,12 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
RomFSFactory::~RomFSFactory() = default;
void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
this->update_raw = std::move(update_raw);
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
if (!updatable)
return MakeResult<VirtualFile>(file);
const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID());
return MakeResult<VirtualFile>(
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset));
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {

View File

@@ -32,13 +32,11 @@ public:
explicit RomFSFactory(Loader::AppLoader& app_loader);
~RomFSFactory();
void SetPackedUpdate(VirtualFile update_raw);
ResultVal<VirtualFile> OpenCurrentProcess();
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
private:
VirtualFile file;
VirtualFile update_raw;
bool updatable;
u64 ivfc_offset;
};

View File

@@ -259,11 +259,8 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
auto next_nca = std::make_shared<NCA>(next_file);
if (next_nca->GetType() == NCAContentType::Program)
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
(next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
(cnmt.GetTitleID() & 0x800) != 0)) {
if (next_nca->GetStatus() == Loader::ResultStatus::Success)
ncas_title[rec.type] = std::move(next_nca);
}
}
break;

View File

@@ -209,7 +209,7 @@ static Kernel::Thread* FindThreadById(int id) {
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (auto& thread : threads) {
if (thread->GetThreadID() == static_cast<u32>(id)) {
if (thread->GetThreadId() == static_cast<u32>(id)) {
current_core = core;
return thread.get();
}
@@ -223,18 +223,16 @@ static u64 RegRead(std::size_t id, Kernel::Thread* thread = nullptr) {
return 0;
}
const auto& thread_context = thread->GetContext();
if (id < SP_REGISTER) {
return thread_context.cpu_registers[id];
return thread->context.cpu_registers[id];
} else if (id == SP_REGISTER) {
return thread_context.sp;
return thread->context.sp;
} else if (id == PC_REGISTER) {
return thread_context.pc;
return thread->context.pc;
} else if (id == PSTATE_REGISTER) {
return thread_context.pstate;
return thread->context.pstate;
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
return thread_context.vector_registers[id - UC_ARM64_REG_Q0][0];
return thread->context.vector_registers[id - UC_ARM64_REG_Q0][0];
} else {
return 0;
}
@@ -245,18 +243,16 @@ static void RegWrite(std::size_t id, u64 val, Kernel::Thread* thread = nullptr)
return;
}
auto& thread_context = thread->GetContext();
if (id < SP_REGISTER) {
thread_context.cpu_registers[id] = val;
thread->context.cpu_registers[id] = val;
} else if (id == SP_REGISTER) {
thread_context.sp = val;
thread->context.sp = val;
} else if (id == PC_REGISTER) {
thread_context.pc = val;
thread->context.pc = val;
} else if (id == PSTATE_REGISTER) {
thread_context.pstate = static_cast<u32>(val);
thread->context.pstate = static_cast<u32>(val);
} else if (id > PSTATE_REGISTER && id < FPCR_REGISTER) {
thread_context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
thread->context.vector_registers[id - (PSTATE_REGISTER + 1)][0] = val;
}
}
@@ -599,7 +595,7 @@ static void HandleQuery() {
for (u32 core = 0; core < Core::NUM_CPU_CORES; core++) {
const auto& threads = Core::System::GetInstance().Scheduler(core)->GetThreadList();
for (const auto& thread : threads) {
val += fmt::format("{:x}", thread->GetThreadID());
val += fmt::format("{:x}", thread->GetThreadId());
val += ",";
}
}
@@ -616,7 +612,7 @@ static void HandleQuery() {
for (const auto& thread : threads) {
buffer +=
fmt::format(R"*(<thread id="{:x}" core="{:d}" name="Thread {:x}"></thread>)*",
thread->GetThreadID(), core, thread->GetThreadID());
thread->GetThreadId(), core, thread->GetThreadId());
}
}
buffer += "</threads>";
@@ -697,7 +693,7 @@ static void SendSignal(Kernel::Thread* thread, u32 signal, bool full = true) {
}
if (thread) {
buffer += fmt::format(";thread:{:x};", thread->GetThreadID());
buffer += fmt::format(";thread:{:x};", thread->GetThreadId());
}
SendReply(buffer.c_str());
@@ -861,9 +857,7 @@ static void WriteRegister() {
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
SendReply("OK");
}
@@ -892,9 +886,7 @@ static void WriteRegisters() {
}
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
SendReply("OK");
}
@@ -968,9 +960,7 @@ static void Step() {
if (command_length > 1) {
RegWrite(PC_REGISTER, GdbHexToLong(command_buffer + 1), current_thread);
// Update Unicorn context skipping scheduler, no running threads at this point
Core::System::GetInstance()
.ArmInterface(current_core)
.LoadContext(current_thread->GetContext());
Core::System::GetInstance().ArmInterface(current_core).LoadContext(current_thread->context);
}
step_loop = true;
halt_loop = true;

View File

@@ -23,13 +23,13 @@ namespace AddressArbiter {
// Performs actual address waiting logic.
static ResultCode WaitForAddress(VAddr address, s64 timeout) {
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->SetArbiterWaitAddress(address);
current_thread->SetStatus(ThreadStatus::WaitArb);
current_thread->InvalidateWakeupCallback();
current_thread->arb_wait_address = address;
current_thread->status = ThreadStatus::WaitArb;
current_thread->wakeup_callback = nullptr;
current_thread->WakeAfterDelay(timeout);
Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
return RESULT_TIMEOUT;
}
@@ -39,10 +39,10 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr arb_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
const auto& thread_list = scheduler->GetThreadList();
auto& thread_list = scheduler->GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetArbiterWaitAddress() == arb_addr)
for (auto& thread : thread_list) {
if (thread->arb_wait_address == arb_addr)
waiting_threads.push_back(thread);
}
};
@@ -57,7 +57,7 @@ static std::vector<SharedPtr<Thread>> GetThreadsWaitingOnAddress(VAddr address)
// Sort them by priority, such that the highest priority ones come first.
std::sort(threads.begin(), threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->GetPriority() < rhs->GetPriority();
return lhs->current_priority < rhs->current_priority;
});
return threads;
@@ -73,9 +73,9 @@ static void WakeThreads(std::vector<SharedPtr<Thread>>& waiting_threads, s32 num
// Signal the waiting threads.
for (std::size_t i = 0; i < last; i++) {
ASSERT(waiting_threads[i]->GetStatus() == ThreadStatus::WaitArb);
ASSERT(waiting_threads[i]->status == ThreadStatus::WaitArb);
waiting_threads[i]->SetWaitSynchronizationResult(RESULT_SUCCESS);
waiting_threads[i]->SetArbiterWaitAddress(0);
waiting_threads[i]->arb_wait_address = 0;
waiting_threads[i]->ResumeFromWait();
}
}

View File

@@ -17,10 +17,6 @@ namespace Kernel {
ClientPort::ClientPort(KernelCore& kernel) : Object{kernel} {}
ClientPort::~ClientPort() = default;
SharedPtr<ServerPort> ClientPort::GetServerPort() const {
return server_port;
}
ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() {
// Note: Threads do not wait for the server endpoint to call
// AcceptSession before returning from this call.

View File

@@ -30,8 +30,6 @@ public:
return HANDLE_TYPE;
}
SharedPtr<ServerPort> GetServerPort() const;
/**
* Creates a new Session pair, adds the created ServerSession to the associated ServerPort's
* list of pending sessions, and signals the ServerPort, causing any threads

View File

@@ -42,14 +42,14 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
Kernel::SharedPtr<Kernel::Event> event) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->SetWakeupCallback([context = *this, callback](
thread->wakeup_callback = [context = *this, callback](
ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index) mutable -> bool {
ASSERT(thread->GetStatus() == ThreadStatus::WaitHLEEvent);
ASSERT(thread->status == ThreadStatus::WaitHLEEvent);
callback(thread, context, reason);
context.WriteToOutgoingCommandBuffer(*thread);
return true;
});
};
if (!event) {
// Create event if not provided
@@ -59,8 +59,8 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
}
event->Clear();
thread->SetStatus(ThreadStatus::WaitHLEEvent);
thread->SetWaitObjects({event});
thread->status = ThreadStatus::WaitHLEEvent;
thread->wait_objects = {event};
event->AddWaitingThread(thread);
if (timeout > 0) {
@@ -209,7 +209,7 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(u32_le* src_cmdb
ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread) {
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> dst_cmdbuf;
Memory::ReadBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
Memory::ReadBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
// The header was already built in the internal command buffer. Attempt to parse it to verify
@@ -268,7 +268,7 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(const Thread& thread)
}
// Copy the translated command buffer back into the thread's command buffer area.
Memory::WriteBlock(*thread.GetOwnerProcess(), thread.GetTLSAddress(), dst_cmdbuf.data(),
Memory::WriteBlock(*thread.owner_process, thread.GetTLSAddress(), dst_cmdbuf.data(),
dst_cmdbuf.size() * sizeof(u32));
return RESULT_SUCCESS;

View File

@@ -46,40 +46,40 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
bool resume = true;
if (thread->GetStatus() == ThreadStatus::WaitSynchAny ||
thread->GetStatus() == ThreadStatus::WaitSynchAll ||
thread->GetStatus() == ThreadStatus::WaitHLEEvent) {
if (thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent) {
// Remove the thread from each of its waiting objects' waitlists
for (const auto& object : thread->GetWaitObjects()) {
for (auto& object : thread->wait_objects) {
object->RemoveWaitingThread(thread.get());
}
thread->ClearWaitObjects();
thread->wait_objects.clear();
// Invoke the wakeup callback before clearing the wait objects
if (thread->HasWakeupCallback()) {
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
if (thread->wakeup_callback) {
resume = thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr, 0);
}
}
if (thread->GetMutexWaitAddress() != 0 || thread->GetCondVarWaitAddress() != 0 ||
thread->GetWaitHandle() != 0) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
if (thread->mutex_wait_address != 0 || thread->condvar_wait_address != 0 ||
thread->wait_handle) {
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
auto* const lock_owner = thread->GetLockOwner();
auto lock_owner = thread->lock_owner;
// Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
// and don't have a lock owner unless SignalProcessWideKey was called first and the thread
// wasn't awakened due to the mutex already being acquired.
if (lock_owner != nullptr) {
if (lock_owner) {
lock_owner->RemoveMutexWaiter(thread);
}
}
if (thread->GetArbiterWaitAddress() != 0) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitArb);
thread->SetArbiterWaitAddress(0);
if (thread->arb_wait_address != 0) {
ASSERT(thread->status == ThreadStatus::WaitArb);
thread->arb_wait_address = 0;
}
if (resume) {

View File

@@ -28,11 +28,11 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
SharedPtr<Thread> highest_priority_thread;
u32 num_waiters = 0;
for (const auto& thread : current_thread->GetMutexWaitingThreads()) {
if (thread->GetMutexWaitAddress() != mutex_addr)
for (auto& thread : current_thread->wait_mutex_threads) {
if (thread->mutex_wait_address != mutex_addr)
continue;
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
ASSERT(thread->status == ThreadStatus::WaitMutex);
++num_waiters;
if (highest_priority_thread == nullptr ||
@@ -47,12 +47,12 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
SharedPtr<Thread> new_owner) {
const auto threads = current_thread->GetMutexWaitingThreads();
for (const auto& thread : threads) {
if (thread->GetMutexWaitAddress() != mutex_addr)
auto threads = current_thread->wait_mutex_threads;
for (auto& thread : threads) {
if (thread->mutex_wait_address != mutex_addr)
continue;
ASSERT(thread->GetLockOwner() == current_thread);
ASSERT(thread->lock_owner == current_thread);
current_thread->RemoveMutexWaiter(thread);
if (new_owner != thread)
new_owner->AddMutexWaiter(thread);
@@ -84,11 +84,11 @@ ResultCode Mutex::TryAcquire(HandleTable& handle_table, VAddr address, Handle ho
return ERR_INVALID_HANDLE;
// Wait until the mutex is released
GetCurrentThread()->SetMutexWaitAddress(address);
GetCurrentThread()->SetWaitHandle(requesting_thread_handle);
GetCurrentThread()->mutex_wait_address = address;
GetCurrentThread()->wait_handle = requesting_thread_handle;
GetCurrentThread()->SetStatus(ThreadStatus::WaitMutex);
GetCurrentThread()->InvalidateWakeupCallback();
GetCurrentThread()->status = ThreadStatus::WaitMutex;
GetCurrentThread()->wakeup_callback = nullptr;
// Update the lock holder thread's priority to prevent priority inversion.
holding_thread->AddMutexWaiter(GetCurrentThread());
@@ -115,7 +115,7 @@ ResultCode Mutex::Release(VAddr address) {
// Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, GetCurrentThread(), thread);
u32 mutex_value = thread->GetWaitHandle();
u32 mutex_value = thread->wait_handle;
if (num_waiters >= 2) {
// Notify the guest that there are still some threads waiting for the mutex
@@ -125,13 +125,13 @@ ResultCode Mutex::Release(VAddr address) {
// Grant the mutex to the next waiting thread and resume it.
Memory::Write32(address, mutex_value);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->ResumeFromWait();
thread->SetLockOwner(nullptr);
thread->SetCondVarWaitAddress(0);
thread->SetMutexWaitAddress(0);
thread->SetWaitHandle(0);
thread->lock_owner = nullptr;
thread->condvar_wait_address = 0;
thread->mutex_wait_address = 0;
thread->wait_handle = 0;
return RESULT_SUCCESS;
}

View File

@@ -144,15 +144,15 @@ void Process::PrepareForTermination() {
const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
for (auto& thread : thread_list) {
if (thread->GetOwnerProcess() != this)
if (thread->owner_process != this)
continue;
if (thread == GetCurrentThread())
continue;
// TODO(Subv): When are the other running/ready threads terminated?
ASSERT_MSG(thread->GetStatus() == ThreadStatus::WaitSynchAny ||
thread->GetStatus() == ThreadStatus::WaitSynchAll,
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll,
"Exiting processes with non-waiting threads is currently unimplemented");
thread->Stop();

View File

@@ -38,10 +38,10 @@ Thread* Scheduler::PopNextReadyThread() {
Thread* next = nullptr;
Thread* thread = GetCurrentThread();
if (thread && thread->GetStatus() == ThreadStatus::Running) {
if (thread && thread->status == ThreadStatus::Running) {
// We have to do better than the current thread.
// This call returns null when that's not possible.
next = ready_queue.pop_first_better(thread->GetPriority());
next = ready_queue.pop_first_better(thread->current_priority);
if (!next) {
// Otherwise just keep going with the current thread
next = thread;
@@ -58,21 +58,22 @@ void Scheduler::SwitchContext(Thread* new_thread) {
// Save context for previous thread
if (previous_thread) {
cpu_core.SaveContext(previous_thread->GetContext());
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->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
previous_thread->tpidr_el0 = cpu_core.GetTPIDR_EL0();
if (previous_thread->GetStatus() == ThreadStatus::Running) {
if (previous_thread->status == ThreadStatus::Running) {
// This is only the case when a reschedule is triggered without the current thread
// yielding execution (i.e. an event triggered, system core time-sliced, etc)
ready_queue.push_front(previous_thread->GetPriority(), previous_thread);
previous_thread->SetStatus(ThreadStatus::Ready);
ready_queue.push_front(previous_thread->current_priority, previous_thread);
previous_thread->status = ThreadStatus::Ready;
}
}
// Load context of new thread
if (new_thread) {
ASSERT_MSG(new_thread->GetStatus() == ThreadStatus::Ready,
ASSERT_MSG(new_thread->status == ThreadStatus::Ready,
"Thread must be ready to become running.");
// Cancel any outstanding wakeup events for this thread
@@ -82,16 +83,15 @@ void Scheduler::SwitchContext(Thread* new_thread) {
current_thread = new_thread;
ready_queue.remove(new_thread->GetPriority(), new_thread);
new_thread->SetStatus(ThreadStatus::Running);
ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = ThreadStatus::Running;
const auto thread_owner_process = current_thread->GetOwnerProcess();
if (previous_process != thread_owner_process) {
Core::CurrentProcess() = thread_owner_process;
if (previous_process != current_thread->owner_process) {
Core::CurrentProcess() = current_thread->owner_process;
SetCurrentPageTable(&Core::CurrentProcess()->VMManager().page_table);
}
cpu_core.LoadContext(new_thread->GetContext());
cpu_core.LoadContext(new_thread->context);
cpu_core.SetTlsAddress(new_thread->GetTLSAddress());
cpu_core.SetTPIDR_EL0(new_thread->GetTPIDR_EL0());
cpu_core.ClearExclusiveState();
@@ -136,14 +136,14 @@ void Scheduler::RemoveThread(Thread* thread) {
void Scheduler::ScheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ASSERT(thread->status == ThreadStatus::Ready);
ready_queue.push_back(priority, thread);
}
void Scheduler::UnscheduleThread(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
ASSERT(thread->GetStatus() == ThreadStatus::Ready);
ASSERT(thread->status == ThreadStatus::Ready);
ready_queue.remove(priority, thread);
}
@@ -151,8 +151,8 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
std::lock_guard<std::mutex> lock(scheduler_mutex);
// If thread was ready, adjust queues
if (thread->GetStatus() == ThreadStatus::Ready)
ready_queue.move(thread, thread->GetPriority(), priority);
if (thread->status == ThreadStatus::Ready)
ready_queue.move(thread, thread->current_priority, priority);
else
ready_queue.prepare(priority);
}

View File

@@ -11,7 +11,6 @@
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
namespace Kernel {

View File

@@ -120,10 +120,10 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
result = hle_handler->HandleSyncRequest(context);
}
if (thread->GetStatus() == ThreadStatus::Running) {
if (thread->status == ThreadStatus::Running) {
// Put the thread to sleep until the server replies, it will be awoken in
// svcReplyAndReceive for LLE servers.
thread->SetStatus(ThreadStatus::WaitIPC);
thread->status = ThreadStatus::WaitIPC;
if (hle_handler != nullptr) {
// For HLE services, we put the request threads to sleep for a short duration to

View File

@@ -156,7 +156,7 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
return ERR_INVALID_HANDLE;
}
*thread_id = thread->GetThreadID();
*thread_id = thread->GetThreadId();
return RESULT_SUCCESS;
}
@@ -177,7 +177,7 @@ static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
/// Default thread wakeup callback for WaitSynchronization
static bool DefaultThreadWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index) {
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
if (reason == ThreadWakeupReason::Timeout) {
thread->SetWaitSynchronizationResult(RESULT_TIMEOUT);
@@ -204,10 +204,10 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
if (handle_count > MaxHandles)
return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
auto* const thread = GetCurrentThread();
auto thread = GetCurrentThread();
using ObjectPtr = Thread::ThreadWaitObjects::value_type;
Thread::ThreadWaitObjects objects(handle_count);
using ObjectPtr = SharedPtr<WaitObject>;
std::vector<ObjectPtr> objects(handle_count);
auto& kernel = Core::System::GetInstance().Kernel();
for (u64 i = 0; i < handle_count; ++i) {
@@ -244,14 +244,14 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
for (auto& object : objects)
object->AddWaitingThread(thread);
thread->SetWaitObjects(std::move(objects));
thread->SetStatus(ThreadStatus::WaitSynchAny);
thread->wait_objects = std::move(objects);
thread->status = ThreadStatus::WaitSynchAny;
// Create an event to wake the thread up after the specified nanosecond delay has passed
thread->WakeAfterDelay(nano_seconds);
thread->SetWakeupCallback(DefaultThreadWakeupCallback);
thread->wakeup_callback = DefaultThreadWakeupCallback;
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
return RESULT_TIMEOUT;
}
@@ -266,7 +266,7 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
return ERR_INVALID_HANDLE;
}
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
ASSERT(thread->status == ThreadStatus::WaitSynchAny);
thread->SetWaitSynchronizationResult(
ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
thread->ResumeFromWait();
@@ -425,7 +425,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
}
const auto current_process = Core::CurrentProcess();
if (thread->GetOwnerProcess() != current_process) {
if (thread->owner_process != current_process) {
return ERR_INVALID_HANDLE;
}
@@ -433,7 +433,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
return ERR_ALREADY_REGISTERED;
}
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
Core::ARM_Interface::ThreadContext ctx = thread->context;
// Mask away mode bits, interrupt bits, IL bit, and other reserved bits.
ctx.pstate &= 0xFF0FFE20;
@@ -479,14 +479,14 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
thread->SetPriority(priority);
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
return RESULT_SUCCESS;
}
/// Get which CPU core is executing the current thread
static u32 GetCurrentProcessorNumber() {
LOG_TRACE(Kernel_SVC, "called");
return GetCurrentThread()->GetProcessorID();
return GetCurrentThread()->processor_id;
}
static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size,
@@ -622,14 +622,10 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
CASCADE_RESULT(SharedPtr<Thread> thread,
Thread::Create(kernel, name, entry_point, priority, arg, processor_id, stack_top,
Core::CurrentProcess()));
const auto new_guest_handle = kernel.HandleTable().Create(thread);
if (new_guest_handle.Failed()) {
return new_guest_handle.Code();
}
thread->SetGuestHandle(*new_guest_handle);
*out_handle = *new_guest_handle;
CASCADE_RESULT(thread->guest_handle, kernel.HandleTable().Create(thread));
*out_handle = thread->guest_handle;
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
LOG_TRACE(Kernel_SVC,
"called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
@@ -649,10 +645,10 @@ static ResultCode StartThread(Handle thread_handle) {
return ERR_INVALID_HANDLE;
}
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
ASSERT(thread->status == ThreadStatus::Dormant);
thread->ResumeFromWait();
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -698,17 +694,17 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
CASCADE_CODE(Mutex::Release(mutex_addr));
SharedPtr<Thread> current_thread = GetCurrentThread();
current_thread->SetCondVarWaitAddress(condition_variable_addr);
current_thread->SetMutexWaitAddress(mutex_addr);
current_thread->SetWaitHandle(thread_handle);
current_thread->SetStatus(ThreadStatus::WaitMutex);
current_thread->InvalidateWakeupCallback();
current_thread->condvar_wait_address = condition_variable_addr;
current_thread->mutex_wait_address = mutex_addr;
current_thread->wait_handle = thread_handle;
current_thread->status = ThreadStatus::WaitMutex;
current_thread->wakeup_callback = nullptr;
current_thread->WakeAfterDelay(nano_seconds);
// Note: Deliberately don't attempt to inherit the lock owner's priority.
Core::System::GetInstance().CpuCore(current_thread->GetProcessorID()).PrepareReschedule();
Core::System::GetInstance().CpuCore(current_thread->processor_id).PrepareReschedule();
return RESULT_SUCCESS;
}
@@ -717,14 +713,14 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
LOG_TRACE(Kernel_SVC, "called, condition_variable_addr=0x{:X}, target=0x{:08X}",
condition_variable_addr, target);
const auto RetrieveWaitingThreads = [](std::size_t core_index,
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr condvar_addr) {
auto RetrieveWaitingThreads = [](std::size_t core_index,
std::vector<SharedPtr<Thread>>& waiting_threads,
VAddr condvar_addr) {
const auto& scheduler = Core::System::GetInstance().Scheduler(core_index);
const auto& thread_list = scheduler->GetThreadList();
auto& thread_list = scheduler->GetThreadList();
for (const auto& thread : thread_list) {
if (thread->GetCondVarWaitAddress() == condvar_addr)
for (auto& thread : thread_list) {
if (thread->condvar_wait_address == condvar_addr)
waiting_threads.push_back(thread);
}
};
@@ -738,7 +734,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
// Sort them by priority, such that the highest priority ones come first.
std::sort(waiting_threads.begin(), waiting_threads.end(),
[](const SharedPtr<Thread>& lhs, const SharedPtr<Thread>& rhs) {
return lhs->GetPriority() < rhs->GetPriority();
return lhs->current_priority < rhs->current_priority;
});
// Only process up to 'target' threads, unless 'target' is -1, in which case process
@@ -754,7 +750,7 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
for (std::size_t index = 0; index < last; ++index) {
auto& thread = waiting_threads[index];
ASSERT(thread->GetCondVarWaitAddress() == condition_variable_addr);
ASSERT(thread->condvar_wait_address == condition_variable_addr);
std::size_t current_core = Core::System::GetInstance().CurrentCoreIndex();
@@ -763,43 +759,42 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
// Atomically read the value of the mutex.
u32 mutex_val = 0;
do {
monitor.SetExclusive(current_core, thread->GetMutexWaitAddress());
monitor.SetExclusive(current_core, thread->mutex_wait_address);
// If the mutex is not yet acquired, acquire it.
mutex_val = Memory::Read32(thread->GetMutexWaitAddress());
mutex_val = Memory::Read32(thread->mutex_wait_address);
if (mutex_val != 0) {
monitor.ClearExclusive();
break;
}
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
thread->GetWaitHandle()));
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
thread->wait_handle));
if (mutex_val == 0) {
// We were able to acquire the mutex, resume this thread.
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->ResumeFromWait();
auto* const lock_owner = thread->GetLockOwner();
if (lock_owner != nullptr) {
auto lock_owner = thread->lock_owner;
if (lock_owner)
lock_owner->RemoveMutexWaiter(thread);
}
thread->SetLockOwner(nullptr);
thread->SetMutexWaitAddress(0);
thread->SetCondVarWaitAddress(0);
thread->SetWaitHandle(0);
thread->lock_owner = nullptr;
thread->mutex_wait_address = 0;
thread->condvar_wait_address = 0;
thread->wait_handle = 0;
} else {
// Atomically signal that the mutex now has a waiting thread.
do {
monitor.SetExclusive(current_core, thread->GetMutexWaitAddress());
monitor.SetExclusive(current_core, thread->mutex_wait_address);
// Ensure that the mutex value is still what we expect.
u32 value = Memory::Read32(thread->GetMutexWaitAddress());
u32 value = Memory::Read32(thread->mutex_wait_address);
// TODO(Subv): When this happens, the kernel just clears the exclusive state and
// retries the initial read for this thread.
ASSERT_MSG(mutex_val == value, "Unhandled synchronization primitive case");
} while (!monitor.ExclusiveWrite32(current_core, thread->GetMutexWaitAddress(),
} while (!monitor.ExclusiveWrite32(current_core, thread->mutex_wait_address,
mutex_val | Mutex::MutexHasWaitersFlag));
// The mutex is already owned by some other thread, make this thread wait on it.
@@ -807,12 +802,12 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
Handle owner_handle = static_cast<Handle>(mutex_val & Mutex::MutexOwnerMask);
auto owner = kernel.HandleTable().Get<Thread>(owner_handle);
ASSERT(owner);
ASSERT(thread->GetStatus() == ThreadStatus::WaitMutex);
thread->InvalidateWakeupCallback();
ASSERT(thread->status == ThreadStatus::WaitMutex);
thread->wakeup_callback = nullptr;
owner->AddMutexWaiter(thread);
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
Core::System::GetInstance().CpuCore(thread->processor_id).PrepareReschedule();
}
}
@@ -918,8 +913,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
return ERR_INVALID_HANDLE;
}
*core = thread->GetIdealCore();
*mask = thread->GetAffinityMask();
*core = thread->ideal_core;
*mask = thread->affinity_mask;
return RESULT_SUCCESS;
}
@@ -935,13 +930,11 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
}
if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
ASSERT(thread->owner_process->GetDefaultProcessorID() !=
static_cast<u8>(THREADPROCESSORID_DEFAULT));
// Set the target CPU to the one specified in the process' exheader.
core = default_processor_id;
mask = 1ULL << core;
core = thread->owner_process->GetDefaultProcessorID();
mask = 1ull << core;
}
if (mask == 0) {
@@ -952,7 +945,7 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
static constexpr u32 OnlyChangeMask = static_cast<u32>(-3);
if (core == OnlyChangeMask) {
core = thread->GetIdealCore();
core = thread->ideal_core;
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
}

View File

@@ -70,7 +70,7 @@ void Thread::Stop() {
void WaitCurrentThread_Sleep() {
Thread* thread = GetCurrentThread();
thread->SetStatus(ThreadStatus::WaitSleep);
thread->status = ThreadStatus::WaitSleep;
}
void ExitCurrentThread() {
@@ -169,7 +169,7 @@ void Thread::ResumeFromWait() {
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = next_scheduler.get();
scheduler = next_scheduler;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
@@ -233,7 +233,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name
thread->name = std::move(name);
thread->callback_handle = kernel.ThreadWakeupCallbackHandleTable().Create(thread).Unwrap();
thread->owner_process = owner_process;
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id).get();
thread->scheduler = Core::System::GetInstance().Scheduler(processor_id);
thread->scheduler->AddThread(thread, priority);
thread->tls_address = thread->owner_process->MarkNextAvailableTLSSlotAsUsed(*thread);
@@ -269,9 +269,9 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri
SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
// Register 1 must be a handle to the main thread
const Handle guest_handle = kernel.HandleTable().Create(thread).Unwrap();
thread->SetGuestHandle(guest_handle);
thread->GetContext().cpu_registers[1] = guest_handle;
thread->guest_handle = kernel.HandleTable().Create(thread).Unwrap();
thread->context.cpu_registers[1] = thread->guest_handle;
// Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
thread->ResumeFromWait();
@@ -299,18 +299,6 @@ VAddr Thread::GetCommandBufferAddress() const {
return GetTLSAddress() + CommandHeaderOffset;
}
void Thread::SetStatus(ThreadStatus new_status) {
if (new_status == status) {
return;
}
if (status == ThreadStatus::Running) {
last_running_ticks = CoreTiming::GetTicks();
}
status = new_status;
}
void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
if (thread->lock_owner == this) {
// If the thread is already waiting for this thread to release the mutex, ensure that the
@@ -400,23 +388,11 @@ void Thread::ChangeCore(u32 core, u64 mask) {
next_scheduler->ScheduleThread(this, current_priority);
// Change thread's scheduler
scheduler = next_scheduler.get();
scheduler = next_scheduler;
Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
}
bool Thread::AllWaitObjectsReady() {
return std::none_of(
wait_objects.begin(), wait_objects.end(),
[this](const SharedPtr<WaitObject>& object) { return object->ShouldWait(this); });
}
bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index) {
ASSERT(wakeup_callback);
return wakeup_callback(reason, std::move(thread), std::move(object), index);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**

View File

@@ -65,15 +65,6 @@ public:
using TLSMemory = std::vector<u8>;
using TLSMemoryPtr = std::shared_ptr<TLSMemory>;
using MutexWaitingThreads = std::vector<SharedPtr<Thread>>;
using ThreadContext = Core::ARM_Interface::ThreadContext;
using ThreadWaitObjects = std::vector<SharedPtr<WaitObject>>;
using WakeupCallback = std::function<bool(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index)>;
/**
* Creates and returns a new thread. The new thread is immediately scheduled
* @param kernel The kernel instance this thread will be created under.
@@ -114,14 +105,6 @@ public:
return current_priority;
}
/**
* Gets the thread's nominal priority.
* @return The current thread's nominal priority.
*/
u32 GetNominalPriority() const {
return nominal_priority;
}
/**
* Sets the thread's current priority
* @param priority The new priority
@@ -150,7 +133,7 @@ public:
* Gets the thread's thread ID
* @return The thread's ID
*/
u32 GetThreadID() const {
u32 GetThreadId() const {
return thread_id;
}
@@ -220,11 +203,6 @@ public:
return tpidr_el0;
}
/// Sets the value of the TPIDR_EL0 Read/Write system register for this thread.
void SetTPIDR_EL0(u64 value) {
tpidr_el0 = value;
}
/*
* Returns the address of the current thread's command buffer, located in the TLS.
* @returns VAddr of the thread's command buffer.
@@ -240,193 +218,69 @@ public:
return status == ThreadStatus::WaitSynchAll;
}
ThreadContext& GetContext() {
return context;
}
Core::ARM_Interface::ThreadContext context;
const ThreadContext& GetContext() const {
return context;
}
u32 thread_id;
ThreadStatus GetStatus() const {
return status;
}
ThreadStatus status;
VAddr entry_point;
VAddr stack_top;
void SetStatus(ThreadStatus new_status);
u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application
u32 current_priority; ///< Current thread priority, can be temporarily changed
u64 GetLastRunningTicks() const {
return last_running_ticks;
}
u64 last_running_ticks; ///< CPU tick when thread was last running
s32 GetProcessorID() const {
return processor_id;
}
s32 processor_id;
SharedPtr<Process>& GetOwnerProcess() {
return owner_process;
}
VAddr tls_address; ///< Virtual address of the Thread Local Storage of the thread
u64 tpidr_el0; ///< TPIDR_EL0 read/write system register.
const SharedPtr<Process>& GetOwnerProcess() const {
return owner_process;
}
SharedPtr<Process> owner_process; ///< Process that owns this thread
const ThreadWaitObjects& GetWaitObjects() const {
return wait_objects;
}
/// Objects that the thread is waiting on, in the same order as they were
// passed to WaitSynchronization1/N.
std::vector<SharedPtr<WaitObject>> wait_objects;
void SetWaitObjects(ThreadWaitObjects objects) {
wait_objects = std::move(objects);
}
/// List of threads that are waiting for a mutex that is held by this thread.
std::vector<SharedPtr<Thread>> wait_mutex_threads;
void ClearWaitObjects() {
wait_objects.clear();
}
/// Thread that owns the lock that this thread is waiting for.
SharedPtr<Thread> lock_owner;
/// Determines whether all the objects this thread is waiting on are ready.
bool AllWaitObjectsReady();
// If waiting on a ConditionVariable, this is the ConditionVariable address
VAddr condvar_wait_address;
VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
Handle wait_handle; ///< The handle used to wait for the mutex.
const MutexWaitingThreads& GetMutexWaitingThreads() const {
return wait_mutex_threads;
}
// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
Thread* GetLockOwner() const {
return lock_owner.get();
}
std::string name;
void SetLockOwner(SharedPtr<Thread> owner) {
lock_owner = std::move(owner);
}
/// Handle used by guest emulated application to access this thread
Handle guest_handle;
VAddr GetCondVarWaitAddress() const {
return condvar_wait_address;
}
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle callback_handle;
void SetCondVarWaitAddress(VAddr address) {
condvar_wait_address = address;
}
using WakeupCallback = bool(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index);
// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
// was waiting via WaitSynchronizationN then the object will be the last object that became
// available. In case of a timeout, the object will be nullptr.
std::function<WakeupCallback> wakeup_callback;
VAddr GetMutexWaitAddress() const {
return mutex_wait_address;
}
std::shared_ptr<Scheduler> scheduler;
void SetMutexWaitAddress(VAddr address) {
mutex_wait_address = address;
}
Handle GetWaitHandle() const {
return wait_handle;
}
void SetWaitHandle(Handle handle) {
wait_handle = handle;
}
VAddr GetArbiterWaitAddress() const {
return arb_wait_address;
}
void SetArbiterWaitAddress(VAddr address) {
arb_wait_address = address;
}
void SetGuestHandle(Handle handle) {
guest_handle = handle;
}
bool HasWakeupCallback() const {
return wakeup_callback != nullptr;
}
void SetWakeupCallback(WakeupCallback callback) {
wakeup_callback = std::move(callback);
}
void InvalidateWakeupCallback() {
SetWakeupCallback(nullptr);
}
/**
* Invokes the thread's wakeup callback.
*
* @pre A valid wakeup callback has been set. Violating this precondition
* will cause an assertion to trigger.
*/
bool InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> thread,
SharedPtr<WaitObject> object, std::size_t index);
u32 GetIdealCore() const {
return ideal_core;
}
u64 GetAffinityMask() const {
return affinity_mask;
}
u32 ideal_core{0xFFFFFFFF};
u64 affinity_mask{0x1};
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
Core::ARM_Interface::ThreadContext context{};
u32 thread_id = 0;
ThreadStatus status = ThreadStatus::Dormant;
VAddr entry_point = 0;
VAddr stack_top = 0;
u32 nominal_priority = 0; ///< Nominal thread priority, as set by the emulated application
u32 current_priority = 0; ///< Current thread priority, can be temporarily changed
u64 last_running_ticks = 0; ///< CPU tick when thread was last running
s32 processor_id = 0;
VAddr tls_address = 0; ///< Virtual address of the Thread Local Storage of the thread
u64 tpidr_el0 = 0; ///< TPIDR_EL0 read/write system register.
/// Process that owns this thread
SharedPtr<Process> owner_process;
/// Objects that the thread is waiting on, in the same order as they were
/// passed to WaitSynchronization1/N.
ThreadWaitObjects wait_objects;
/// List of threads that are waiting for a mutex that is held by this thread.
MutexWaitingThreads wait_mutex_threads;
/// Thread that owns the lock that this thread is waiting for.
SharedPtr<Thread> lock_owner;
/// If waiting on a ConditionVariable, this is the ConditionVariable address
VAddr condvar_wait_address = 0;
/// If waiting on a Mutex, this is the mutex address
VAddr mutex_wait_address = 0;
/// The handle used to wait for the mutex.
Handle wait_handle = 0;
/// If waiting for an AddressArbiter, this is the address being waited on.
VAddr arb_wait_address{0};
/// Handle used by guest emulated application to access this thread
Handle guest_handle = 0;
/// Handle used as userdata to reference this object when inserting into the CoreTiming queue.
Handle callback_handle = 0;
/// Callback that will be invoked when the thread is resumed from a waiting state. If the thread
/// was waiting via WaitSynchronizationN then the object will be the last object that became
/// available. In case of a timeout, the object will be nullptr.
WakeupCallback wakeup_callback;
Scheduler* scheduler = nullptr;
u32 ideal_core{0xFFFFFFFF};
u64 affinity_mask{0x1};
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
std::string name;
};
/**

View File

@@ -35,15 +35,13 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
u32 candidate_priority = THREADPRIO_LOWEST + 1;
for (const auto& thread : waiting_threads) {
const ThreadStatus thread_status = thread->GetStatus();
// The list of waiting threads must not contain threads that are not waiting to be awakened.
ASSERT_MSG(thread_status == ThreadStatus::WaitSynchAny ||
thread_status == ThreadStatus::WaitSynchAll ||
thread_status == ThreadStatus::WaitHLEEvent,
ASSERT_MSG(thread->status == ThreadStatus::WaitSynchAny ||
thread->status == ThreadStatus::WaitSynchAll ||
thread->status == ThreadStatus::WaitHLEEvent,
"Inconsistent thread statuses in waiting_threads");
if (thread->GetPriority() >= candidate_priority)
if (thread->current_priority >= candidate_priority)
continue;
if (ShouldWait(thread.get()))
@@ -52,13 +50,16 @@ SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() {
// A thread is ready to run if it's either in ThreadStatus::WaitSynchAny or
// in ThreadStatus::WaitSynchAll and the rest of the objects it is waiting on are ready.
bool ready_to_run = true;
if (thread_status == ThreadStatus::WaitSynchAll) {
ready_to_run = thread->AllWaitObjectsReady();
if (thread->status == ThreadStatus::WaitSynchAll) {
ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(),
[&thread](const SharedPtr<WaitObject>& object) {
return object->ShouldWait(thread.get());
});
}
if (ready_to_run) {
candidate = thread.get();
candidate_priority = thread->GetPriority();
candidate_priority = thread->current_priority;
}
}
@@ -74,24 +75,24 @@ void WaitObject::WakeupWaitingThread(SharedPtr<Thread> thread) {
if (!thread->IsSleepingOnWaitAll()) {
Acquire(thread.get());
} else {
for (const auto& object : thread->GetWaitObjects()) {
for (auto& object : thread->wait_objects) {
ASSERT(!object->ShouldWait(thread.get()));
object->Acquire(thread.get());
}
}
const std::size_t index = thread->GetWaitObjectIndex(this);
std::size_t index = thread->GetWaitObjectIndex(this);
for (const auto& object : thread->GetWaitObjects())
for (auto& object : thread->wait_objects)
object->RemoveWaitingThread(thread.get());
thread->ClearWaitObjects();
thread->wait_objects.clear();
thread->CancelWakeupTimer();
bool resume = true;
if (thread->HasWakeupCallback())
resume = thread->InvokeWakeupCallback(ThreadWakeupReason::Signal, thread, this, index);
if (thread->wakeup_callback)
resume = thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this, index);
if (resume)
thread->ResumeFromWait();

View File

@@ -264,15 +264,6 @@ ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
return RESULT_SUCCESS;
}
void SetPackedUpdate(FileSys::VirtualFile update_raw) {
LOG_TRACE(Service_FS, "Setting packed update for romfs");
if (romfs_factory == nullptr)
return;
romfs_factory->SetPackedUpdate(std::move(update_raw));
}
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
LOG_TRACE(Service_FS, "Opening RomFS for current process");

View File

@@ -39,7 +39,6 @@ ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory)
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
void SetPackedUpdate(FileSys::VirtualFile update_raw);
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type);

View File

@@ -6,12 +6,9 @@
#include <memory>
#include <string>
#include <type_traits>
#include <unordered_map>
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/server_port.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
@@ -51,22 +48,6 @@ public:
ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name);
ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name);
template <typename T>
std::shared_ptr<T> GetService(const std::string& service_name) const {
static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
"Not a base of ServiceFrameworkBase");
auto service = registered_services.find(service_name);
if (service == registered_services.end()) {
LOG_DEBUG(Service, "Can't find service: {}", service_name);
return nullptr;
}
auto port = service->second->GetServerPort();
if (port == nullptr) {
return nullptr;
}
return std::static_pointer_cast<T>(port->hle_handler);
}
void InvokeControlRequest(Kernel::HLERequestContext& context);
private:

View File

@@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
return "unknown";
}
constexpr std::array<const char*, 59> RESULT_MESSAGES{
constexpr std::array<const char*, 58> RESULT_MESSAGES{
"The operation completed successfully.",
"The loader requested to load is already loaded.",
"The operation is not implemented.",
@@ -152,7 +152,6 @@ constexpr std::array<const char*, 59> RESULT_MESSAGES{
"The BKTR-type NCA has a bad Relocation bucket.",
"The BKTR-type NCA has a bad Subsection bucket.",
"The BKTR-type NCA is missing the base RomFS.",
"The NSP or XCI does not contain an update in addition to the base game.",
};
std::ostream& operator<<(std::ostream& os, ResultStatus status) {

View File

@@ -114,7 +114,6 @@ enum class ResultStatus : u16 {
ErrorBadRelocationBuckets,
ErrorBadSubsectionBuckets,
ErrorMissingBKTRBaseRomFS,
ErrorNoPackedUpdate,
};
std::ostream& operator<<(std::ostream& os, ResultStatus status);
@@ -197,19 +196,10 @@ public:
/**
* Get the RomFS of the application
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer
* @param file The directory containing the RomFS
* @param dir The directory containing the RomFS
* @return ResultStatus result of function
*/
virtual ResultStatus ReadRomFS(FileSys::VirtualFile& file) {
return ResultStatus::ErrorNotImplemented;
}
/**
* Get the raw update of the application, should it come packed with one
* @param file The raw update NCA file (Program-type
* @return ResultStatus result of function
*/
virtual ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) {
virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) {
return ResultStatus::ErrorNotImplemented;
}

View File

@@ -72,10 +72,6 @@ ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
return nca_loader->ReadRomFS(dir);
}
u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
return nca_loader->ReadRomFSIVFCOffset();
}
ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
return nca_loader->ReadProgramId(out_program_id);
}

View File

@@ -36,7 +36,6 @@ public:
ResultStatus Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override;
private:

View File

@@ -10,10 +10,8 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/nca.h"
#include "core/loader/nsp.h"
@@ -93,39 +91,13 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
if (result != ResultStatus::Success)
return result;
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
is_loaded = true;
return ResultStatus::Success;
}
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
return secondary_loader->ReadRomFS(file);
}
u64 AppLoader_NSP::ReadRomFSIVFCOffset() const {
return secondary_loader->ReadRomFSIVFCOffset();
}
ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& file) {
if (nsp->IsExtractedType())
return ResultStatus::ErrorNoPackedUpdate;
const auto read =
nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_id), FileSys::ContentRecordType::Program);
if (read == nullptr)
return ResultStatus::ErrorNoPackedUpdate;
const auto nca_test = std::make_shared<FileSys::NCA>(read);
if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS)
return nca_test->GetStatus();
file = read;
return ResultStatus::Success;
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) {
return secondary_loader->ReadRomFS(dir);
}
ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {

View File

@@ -37,9 +37,7 @@ public:
ResultStatus Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override;

View File

@@ -9,11 +9,7 @@
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/nca.h"
#include "core/loader/xci.h"
@@ -67,41 +63,13 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
if (result != ResultStatus::Success)
return result;
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
is_loaded = true;
return ResultStatus::Success;
}
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
return nca_loader->ReadRomFS(file);
}
u64 AppLoader_XCI::ReadRomFSIVFCOffset() const {
return nca_loader->ReadRomFSIVFCOffset();
}
ResultStatus AppLoader_XCI::ReadUpdateRaw(FileSys::VirtualFile& file) {
u64 program_id{};
nca_loader->ReadProgramId(program_id);
if (program_id == 0)
return ResultStatus::ErrorXCIMissingProgramNCA;
const auto read = xci->GetSecurePartitionNSP()->GetNCAFile(
FileSys::GetUpdateTitleID(program_id), FileSys::ContentRecordType::Program);
if (read == nullptr)
return ResultStatus::ErrorNoPackedUpdate;
const auto nca_test = std::make_shared<FileSys::NCA>(read);
if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS)
return nca_test->GetStatus();
file = read;
return ResultStatus::Success;
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& dir) {
return nca_loader->ReadRomFS(dir);
}
ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {

View File

@@ -37,9 +37,7 @@ public:
ResultStatus Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override;

View File

@@ -155,12 +155,6 @@ struct Values {
// Debugging
bool use_gdbstub;
u16 gdbstub_port;
// WebService
bool enable_telemetry;
std::string web_api_url;
std::string yuzu_username;
std::string yuzu_token;
} extern values;
void Apply();

View File

@@ -6,8 +6,6 @@
#include "common/common_types.h"
#include "common/file_util.h"
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -15,31 +13,10 @@
#include "core/settings.h"
#include "core/telemetry_session.h"
#ifdef ENABLE_WEB_SERVICE
#include "web_service/telemetry_json.h"
#include "web_service/verify_login.h"
#endif
namespace Core {
static u64 GenerateTelemetryId() {
u64 telemetry_id{};
mbedtls_entropy_context entropy;
mbedtls_entropy_init(&entropy);
mbedtls_ctr_drbg_context ctr_drbg;
std::string personalization = "yuzu Telemetry ID";
mbedtls_ctr_drbg_init(&ctr_drbg);
ASSERT(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
reinterpret_cast<const unsigned char*>(personalization.c_str()),
personalization.size()) == 0);
ASSERT(mbedtls_ctr_drbg_random(&ctr_drbg, reinterpret_cast<unsigned char*>(&telemetry_id),
sizeof(u64)) == 0);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
return telemetry_id;
}
@@ -48,21 +25,14 @@ u64 GetTelemetryId() {
const std::string filename{FileUtil::GetUserPath(FileUtil::UserPath::ConfigDir) +
"telemetry_id"};
bool generate_new_id = !FileUtil::Exists(filename);
if (!generate_new_id) {
if (FileUtil::Exists(filename)) {
FileUtil::IOFile file(filename, "rb");
if (!file.IsOpen()) {
LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
return {};
}
file.ReadBytes(&telemetry_id, sizeof(u64));
if (telemetry_id == 0) {
LOG_ERROR(Frontend, "telemetry_id is 0. Generating a new one.", telemetry_id);
generate_new_id = true;
}
}
if (generate_new_id) {
} else {
FileUtil::IOFile file(filename, "wb");
if (!file.IsOpen()) {
LOG_ERROR(Core, "failed to open telemetry_id: {}", filename);
@@ -89,20 +59,23 @@ u64 RegenerateTelemetryId() {
return new_telemetry_id;
}
bool VerifyLogin(const std::string& username, const std::string& token) {
std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func) {
#ifdef ENABLE_WEB_SERVICE
return WebService::VerifyLogin(Settings::values.web_api_url, username, token);
return WebService::VerifyLogin(username, token, Settings::values.verify_endpoint_url, func);
#else
return false;
return std::async(std::launch::async, [func{std::move(func)}]() {
func();
return false;
});
#endif
}
TelemetrySession::TelemetrySession() {
#ifdef ENABLE_WEB_SERVICE
if (Settings::values.enable_telemetry) {
backend = std::make_unique<WebService::TelemetryJson>(Settings::values.web_api_url,
Settings::values.yuzu_username,
Settings::values.yuzu_token);
backend = std::make_unique<WebService::TelemetryJson>(
Settings::values.telemetry_endpoint_url, Settings::values.yuzu_username,
Settings::values.yuzu_token);
} else {
backend = std::make_unique<Telemetry::NullVisitor>();
}
@@ -121,8 +94,7 @@ TelemetrySession::TelemetrySession() {
u64 program_id{};
const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadProgramId(program_id)};
if (res == Loader::ResultStatus::Success) {
const std::string formatted_program_id{fmt::format("{:016X}", program_id)};
AddField(Telemetry::FieldType::Session, "ProgramId", formatted_program_id);
AddField(Telemetry::FieldType::Session, "ProgramId", program_id);
std::string name;
System::GetInstance().GetAppLoader().ReadTitle(name);

View File

@@ -4,6 +4,7 @@
#pragma once
#include <future>
#include <memory>
#include "common/telemetry.h"
@@ -30,8 +31,6 @@ public:
field_collection.AddField(type, name, std::move(value));
}
static void FinalizeAsyncJob();
private:
Telemetry::FieldCollection field_collection; ///< Tracks all added fields for the session
std::unique_ptr<Telemetry::VisitorInterface> backend; ///< Backend interface that logs fields
@@ -56,6 +55,6 @@ u64 RegenerateTelemetryId();
* @param func A function that gets exectued when the verification is finished
* @returns Future with bool indicating whether the verification succeeded
*/
bool VerifyLogin(const std::string& username, const std::string& token);
std::future<bool> VerifyLogin(std::string username, std::string token, std::function<void()> func);
} // namespace Core

View File

@@ -27,8 +27,6 @@ add_library(video_core STATIC
renderer_base.h
renderer_opengl/gl_buffer_cache.cpp
renderer_opengl/gl_buffer_cache.h
renderer_opengl/gl_primitive_assembler.cpp
renderer_opengl/gl_primitive_assembler.h
renderer_opengl/gl_rasterizer.cpp
renderer_opengl/gl_rasterizer.h
renderer_opengl/gl_rasterizer_cache.cpp

View File

@@ -4,13 +4,11 @@
#include "core/memory.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/textures/decoders.h"
namespace Tegra::Engines {
Fermi2D::Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager)
: memory_manager(memory_manager), rasterizer{rasterizer} {}
Fermi2D::Fermi2D(MemoryManager& memory_manager) : memory_manager(memory_manager) {}
void Fermi2D::WriteReg(u32 method, u32 value) {
ASSERT_MSG(method < Regs::NUM_REGS,
@@ -46,31 +44,27 @@ void Fermi2D::HandleSurfaceCopy() {
u32 src_bytes_per_pixel = RenderTargetBytesPerPixel(regs.src.format);
u32 dst_bytes_per_pixel = RenderTargetBytesPerPixel(regs.dst.format);
if (!rasterizer.AccelerateSurfaceCopy(regs.src, regs.dst)) {
// TODO(bunnei): The below implementation currently will not get hit, as
// AccelerateSurfaceCopy tries to always copy and will always return success. This should be
// changed once we properly support flushing.
if (regs.src.linear == regs.dst.linear) {
// If the input layout and the output layout are the same, just perform a raw copy.
ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
Memory::CopyBlock(dest_cpu, source_cpu,
src_bytes_per_pixel * regs.dst.width * regs.dst.height);
return;
}
if (regs.src.linear == regs.dst.linear) {
// If the input layout and the output layout are the same, just perform a raw copy.
ASSERT(regs.src.BlockHeight() == regs.dst.BlockHeight());
Memory::CopyBlock(dest_cpu, source_cpu,
src_bytes_per_pixel * regs.dst.width * regs.dst.height);
return;
}
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);
if (!regs.src.linear && regs.dst.linear) {
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel,
dst_bytes_per_pixel, src_buffer, dst_buffer, true,
regs.src.BlockHeight());
} else {
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel,
dst_bytes_per_pixel, dst_buffer, src_buffer, false,
regs.dst.BlockHeight());
}
u8* src_buffer = Memory::GetPointer(source_cpu);
u8* dst_buffer = Memory::GetPointer(dest_cpu);
if (!regs.src.linear && regs.dst.linear) {
// If the input is tiled and the output is linear, deswizzle the input and copy it over.
Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel,
dst_bytes_per_pixel, src_buffer, dst_buffer, true,
regs.src.BlockHeight());
} else {
// If the input is linear and the output is tiled, swizzle the input and copy it over.
Texture::CopySwizzledData(regs.src.width, regs.src.height, src_bytes_per_pixel,
dst_bytes_per_pixel, dst_buffer, src_buffer, false,
regs.dst.BlockHeight());
}
}

View File

@@ -12,10 +12,6 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace VideoCore {
class RasterizerInterface;
}
namespace Tegra::Engines {
#define FERMI2D_REG_INDEX(field_name) \
@@ -23,7 +19,7 @@ namespace Tegra::Engines {
class Fermi2D final {
public:
explicit Fermi2D(VideoCore::RasterizerInterface& rasterizer, MemoryManager& memory_manager);
explicit Fermi2D(MemoryManager& memory_manager);
~Fermi2D() = default;
/// Write the value to the register identified by method.
@@ -98,8 +94,6 @@ public:
MemoryManager& memory_manager;
private:
VideoCore::RasterizerInterface& rasterizer;
/// Performs the copy from the source surface to the destination surface as configured in the
/// registers.
void HandleSurfaceCopy();

View File

@@ -744,12 +744,6 @@ public:
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(end_addr_high) << 32) |
end_addr_low);
}
/// Adjust the index buffer offset so it points to the first desired index.
GPUVAddr IndexStart() const {
return StartAddress() + static_cast<size_t>(first) *
static_cast<size_t>(FormatSizeInBytes());
}
} index_array;
INSERT_PADDING_WORDS(0x7);

View File

@@ -314,15 +314,6 @@ enum class TextureMiscMode : u64 {
PTP,
};
enum class IsberdMode : u64 {
None = 0,
Patch = 1,
Prim = 2,
Attr = 3,
};
enum class IsberdShift : u64 { None = 0, U16 = 1, B32 = 2 };
enum class IpaInterpMode : u64 {
Linear = 0,
Perspective = 1,
@@ -349,87 +340,6 @@ struct IpaMode {
}
};
enum class SystemVariable : u64 {
LaneId = 0x00,
VirtCfg = 0x02,
VirtId = 0x03,
Pm0 = 0x04,
Pm1 = 0x05,
Pm2 = 0x06,
Pm3 = 0x07,
Pm4 = 0x08,
Pm5 = 0x09,
Pm6 = 0x0a,
Pm7 = 0x0b,
OrderingTicket = 0x0f,
PrimType = 0x10,
InvocationId = 0x11,
Ydirection = 0x12,
ThreadKill = 0x13,
ShaderType = 0x14,
DirectBeWriteAddressLow = 0x15,
DirectBeWriteAddressHigh = 0x16,
DirectBeWriteEnabled = 0x17,
MachineId0 = 0x18,
MachineId1 = 0x19,
MachineId2 = 0x1a,
MachineId3 = 0x1b,
Affinity = 0x1c,
InvocationInfo = 0x1d,
WscaleFactorXY = 0x1e,
WscaleFactorZ = 0x1f,
Tid = 0x20,
TidX = 0x21,
TidY = 0x22,
TidZ = 0x23,
CtaParam = 0x24,
CtaIdX = 0x25,
CtaIdY = 0x26,
CtaIdZ = 0x27,
NtId = 0x28,
CirQueueIncrMinusOne = 0x29,
Nlatc = 0x2a,
SmSpaVersion = 0x2c,
MultiPassShaderInfo = 0x2d,
LwinHi = 0x2e,
SwinHi = 0x2f,
SwinLo = 0x30,
SwinSz = 0x31,
SmemSz = 0x32,
SmemBanks = 0x33,
LwinLo = 0x34,
LwinSz = 0x35,
LmemLosz = 0x36,
LmemHioff = 0x37,
EqMask = 0x38,
LtMask = 0x39,
LeMask = 0x3a,
GtMask = 0x3b,
GeMask = 0x3c,
RegAlloc = 0x3d,
CtxAddr = 0x3e, // .fmask = F_SM50
BarrierAlloc = 0x3e, // .fmask = F_SM60
GlobalErrorStatus = 0x40,
WarpErrorStatus = 0x42,
WarpErrorStatusClear = 0x43,
PmHi0 = 0x48,
PmHi1 = 0x49,
PmHi2 = 0x4a,
PmHi3 = 0x4b,
PmHi4 = 0x4c,
PmHi5 = 0x4d,
PmHi6 = 0x4e,
PmHi7 = 0x4f,
ClockLo = 0x50,
ClockHi = 0x51,
GlobalTimerLo = 0x52,
GlobalTimerHi = 0x53,
HwTaskId = 0x60,
CircularQueueEntryIndex = 0x61,
CircularQueueEntryAddressLow = 0x62,
CircularQueueEntryAddressHigh = 0x63,
};
union Instruction {
Instruction& operator=(const Instruction& instr) {
value = instr.value;
@@ -1004,18 +914,6 @@ union Instruction {
}
} bra;
union {
BitField<39, 1, u64> emit; // EmitVertex
BitField<40, 1, u64> cut; // EndPrimitive
} out;
union {
BitField<31, 1, u64> skew;
BitField<32, 1, u64> o;
BitField<33, 2, IsberdMode> mode;
BitField<47, 2, IsberdShift> shift;
} isberd;
union {
BitField<20, 16, u64> imm20_16;
BitField<36, 1, u64> product_shift_left;
@@ -1038,10 +936,6 @@ union Instruction {
BitField<36, 5, u64> index;
} cbuf36;
// Unsure about the size of this one.
// It's always used with a gpr0, so any size should be fine.
BitField<20, 8, SystemVariable> sys20;
BitField<47, 1, u64> generates_cc;
BitField<61, 1, u64> is_b_imm;
BitField<60, 1, u64> is_b_gpr;
@@ -1081,8 +975,6 @@ public:
TMML, // Texture Mip Map Level
EXIT,
IPA,
OUT_R, // Emit vertex/primitive
ISBERD,
FFMA_IMM, // Fused Multiply and Add
FFMA_CR,
FFMA_RC,
@@ -1142,7 +1034,6 @@ public:
MOV_C,
MOV_R,
MOV_IMM,
MOV_SYS,
MOV32_IMM,
SHL_C,
SHL_R,
@@ -1318,8 +1209,6 @@ private:
INST("1101111101011---", Id::TMML, Type::Memory, "TMML"),
INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"),
INST("11100000--------", Id::IPA, Type::Trivial, "IPA"),
INST("1111101111100---", Id::OUT_R, Type::Trivial, "OUT_R"),
INST("1110111111010---", Id::ISBERD, Type::Trivial, "ISBERD"),
INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"),
INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"),
INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"),
@@ -1366,7 +1255,6 @@ private:
INST("0100110010011---", Id::MOV_C, Type::Arithmetic, "MOV_C"),
INST("0101110010011---", Id::MOV_R, Type::Arithmetic, "MOV_R"),
INST("0011100-10011---", Id::MOV_IMM, Type::Arithmetic, "MOV_IMM"),
INST("1111000011001---", Id::MOV_SYS, Type::Trivial, "MOV_SYS"),
INST("000000010000----", Id::MOV32_IMM, Type::ArithmeticImmediate, "MOV32_IMM"),
INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"),
INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"),

View File

@@ -25,7 +25,7 @@ u32 FramebufferConfig::BytesPerPixel(PixelFormat format) {
GPU::GPU(VideoCore::RasterizerInterface& rasterizer) {
memory_manager = std::make_unique<Tegra::MemoryManager>();
maxwell_3d = std::make_unique<Engines::Maxwell3D>(rasterizer, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(rasterizer, *memory_manager);
fermi_2d = std::make_unique<Engines::Fermi2D>(*memory_manager);
maxwell_compute = std::make_unique<Engines::MaxwellCompute>();
maxwell_dma = std::make_unique<Engines::MaxwellDMA>(*memory_manager);
kepler_memory = std::make_unique<Engines::KeplerMemory>(*memory_manager);

View File

@@ -5,7 +5,6 @@
#pragma once
#include "common/common_types.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
@@ -34,9 +33,13 @@ public:
/// and invalidated
virtual void FlushAndInvalidateRegion(VAddr addr, u64 size) = 0;
/// Attempt to use a faster method to perform a surface copy
virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 0
virtual bool AccelerateDisplayTransfer(const void* config) {
return false;
}
/// Attempt to use a faster method to perform a display transfer with is_texture_copy = 1
virtual bool AccelerateTextureCopy(const void* config) {
return false;
}

View File

@@ -34,7 +34,7 @@ GLintptr OGLBufferCache::UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size
}
AlignBuffer(alignment);
const GLintptr uploaded_offset = buffer_offset;
GLintptr uploaded_offset = buffer_offset;
Memory::ReadBlock(*cpu_addr, buffer_ptr, size);
@@ -57,23 +57,13 @@ GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t s
std::size_t alignment) {
AlignBuffer(alignment);
std::memcpy(buffer_ptr, raw_pointer, size);
const GLintptr uploaded_offset = buffer_offset;
GLintptr uploaded_offset = buffer_offset;
buffer_ptr += size;
buffer_offset += size;
return uploaded_offset;
}
std::tuple<u8*, GLintptr> OGLBufferCache::ReserveMemory(std::size_t size, std::size_t alignment) {
AlignBuffer(alignment);
u8* const uploaded_ptr = buffer_ptr;
const GLintptr uploaded_offset = buffer_offset;
buffer_ptr += size;
buffer_offset += size;
return std::make_tuple(uploaded_ptr, uploaded_offset);
}
void OGLBufferCache::Map(std::size_t max_size) {
bool invalidate;
std::tie(buffer_ptr, buffer_offset_base, invalidate) =
@@ -84,7 +74,6 @@ void OGLBufferCache::Map(std::size_t max_size) {
InvalidateAll();
}
}
void OGLBufferCache::Unmap() {
stream_buffer.Unmap(buffer_offset - buffer_offset_base);
}
@@ -95,7 +84,7 @@ GLuint OGLBufferCache::GetHandle() const {
void OGLBufferCache::AlignBuffer(std::size_t alignment) {
// Align the offset, not the mapped pointer
const GLintptr offset_aligned =
GLintptr offset_aligned =
static_cast<GLintptr>(Common::AlignUp(static_cast<std::size_t>(buffer_offset), alignment));
buffer_ptr += offset_aligned - buffer_offset;
buffer_offset = offset_aligned;

View File

@@ -6,7 +6,6 @@
#include <cstddef>
#include <memory>
#include <tuple>
#include "common/common_types.h"
#include "video_core/rasterizer_cache.h"
@@ -34,17 +33,11 @@ class OGLBufferCache final : public RasterizerCache<std::shared_ptr<CachedBuffer
public:
explicit OGLBufferCache(std::size_t size);
/// Uploads data from a guest GPU address. Returns host's buffer offset where it's been
/// allocated.
GLintptr UploadMemory(Tegra::GPUVAddr gpu_addr, std::size_t size, std::size_t alignment = 4,
bool cache = true);
/// Uploads from a host memory. Returns host's buffer offset where it's been allocated.
GLintptr UploadHostMemory(const void* raw_pointer, std::size_t size, std::size_t alignment = 4);
/// Reserves memory to be used by host's CPU. Returns mapped address and offset.
std::tuple<u8*, GLintptr> ReserveMemory(std::size_t size, std::size_t alignment = 4);
void Map(std::size_t max_size);
void Unmap();

View File

@@ -1,64 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <array>
#include "common/assert.h"
#include "common/common_types.h"
#include "core/memory.h"
#include "video_core/renderer_opengl/gl_buffer_cache.h"
#include "video_core/renderer_opengl/gl_primitive_assembler.h"
namespace OpenGL {
constexpr u32 TRIANGLES_PER_QUAD = 6;
constexpr std::array<u32, TRIANGLES_PER_QUAD> QUAD_MAP = {0, 1, 2, 0, 2, 3};
PrimitiveAssembler::PrimitiveAssembler(OGLBufferCache& buffer_cache) : buffer_cache(buffer_cache) {}
PrimitiveAssembler::~PrimitiveAssembler() = default;
std::size_t PrimitiveAssembler::CalculateQuadSize(u32 count) const {
ASSERT_MSG(count % 4 == 0, "Quad count is expected to be a multiple of 4");
return (count / 4) * TRIANGLES_PER_QUAD * sizeof(GLuint);
}
GLintptr PrimitiveAssembler::MakeQuadArray(u32 first, u32 count) {
const std::size_t size{CalculateQuadSize(count)};
auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(size);
for (u32 primitive = 0; primitive < count / 4; ++primitive) {
for (u32 i = 0; i < TRIANGLES_PER_QUAD; ++i) {
const u32 index = first + primitive * 4 + QUAD_MAP[i];
std::memcpy(dst_pointer, &index, sizeof(index));
dst_pointer += sizeof(index);
}
}
return index_offset;
}
GLintptr PrimitiveAssembler::MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size,
u32 count) {
const std::size_t map_size{CalculateQuadSize(count)};
auto [dst_pointer, index_offset] = buffer_cache.ReserveMemory(map_size);
auto& memory_manager = Core::System::GetInstance().GPU().MemoryManager();
const boost::optional<VAddr> cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
const u8* source{Memory::GetPointer(*cpu_addr)};
for (u32 primitive = 0; primitive < count / 4; ++primitive) {
for (std::size_t i = 0; i < TRIANGLES_PER_QUAD; ++i) {
const u32 index = primitive * 4 + QUAD_MAP[i];
const u8* src_offset = source + (index * index_size);
std::memcpy(dst_pointer, src_offset, index_size);
dst_pointer += index_size;
}
}
return index_offset;
}
} // namespace OpenGL

View File

@@ -1,33 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include <glad/glad.h>
#include "common/common_types.h"
#include "video_core/memory_manager.h"
namespace OpenGL {
class OGLBufferCache;
class PrimitiveAssembler {
public:
explicit PrimitiveAssembler(OGLBufferCache& buffer_cache);
~PrimitiveAssembler();
/// Calculates the size required by MakeQuadArray and MakeQuadIndexed.
std::size_t CalculateQuadSize(u32 count) const;
GLintptr MakeQuadArray(u32 first, u32 count);
GLintptr MakeQuadIndexed(Tegra::GPUVAddr gpu_addr, std::size_t index_size, u32 count);
private:
OGLBufferCache& buffer_cache;
};
} // namespace OpenGL

View File

@@ -42,41 +42,6 @@ MICROPROFILE_DEFINE(OpenGL_Framebuffer, "OpenGL", "Framebuffer Setup", MP_RGB(12
MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(128, 128, 192));
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
MICROPROFILE_DEFINE(OpenGL_PrimitiveAssembly, "OpenGL", "Prim Asmbl", MP_RGB(255, 100, 100));
struct DrawParameters {
GLenum primitive_mode;
GLsizei count;
GLint current_instance;
bool use_indexed;
GLint vertex_first;
GLenum index_format;
GLint base_vertex;
GLintptr index_buffer_offset;
void DispatchDraw() const {
if (use_indexed) {
const auto index_buffer_ptr = reinterpret_cast<const void*>(index_buffer_offset);
if (current_instance > 0) {
glDrawElementsInstancedBaseVertexBaseInstance(primitive_mode, count, index_format,
index_buffer_ptr, 1, base_vertex,
current_instance);
} else {
glDrawElementsBaseVertex(primitive_mode, count, index_format, index_buffer_ptr,
base_vertex);
}
} else {
if (current_instance > 0) {
glDrawArraysInstancedBaseInstance(primitive_mode, vertex_first, count, 1,
current_instance);
} else {
glDrawArrays(primitive_mode, vertex_first, count);
}
}
}
};
RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo& info)
: emu_window{window}, screen_info{info}, buffer_cache(STREAM_BUFFER_SIZE) {
@@ -207,55 +172,7 @@ void RasterizerOpenGL::SetupVertexArrays() {
}
}
DrawParameters RasterizerOpenGL::SetupDraw() {
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
const auto& regs = gpu.regs;
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
DrawParameters params{};
params.current_instance = gpu.state.current_instance;
if (regs.draw.topology == Maxwell::PrimitiveTopology::Quads) {
MICROPROFILE_SCOPE(OpenGL_PrimitiveAssembly);
params.use_indexed = true;
params.primitive_mode = GL_TRIANGLES;
if (is_indexed) {
params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
params.count = (regs.index_array.count / 4) * 6;
params.index_buffer_offset = primitive_assembler.MakeQuadIndexed(
regs.index_array.IndexStart(), regs.index_array.FormatSizeInBytes(),
regs.index_array.count);
params.base_vertex = static_cast<GLint>(regs.vb_element_base);
} else {
// MakeQuadArray always generates u32 indexes
params.index_format = GL_UNSIGNED_INT;
params.count = (regs.vertex_buffer.count / 4) * 6;
params.index_buffer_offset =
primitive_assembler.MakeQuadArray(regs.vertex_buffer.first, params.count);
}
return params;
}
params.use_indexed = is_indexed;
params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
if (is_indexed) {
MICROPROFILE_SCOPE(OpenGL_Index);
params.index_format = MaxwellToGL::IndexFormat(regs.index_array.format);
params.count = regs.index_array.count;
params.index_buffer_offset =
buffer_cache.UploadMemory(regs.index_array.IndexStart(), CalculateIndexBufferSize());
params.base_vertex = static_cast<GLint>(regs.vb_element_base);
} else {
params.count = regs.vertex_buffer.count;
params.vertex_first = regs.vertex_buffer.first;
}
return params;
}
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
void RasterizerOpenGL::SetupShaders() {
MICROPROFILE_SCOPE(OpenGL_Shader);
const auto& gpu = Core::System::GetInstance().GPU().Maxwell3D();
@@ -270,11 +187,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
// Skip stages that are not enabled
if (!gpu.regs.IsShaderConfigEnabled(index)) {
switch (program) {
case Maxwell::ShaderProgram::Geometry:
shader_program_manager->UseTrivialGeometryShader();
break;
}
continue;
}
@@ -293,18 +205,11 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
switch (program) {
case Maxwell::ShaderProgram::VertexA:
case Maxwell::ShaderProgram::VertexB: {
shader_program_manager->UseProgrammableVertexShader(
shader->GetProgramHandle(primitive_mode));
break;
}
case Maxwell::ShaderProgram::Geometry: {
shader_program_manager->UseProgrammableGeometryShader(
shader->GetProgramHandle(primitive_mode));
shader_program_manager->UseProgrammableVertexShader(shader->GetProgramHandle());
break;
}
case Maxwell::ShaderProgram::Fragment: {
shader_program_manager->UseProgrammableFragmentShader(
shader->GetProgramHandle(primitive_mode));
shader_program_manager->UseProgrammableFragmentShader(shader->GetProgramHandle());
break;
}
default:
@@ -314,13 +219,12 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
}
// Configure the const buffers for this shader stage.
current_constbuffer_bindpoint =
SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage), shader, primitive_mode,
current_constbuffer_bindpoint);
current_constbuffer_bindpoint = SetupConstBuffers(static_cast<Maxwell::ShaderStage>(stage),
shader, current_constbuffer_bindpoint);
// Configure the textures for this shader stage.
current_texture_bindpoint = SetupTextures(static_cast<Maxwell::ShaderStage>(stage), shader,
primitive_mode, current_texture_bindpoint);
current_texture_bindpoint);
// When VertexA is enabled, we have dual vertex shaders
if (program == Maxwell::ShaderProgram::VertexA) {
@@ -330,6 +234,8 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
}
state.Apply();
shader_program_manager->UseTrivialGeometryShader();
}
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
@@ -350,13 +256,6 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
return size;
}
std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs;
return static_cast<std::size_t>(regs.index_array.count) *
static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
}
bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) {
accelerate_draw = is_indexed ? AccelDraw::Indexed : AccelDraw::Arrays;
DrawArrays();
@@ -560,23 +459,16 @@ void RasterizerOpenGL::DrawArrays() {
// Draw the vertex batch
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
const u64 index_buffer_size{static_cast<u64>(regs.index_array.count) *
static_cast<u64>(regs.index_array.FormatSizeInBytes())};
state.draw.vertex_buffer = buffer_cache.GetHandle();
state.Apply();
std::size_t buffer_size = CalculateVertexArraysSize();
// Add space for index buffer (keeping in mind non-core primitives)
switch (regs.draw.topology) {
case Maxwell::PrimitiveTopology::Quads:
buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) +
primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count);
break;
default:
if (is_indexed) {
buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + CalculateIndexBufferSize();
}
break;
if (is_indexed) {
buffer_size = Common::AlignUp<std::size_t>(buffer_size, 4) + index_buffer_size;
}
// Uniform space for the 5 shader stages
@@ -590,16 +482,52 @@ void RasterizerOpenGL::DrawArrays() {
buffer_cache.Map(buffer_size);
SetupVertexArrays();
DrawParameters params = SetupDraw();
SetupShaders(params.primitive_mode);
// If indexed mode, copy the index buffer
GLintptr index_buffer_offset = 0;
if (is_indexed) {
MICROPROFILE_SCOPE(OpenGL_Index);
// Adjust the index buffer offset so it points to the first desired index.
auto index_start = regs.index_array.StartAddress();
index_start += static_cast<size_t>(regs.index_array.first) *
static_cast<size_t>(regs.index_array.FormatSizeInBytes());
index_buffer_offset = buffer_cache.UploadMemory(index_start, index_buffer_size);
}
SetupShaders();
buffer_cache.Unmap();
shader_program_manager->ApplyTo(state);
state.Apply();
// Execute draw call
params.DispatchDraw();
const GLenum primitive_mode{MaxwellToGL::PrimitiveTopology(regs.draw.topology)};
if (is_indexed) {
const GLint base_vertex{static_cast<GLint>(regs.vb_element_base)};
if (gpu.state.current_instance > 0) {
glDrawElementsInstancedBaseVertexBaseInstance(
primitive_mode, regs.index_array.count,
MaxwellToGL::IndexFormat(regs.index_array.format),
reinterpret_cast<const void*>(index_buffer_offset), 1, base_vertex,
gpu.state.current_instance);
} else {
glDrawElementsBaseVertex(primitive_mode, regs.index_array.count,
MaxwellToGL::IndexFormat(regs.index_array.format),
reinterpret_cast<const void*>(index_buffer_offset),
base_vertex);
}
} else {
if (gpu.state.current_instance > 0) {
glDrawArraysInstancedBaseInstance(primitive_mode, regs.vertex_buffer.first,
regs.vertex_buffer.count, 1,
gpu.state.current_instance);
} else {
glDrawArrays(primitive_mode, regs.vertex_buffer.first, regs.vertex_buffer.count);
}
}
// Disable scissor test
state.scissor.enabled = false;
@@ -628,10 +556,14 @@ void RasterizerOpenGL::FlushAndInvalidateRegion(VAddr addr, u64 size) {
InvalidateRegion(addr, size);
}
bool RasterizerOpenGL::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
const Tegra::Engines::Fermi2D::Regs::Surface& dst) {
bool RasterizerOpenGL::AccelerateDisplayTransfer(const void* config) {
MICROPROFILE_SCOPE(OpenGL_Blits);
res_cache.FermiCopySurface(src, dst);
UNREACHABLE();
return true;
}
bool RasterizerOpenGL::AccelerateTextureCopy(const void* config) {
UNREACHABLE();
return true;
}
@@ -669,13 +601,10 @@ void RasterizerOpenGL::SamplerInfo::Create() {
sampler.Create();
mag_filter = min_filter = Tegra::Texture::TextureFilter::Linear;
wrap_u = wrap_v = wrap_p = Tegra::Texture::WrapMode::Wrap;
uses_depth_compare = false;
depth_compare_func = Tegra::Texture::DepthCompareFunc::Never;
// default is GL_LINEAR_MIPMAP_LINEAR
glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Other attributes have correct defaults
glSamplerParameteri(sampler.handle, GL_TEXTURE_COMPARE_FUNC, GL_NEVER);
}
void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntry& config) {
@@ -703,21 +632,6 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
glSamplerParameteri(s, GL_TEXTURE_WRAP_R, MaxwellToGL::WrapMode(wrap_p));
}
if (uses_depth_compare != (config.depth_compare_enabled == 1)) {
uses_depth_compare = (config.depth_compare_enabled == 1);
if (uses_depth_compare) {
glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
} else {
glSamplerParameteri(s, GL_TEXTURE_COMPARE_MODE, GL_NONE);
}
}
if (depth_compare_func != config.depth_compare_func) {
depth_compare_func = config.depth_compare_func;
glSamplerParameteri(s, GL_TEXTURE_COMPARE_FUNC,
MaxwellToGL::DepthCompareFunc(depth_compare_func));
}
if (wrap_u == Tegra::Texture::WrapMode::Border || wrap_v == Tegra::Texture::WrapMode::Border ||
wrap_p == Tegra::Texture::WrapMode::Border) {
const GLvec4 new_border_color = {{config.border_color_r, config.border_color_g,
@@ -730,7 +644,7 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr
}
u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shader,
GLenum primitive_mode, u32 current_bindpoint) {
u32 current_bindpoint) {
MICROPROFILE_SCOPE(OpenGL_UBO);
const auto& gpu = Core::System::GetInstance().GPU();
const auto& maxwell3d = gpu.Maxwell3D();
@@ -782,7 +696,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
buffer.address, size, static_cast<std::size_t>(uniform_buffer_alignment));
// Now configure the bindpoint of the buffer inside the shader
glUniformBlockBinding(shader->GetProgramHandle(primitive_mode),
glUniformBlockBinding(shader->GetProgramHandle(),
shader->GetProgramResourceIndex(used_buffer),
current_bindpoint + bindpoint);
@@ -798,8 +712,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, Shader& shad
return current_bindpoint + static_cast<u32>(entries.size());
}
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
GLenum primitive_mode, u32 current_unit) {
u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader, u32 current_unit) {
MICROPROFILE_SCOPE(OpenGL_Texture);
const auto& gpu = Core::System::GetInstance().GPU();
const auto& maxwell3d = gpu.Maxwell3D();
@@ -814,8 +727,8 @@ u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, Shader& shader,
// Bind the uniform to the sampler.
glProgramUniform1i(shader->GetProgramHandle(primitive_mode),
shader->GetUniformLocation(entry), current_bindpoint);
glProgramUniform1i(shader->GetProgramHandle(), shader->GetUniformLocation(entry),
current_bindpoint);
const auto texture = maxwell3d.GetStageTexture(entry.GetStage(), entry.GetOffset());

View File

@@ -23,7 +23,6 @@
#include "video_core/rasterizer_cache.h"
#include "video_core/rasterizer_interface.h"
#include "video_core/renderer_opengl/gl_buffer_cache.h"
#include "video_core/renderer_opengl/gl_primitive_assembler.h"
#include "video_core/renderer_opengl/gl_rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
@@ -39,7 +38,6 @@ class EmuWindow;
namespace OpenGL {
struct ScreenInfo;
struct DrawParameters;
class RasterizerOpenGL : public VideoCore::RasterizerInterface {
public:
@@ -52,8 +50,8 @@ public:
void FlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override;
void FlushAndInvalidateRegion(VAddr addr, u64 size) override;
bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
const Tegra::Engines::Fermi2D::Regs::Surface& dst) override;
bool AccelerateDisplayTransfer(const void* config) override;
bool AccelerateTextureCopy(const void* config) override;
bool AccelerateFill(const void* config) override;
bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
u32 pixel_stride) override;
@@ -96,8 +94,6 @@ private:
Tegra::Texture::WrapMode wrap_u;
Tegra::Texture::WrapMode wrap_v;
Tegra::Texture::WrapMode wrap_p;
bool uses_depth_compare;
Tegra::Texture::DepthCompareFunc depth_compare_func;
GLvec4 border_color;
};
@@ -120,7 +116,7 @@ private:
* @returns The next available bindpoint for use in the next shader stage.
*/
u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
GLenum primitive_mode, u32 current_bindpoint);
u32 current_bindpoint);
/*
* Configures the current textures to use for the draw command.
@@ -130,7 +126,7 @@ private:
* @returns The next available bindpoint for use in the next shader stage.
*/
u32 SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, Shader& shader,
GLenum primitive_mode, u32 current_unit);
u32 current_unit);
/// Syncs the viewport to match the guest state
void SyncViewport();
@@ -196,18 +192,13 @@ private:
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
OGLBufferCache buffer_cache;
OGLFramebuffer framebuffer;
PrimitiveAssembler primitive_assembler{buffer_cache};
GLint uniform_buffer_alignment;
std::size_t CalculateVertexArraysSize() const;
std::size_t CalculateIndexBufferSize() const;
void SetupVertexArrays();
DrawParameters SetupDraw();
void SetupShaders(GLenum primitive_mode);
void SetupShaders();
enum class AccelDraw { Disabled, Arrays, Indexed };
AccelDraw accelerate_draw = AccelDraw::Disabled;

View File

@@ -143,28 +143,6 @@ static VAddr TryGetCpuAddr(Tegra::GPUVAddr gpu_addr) {
return params;
}
/*static*/ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
SurfaceParams params{};
params.addr = TryGetCpuAddr(config.Address());
params.is_tiled = !config.linear;
params.block_height = params.is_tiled ? config.BlockHeight() : 0,
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
params.component_type = ComponentTypeFromRenderTarget(config.format);
params.type = GetFormatType(params.pixel_format);
params.width = config.width;
params.height = config.height;
params.unaligned_height = config.height;
params.target = SurfaceTarget::Texture2D;
params.depth = 1;
params.size_in_bytes_total = params.SizeInBytesTotal();
params.size_in_bytes_2d = params.SizeInBytes2D();
params.max_mip_level = 0;
params.rt = {};
return params;
}
static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_format_tuples = {{
{GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, ComponentType::UNorm, false}, // ABGR8U
{GL_RGBA8, GL_RGBA, GL_BYTE, ComponentType::SNorm, false}, // ABGR8S
@@ -581,18 +559,6 @@ static bool BlitSurface(const Surface& src_surface, const Surface& dst_surface,
return true;
}
static void FastCopySurface(const Surface& src_surface, const Surface& dst_surface) {
const auto& src_params{src_surface->GetSurfaceParams()};
const auto& dst_params{dst_surface->GetSurfaceParams()};
const u32 width{std::min(src_params.width, dst_params.width)};
const u32 height{std::min(src_params.height, dst_params.height)};
glCopyImageSubData(src_surface->Texture().handle, SurfaceTargetToGL(src_params.target), 0, 0, 0,
0, dst_surface->Texture().handle, SurfaceTargetToGL(dst_params.target), 0, 0,
0, 0, width, height, 1);
}
static void CopySurface(const Surface& src_surface, const Surface& dst_surface,
GLuint copy_pbo_handle, GLenum src_attachment = 0,
GLenum dst_attachment = 0, std::size_t cubemap_face = 0) {
@@ -1067,26 +1033,6 @@ Surface RasterizerCacheOpenGL::GetUncachedSurface(const SurfaceParams& params) {
return surface;
}
void RasterizerCacheOpenGL::FermiCopySurface(
const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
const Tegra::Engines::Fermi2D::Regs::Surface& dst_config) {
const auto& src_params = SurfaceParams::CreateForFermiCopySurface(src_config);
const auto& dst_params = SurfaceParams::CreateForFermiCopySurface(dst_config);
ASSERT(src_params.width == dst_params.width);
ASSERT(src_params.height == dst_params.height);
ASSERT(src_params.pixel_format == dst_params.pixel_format);
ASSERT(src_params.block_height == dst_params.block_height);
ASSERT(src_params.is_tiled == dst_params.is_tiled);
ASSERT(src_params.depth == dst_params.depth);
ASSERT(src_params.depth == 1); // Currently, FastCopySurface only works with 2D surfaces
ASSERT(src_params.target == dst_params.target);
ASSERT(src_params.rt.index == dst_params.rt.index);
FastCopySurface(GetSurface(src_params, true), GetSurface(dst_params, false));
}
Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
const SurfaceParams& new_params) {
// Verify surface is compatible for blitting
@@ -1095,15 +1041,6 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& old_surface,
// Get a new surface with the new parameters, and blit the previous surface to it
Surface new_surface{GetUncachedSurface(new_params)};
// For compatible surfaces, we can just do fast glCopyImageSubData based copy
if (old_params.target == new_params.target && old_params.type == new_params.type &&
old_params.depth == new_params.depth && old_params.depth == 1 &&
SurfaceParams::GetFormatBpp(old_params.pixel_format) ==
SurfaceParams::GetFormatBpp(new_params.pixel_format)) {
FastCopySurface(old_surface, new_surface);
return new_surface;
}
// If the format is the same, just do a framebuffer blit. This is significantly faster than
// using PBOs. The is also likely less accurate, as textures will be converted rather than
// reinterpreted. When use_accurate_framebuffers setting is enabled, perform a more accurate

View File

@@ -13,7 +13,6 @@
#include "common/common_types.h"
#include "common/hash.h"
#include "common/math_util.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -720,10 +719,6 @@ struct SurfaceParams {
Tegra::GPUVAddr zeta_address,
Tegra::DepthFormat format);
/// Creates SurfaceParams for a Fermi2D surface copy
static SurfaceParams CreateForFermiCopySurface(
const Tegra::Engines::Fermi2D::Regs::Surface& config);
/// Checks if surfaces are compatible for caching
bool IsCompatibleSurface(const SurfaceParams& other) const {
return std::tie(pixel_format, type, width, height, target, depth) ==
@@ -842,10 +837,6 @@ public:
/// Tries to find a framebuffer using on the provided CPU address
Surface TryFindFramebufferSurface(VAddr addr) const;
/// Copies the contents of one surface to another
void FermiCopySurface(const Tegra::Engines::Fermi2D::Regs::Surface& src_config,
const Tegra::Engines::Fermi2D::Regs::Surface& dst_config);
private:
void LoadSurface(const Surface& surface);
Surface GetSurface(const SurfaceParams& params, bool preserve_contents = true);

View File

@@ -68,10 +68,6 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
program_result = GLShader::GenerateVertexShader(setup);
gl_type = GL_VERTEX_SHADER;
break;
case Maxwell::ShaderProgram::Geometry:
program_result = GLShader::GenerateGeometryShader(setup);
gl_type = GL_GEOMETRY_SHADER;
break;
case Maxwell::ShaderProgram::Fragment:
program_result = GLShader::GenerateFragmentShader(setup);
gl_type = GL_FRAGMENT_SHADER;
@@ -84,16 +80,11 @@ CachedShader::CachedShader(VAddr addr, Maxwell::ShaderProgram program_type)
entries = program_result.second;
if (program_type != Maxwell::ShaderProgram::Geometry) {
OGLShader shader;
shader.Create(program_result.first.c_str(), gl_type);
program.Create(true, shader.handle);
SetShaderUniformBlockBindings(program.handle);
VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
} else {
// Store shader's code to lazily build it on draw
geometry_programs.code = program_result.first;
}
OGLShader shader;
shader.Create(program_result.first.c_str(), gl_type);
program.Create(true, shader.handle);
SetShaderUniformBlockBindings(program.handle);
VideoCore::LabelGLObject(GL_PROGRAM, program.handle, addr);
}
GLuint CachedShader::GetProgramResourceIndex(const GLShader::ConstBufferEntry& buffer) {
@@ -119,21 +110,6 @@ GLint CachedShader::GetUniformLocation(const GLShader::SamplerEntry& sampler) {
return search->second;
}
GLuint CachedShader::LazyGeometryProgram(OGLProgram& target_program,
const std::string& glsl_topology,
const std::string& debug_name) {
if (target_program.handle != 0) {
return target_program.handle;
}
const std::string source{geometry_programs.code + "layout (" + glsl_topology + ") in;\n"};
OGLShader shader;
shader.Create(source.c_str(), GL_GEOMETRY_SHADER);
target_program.Create(true, shader.handle);
SetShaderUniformBlockBindings(target_program.handle);
VideoCore::LabelGLObject(GL_PROGRAM, target_program.handle, addr, debug_name);
return target_program.handle;
};
Shader ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program) {
const VAddr program_addr{GetShaderAddress(program)};

View File

@@ -7,7 +7,6 @@
#include <map>
#include <memory>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/rasterizer_cache.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -39,31 +38,8 @@ public:
}
/// Gets the GL program handle for the shader
GLuint GetProgramHandle(GLenum primitive_mode) {
if (program_type != Maxwell::ShaderProgram::Geometry) {
return program.handle;
}
switch (primitive_mode) {
case GL_POINTS:
return LazyGeometryProgram(geometry_programs.points, "points", "ShaderPoints");
case GL_LINES:
case GL_LINE_STRIP:
return LazyGeometryProgram(geometry_programs.lines, "lines", "ShaderLines");
case GL_LINES_ADJACENCY:
case GL_LINE_STRIP_ADJACENCY:
return LazyGeometryProgram(geometry_programs.lines_adjacency, "lines_adjacency",
"ShaderLinesAdjacency");
case GL_TRIANGLES:
case GL_TRIANGLE_STRIP:
case GL_TRIANGLE_FAN:
return LazyGeometryProgram(geometry_programs.triangles, "triangles", "ShaderTriangles");
case GL_TRIANGLES_ADJACENCY:
case GL_TRIANGLE_STRIP_ADJACENCY:
return LazyGeometryProgram(geometry_programs.triangles_adjacency, "triangles_adjacency",
"ShaderLines");
default:
UNREACHABLE_MSG("Unknown primitive mode.");
}
GLuint GetProgramHandle() const {
return program.handle;
}
/// Gets the GL program resource location for the specified resource, caching as needed
@@ -73,30 +49,12 @@ public:
GLint GetUniformLocation(const GLShader::SamplerEntry& sampler);
private:
/// Generates a geometry shader or returns one that already exists.
GLuint LazyGeometryProgram(OGLProgram& target_program, const std::string& glsl_topology,
const std::string& debug_name);
VAddr addr;
Maxwell::ShaderProgram program_type;
GLShader::ShaderSetup setup;
GLShader::ShaderEntries entries;
// Non-geometry program.
OGLProgram program;
// Geometry programs. These are needed because GLSL needs an input topology but it's not
// declared by the hardware. Workaround this issue by generating a different shader per input
// topology class.
struct {
std::string code;
OGLProgram points;
OGLProgram lines;
OGLProgram lines_adjacency;
OGLProgram triangles;
OGLProgram triangles_adjacency;
} geometry_programs;
std::map<u32, GLuint> resource_cache;
std::map<u32, GLint> uniform_cache;
};

View File

@@ -7,7 +7,6 @@
#include <string>
#include <string_view>
#include <boost/optional.hpp>
#include <fmt/format.h>
#include "common/assert.h"
@@ -30,32 +29,11 @@ using Tegra::Shader::SubOp;
constexpr u32 PROGRAM_END = MAX_PROGRAM_CODE_LENGTH;
constexpr u32 PROGRAM_HEADER_SIZE = sizeof(Tegra::Shader::Header);
enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
constexpr u32 MAX_GEOMETRY_BUFFERS = 6;
constexpr u32 MAX_ATTRIBUTES = 0x100; // Size in vec4s, this value is untested
class DecompileFail : public std::runtime_error {
public:
using std::runtime_error::runtime_error;
};
/// Translate topology
static std::string GetTopologyName(Tegra::Shader::OutputTopology topology) {
switch (topology) {
case Tegra::Shader::OutputTopology::PointList:
return "points";
case Tegra::Shader::OutputTopology::LineStrip:
return "line_strip";
case Tegra::Shader::OutputTopology::TriangleStrip:
return "triangle_strip";
default:
LOG_CRITICAL(Render_OpenGL, "Unknown output topology {}", static_cast<u32>(topology));
UNREACHABLE();
return "points";
}
}
/// Describes the behaviour of code path of a given entry point and a return point.
enum class ExitMethod {
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
@@ -275,9 +253,8 @@ enum class InternalFlag : u64 {
class GLSLRegisterManager {
public:
GLSLRegisterManager(ShaderWriter& shader, ShaderWriter& declarations,
const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix,
const Tegra::Shader::Header& header)
: shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix}, header{header} {
const Maxwell3D::Regs::ShaderStage& stage, const std::string& suffix)
: shader{shader}, declarations{declarations}, stage{stage}, suffix{suffix} {
BuildRegisterList();
BuildInputList();
}
@@ -381,13 +358,11 @@ public:
* @param reg The destination register to use.
* @param elem The element to use for the operation.
* @param attribute The input attribute to use as the source value.
* @param vertex The register that decides which vertex to read from (used in GS).
*/
void SetRegisterToInputAttibute(const Register& reg, u64 elem, Attribute::Index attribute,
const Tegra::Shader::IpaMode& input_mode,
boost::optional<Register> vertex = {}) {
const Tegra::Shader::IpaMode& input_mode) {
const std::string dest = GetRegisterAsFloat(reg);
const std::string src = GetInputAttribute(attribute, input_mode, vertex) + GetSwizzle(elem);
const std::string src = GetInputAttribute(attribute, input_mode) + GetSwizzle(elem);
shader.AddLine(dest + " = " + src + ';');
}
@@ -416,29 +391,16 @@ public:
* are stored as floats, so this may require conversion.
* @param attribute The destination output attribute.
* @param elem The element to use for the operation.
* @param val_reg The register to use as the source value.
* @param buf_reg The register that tells which buffer to write to (used in geometry shaders).
* @param reg The register to use as the source value.
*/
void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& val_reg,
const Register& buf_reg) {
void SetOutputAttributeToRegister(Attribute::Index attribute, u64 elem, const Register& reg) {
const std::string dest = GetOutputAttribute(attribute);
const std::string src = GetRegisterAsFloat(val_reg);
const std::string src = GetRegisterAsFloat(reg);
if (!dest.empty()) {
// Can happen with unknown/unimplemented output attributes, in which case we ignore the
// instruction for now.
if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
// TODO(Rodrigo): nouveau sets some attributes after setting emitting a geometry
// shader. These instructions use a dirty register as buffer index. To avoid some
// drivers from complaining for the out of boundary writes, guard them.
const std::string buf_index{"min(" + GetRegisterAsInteger(buf_reg) + ", " +
std::to_string(MAX_GEOMETRY_BUFFERS - 1) + ')'};
shader.AddLine("amem[" + buf_index + "][" +
std::to_string(static_cast<u32>(attribute)) + ']' +
GetSwizzle(elem) + " = " + src + ';');
} else {
shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
}
shader.AddLine(dest + GetSwizzle(elem) + " = " + src + ';');
}
}
@@ -479,123 +441,41 @@ public:
}
}
/// Add declarations.
/// Add declarations for registers
void GenerateDeclarations(const std::string& suffix) {
GenerateRegisters(suffix);
GenerateInternalFlags();
GenerateInputAttrs();
GenerateOutputAttrs();
GenerateConstBuffers();
GenerateSamplers();
GenerateGeometry();
}
/// Returns a list of constant buffer declarations.
std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
std::vector<ConstBufferEntry> result;
std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
return result;
}
/// Returns a list of samplers used in the shader.
const std::vector<SamplerEntry>& GetSamplers() const {
return used_samplers;
}
/// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
/// necessary.
std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
bool is_array, bool is_shadow) {
const auto offset = static_cast<std::size_t>(sampler.index.Value());
// If this sampler has already been used, return the existing mapping.
const auto itr =
std::find_if(used_samplers.begin(), used_samplers.end(),
[&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
if (itr != used_samplers.end()) {
ASSERT(itr->GetType() == type && itr->IsArray() == is_array &&
itr->IsShadow() == is_shadow);
return itr->GetName();
}
// Otherwise create a new mapping for this sampler
const std::size_t next_index = used_samplers.size();
const SamplerEntry entry{stage, offset, next_index, type, is_array, is_shadow};
used_samplers.emplace_back(entry);
return entry.GetName();
}
private:
/// Generates declarations for registers.
void GenerateRegisters(const std::string& suffix) {
for (const auto& reg : regs) {
declarations.AddLine(GLSLRegister::GetTypeString() + ' ' + reg.GetPrefixString() +
std::to_string(reg.GetIndex()) + '_' + suffix + " = 0;");
}
declarations.AddNewLine();
}
/// Generates declarations for internal flags.
void GenerateInternalFlags() {
for (u32 ii = 0; ii < static_cast<u64>(InternalFlag::Amount); ii++) {
const InternalFlag code = static_cast<InternalFlag>(ii);
declarations.AddLine("bool " + GetInternalFlag(code) + " = false;");
}
declarations.AddNewLine();
}
/// Generates declarations for input attributes.
void GenerateInputAttrs() {
if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
const std::string attr =
stage == Maxwell3D::Regs::ShaderStage::Geometry ? "gs_position[]" : "position";
declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
") in vec4 " + attr + ';');
}
for (const auto element : declr_input_attribute) {
// TODO(bunnei): Use proper number of elements for these
u32 idx =
static_cast<u32>(element.first) - static_cast<u32>(Attribute::Index::Attribute_0);
if (stage != Maxwell3D::Regs::ShaderStage::Vertex) {
// If inputs are varyings, add an offset
idx += GENERIC_VARYING_START_LOCATION;
}
std::string attr{GetInputAttribute(element.first, element.second)};
if (stage == Maxwell3D::Regs::ShaderStage::Geometry) {
attr = "gs_" + attr + "[]";
}
declarations.AddLine("layout (location = " + std::to_string(idx) + ") " +
GetInputFlags(element.first) + "in vec4 " + attr + ';');
declarations.AddLine("layout(location = " + std::to_string(idx) + ")" +
GetInputFlags(element.first) + "in vec4 " +
GetInputAttribute(element.first, element.second) + ';');
}
declarations.AddNewLine();
}
/// Generates declarations for output attributes.
void GenerateOutputAttrs() {
if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
declarations.AddLine("layout (location = " + std::to_string(POSITION_VARYING_LOCATION) +
") out vec4 position;");
}
for (const auto& index : declr_output_attribute) {
// TODO(bunnei): Use proper number of elements for these
const u32 idx = static_cast<u32>(index) -
static_cast<u32>(Attribute::Index::Attribute_0) +
GENERIC_VARYING_START_LOCATION;
declarations.AddLine("layout (location = " + std::to_string(idx) + ") out vec4 " +
GetOutputAttribute(index) + ';');
declarations.AddLine("layout(location = " +
std::to_string(static_cast<u32>(index) -
static_cast<u32>(Attribute::Index::Attribute_0)) +
") out vec4 " + GetOutputAttribute(index) + ';');
}
declarations.AddNewLine();
}
/// Generates declarations for constant buffers.
void GenerateConstBuffers() {
for (const auto& entry : GetConstBuffersDeclarations()) {
declarations.AddLine("layout (std140) uniform " + entry.GetName());
declarations.AddLine("layout(std140) uniform " + entry.GetName());
declarations.AddLine('{');
declarations.AddLine(" vec4 c" + std::to_string(entry.GetIndex()) +
"[MAX_CONSTBUFFER_ELEMENTS];");
@@ -603,10 +483,7 @@ private:
declarations.AddNewLine();
}
declarations.AddNewLine();
}
/// Generates declarations for samplers.
void GenerateSamplers() {
const auto& samplers = GetSamplers();
for (const auto& sampler : samplers) {
declarations.AddLine("uniform " + sampler.GetTypeString() + ' ' + sampler.GetName() +
@@ -615,42 +492,43 @@ private:
declarations.AddNewLine();
}
/// Generates declarations used for geometry shaders.
void GenerateGeometry() {
if (stage != Maxwell3D::Regs::ShaderStage::Geometry)
return;
declarations.AddLine(
"layout (" + GetTopologyName(header.common3.output_topology) +
", max_vertices = " + std::to_string(header.common4.max_output_vertices) + ") out;");
declarations.AddNewLine();
declarations.AddLine("vec4 amem[" + std::to_string(MAX_GEOMETRY_BUFFERS) + "][" +
std::to_string(MAX_ATTRIBUTES) + "];");
declarations.AddNewLine();
constexpr char buffer[] = "amem[output_buffer]";
declarations.AddLine("void emit_vertex(uint output_buffer) {");
++declarations.scope;
for (const auto element : declr_output_attribute) {
declarations.AddLine(GetOutputAttribute(element) + " = " + buffer + '[' +
std::to_string(static_cast<u32>(element)) + "];");
}
declarations.AddLine("position = " + std::string(buffer) + '[' +
std::to_string(static_cast<u32>(Attribute::Index::Position)) + "];");
// If a geometry shader is attached, it will always flip (it's the last stage before
// fragment). For more info about flipping, refer to gl_shader_gen.cpp.
declarations.AddLine("position.xy *= viewport_flip.xy;");
declarations.AddLine("gl_Position = position;");
declarations.AddLine("position.w = 1.0;");
declarations.AddLine("EmitVertex();");
--declarations.scope;
declarations.AddLine('}');
declarations.AddNewLine();
/// Returns a list of constant buffer declarations
std::vector<ConstBufferEntry> GetConstBuffersDeclarations() const {
std::vector<ConstBufferEntry> result;
std::copy_if(declr_const_buffers.begin(), declr_const_buffers.end(),
std::back_inserter(result), [](const auto& entry) { return entry.IsUsed(); });
return result;
}
/// Returns a list of samplers used in the shader
const std::vector<SamplerEntry>& GetSamplers() const {
return used_samplers;
}
/// Returns the GLSL sampler used for the input shader sampler, and creates a new one if
/// necessary.
std::string AccessSampler(const Sampler& sampler, Tegra::Shader::TextureType type,
bool is_array) {
const std::size_t offset = static_cast<std::size_t>(sampler.index.Value());
// If this sampler has already been used, return the existing mapping.
const auto itr =
std::find_if(used_samplers.begin(), used_samplers.end(),
[&](const SamplerEntry& entry) { return entry.GetOffset() == offset; });
if (itr != used_samplers.end()) {
ASSERT(itr->GetType() == type && itr->IsArray() == is_array);
return itr->GetName();
}
// Otherwise create a new mapping for this sampler
const std::size_t next_index = used_samplers.size();
const SamplerEntry entry{stage, offset, next_index, type, is_array};
used_samplers.emplace_back(entry);
return entry.GetName();
}
private:
/// Generates code representing a temporary (GPR) register.
std::string GetRegister(const Register& reg, unsigned elem) {
if (reg == Register::ZeroIndex) {
@@ -707,19 +585,11 @@ private:
/// Generates code representing an input attribute register.
std::string GetInputAttribute(Attribute::Index attribute,
const Tegra::Shader::IpaMode& input_mode,
boost::optional<Register> vertex = {}) {
auto GeometryPass = [&](const std::string& name) {
if (stage == Maxwell3D::Regs::ShaderStage::Geometry && vertex) {
return "gs_" + name + '[' + GetRegisterAsInteger(vertex.value(), 0, false) + ']';
}
return name;
};
const Tegra::Shader::IpaMode& input_mode) {
switch (attribute) {
case Attribute::Index::Position:
if (stage != Maxwell3D::Regs::ShaderStage::Fragment) {
return GeometryPass("position");
return "position";
} else {
return "vec4(gl_FragCoord.x, gl_FragCoord.y, gl_FragCoord.z, 1.0)";
}
@@ -748,7 +618,7 @@ private:
UNREACHABLE();
}
}
return GeometryPass("input_attribute_" + std::to_string(index));
return "input_attribute_" + std::to_string(index);
}
LOG_CRITICAL(HW_GPU, "Unhandled input attribute: {}", static_cast<u32>(attribute));
@@ -801,7 +671,7 @@ private:
return out;
}
/// Generates code representing the declaration name of an output attribute register.
/// Generates code representing an output attribute register.
std::string GetOutputAttribute(Attribute::Index attribute) {
switch (attribute) {
case Attribute::Index::Position:
@@ -837,7 +707,6 @@ private:
std::vector<SamplerEntry> used_samplers;
const Maxwell3D::Regs::ShaderStage& stage;
const std::string& suffix;
const Tegra::Shader::Header& header;
};
class GLSLGenerator {
@@ -878,9 +747,8 @@ private:
}
/// Generates code representing a texture sampler.
std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array,
bool is_shadow) {
return regs.AccessSampler(sampler, type, is_array, is_shadow);
std::string GetSampler(const Sampler& sampler, Tegra::Shader::TextureType type, bool is_array) {
return regs.AccessSampler(sampler, type, is_array);
}
/**
@@ -1134,24 +1002,6 @@ private:
shader.AddLine('}');
}
static u32 TextureCoordinates(Tegra::Shader::TextureType texture_type) {
switch (texture_type) {
case Tegra::Shader::TextureType::Texture1D: {
return 1;
}
case Tegra::Shader::TextureType::Texture2D: {
return 2;
}
case Tegra::Shader::TextureType::TextureCube: {
return 3;
}
default:
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}", static_cast<u32>(texture_type));
UNREACHABLE();
return 0;
}
}
/*
* Emits code to push the input target address to the SSY address stack, incrementing the stack
* top.
@@ -1233,8 +1083,8 @@ private:
return offset + 1;
}
shader.AddLine(
fmt::format("// {}: {} (0x{:016x})", offset, opcode->GetName(), instr.value));
shader.AddLine("// " + std::to_string(offset) + ": " + opcode->GetName() + " (" +
std::to_string(instr.value) + ')');
using Tegra::Shader::Pred;
ASSERT_MSG(instr.pred.full_pred != Pred::NeverExecute,
@@ -1956,7 +1806,7 @@ private:
const auto LoadNextElement = [&](u32 reg_offset) {
regs.SetRegisterToInputAttibute(instr.gpr0.Value() + reg_offset, next_element,
static_cast<Attribute::Index>(next_index),
input_mode, instr.gpr39.Value());
input_mode);
// Load the next attribute element into the following register. If the element
// to load goes beyond the vec4 size, load the first element of the next
@@ -2020,8 +1870,8 @@ private:
const auto StoreNextElement = [&](u32 reg_offset) {
regs.SetOutputAttributeToRegister(static_cast<Attribute::Index>(next_index),
next_element, instr.gpr0.Value() + reg_offset,
instr.gpr39.Value());
next_element,
instr.gpr0.Value() + reg_offset);
// Load the next attribute element into the following register. If the element
// to load goes beyond the vec4 size, load the first element of the next
@@ -2046,35 +1896,24 @@ private:
"NODEP is not implemented");
ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
"AOFFI is not implemented");
ASSERT_MSG(!instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
"DC is not implemented");
const bool depth_compare =
instr.tex.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
u32 num_coordinates = TextureCoordinates(texture_type);
if (depth_compare)
num_coordinates += 1;
switch (num_coordinates) {
case 1: {
switch (texture_type) {
case Tegra::Shader::TextureType::Texture1D: {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
coord = "float coords = " + x + ';';
break;
}
case 2: {
case Tegra::Shader::TextureType::Texture2D: {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
break;
}
case 3: {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
break;
}
default:
LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}",
static_cast<u32>(num_coordinates));
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
static_cast<u32>(texture_type));
UNREACHABLE();
// Fallback to interpreting as a 2D texture for now
@@ -2085,10 +1924,9 @@ private:
}
// TODO: make sure coordinates are always indexed to gpr8 and gpr20 is always bias
// or lod.
std::string op_c;
const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20);
const std::string sampler =
GetSampler(instr.sampler, texture_type, false, depth_compare);
const std::string sampler = GetSampler(instr.sampler, texture_type, false);
// Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction.
@@ -2097,7 +1935,7 @@ private:
shader.AddLine(coord);
std::string texture;
switch (instr.tex.GetTextureProcessMode()) {
switch (instr.tex.process_mode) {
case Tegra::Shader::TextureProcessMode::None: {
texture = "texture(" + sampler + ", coords)";
break;
@@ -2108,22 +1946,12 @@ private:
}
case Tegra::Shader::TextureProcessMode::LB:
case Tegra::Shader::TextureProcessMode::LBA: {
if (num_coordinates <= 2) {
op_c = regs.GetRegisterAsFloat(instr.gpr20);
} else {
op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
}
// TODO: Figure if A suffix changes the equation at all.
texture = "texture(" + sampler + ", coords, " + op_c + ')';
break;
}
case Tegra::Shader::TextureProcessMode::LL:
case Tegra::Shader::TextureProcessMode::LLA: {
if (num_coordinates <= 2) {
op_c = regs.GetRegisterAsFloat(instr.gpr20);
} else {
op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
}
// TODO: Figure if A suffix changes the equation at all.
texture = "textureLod(" + sampler + ", coords, " + op_c + ')';
break;
@@ -2131,22 +1959,18 @@ private:
default: {
texture = "texture(" + sampler + ", coords)";
LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}",
static_cast<u32>(instr.tex.GetTextureProcessMode()));
static_cast<u32>(instr.tex.process_mode.Value()));
UNREACHABLE();
}
}
if (!depth_compare) {
std::size_t dest_elem{};
for (std::size_t elem = 0; elem < 4; ++elem) {
if (!instr.tex.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
++dest_elem;
std::size_t dest_elem{};
for (std::size_t elem = 0; elem < 4; ++elem) {
if (!instr.tex.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
} else {
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
++dest_elem;
}
--shader.scope;
shader.AddLine("}");
@@ -2159,15 +1983,11 @@ private:
ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::NODEP),
"NODEP is not implemented");
ASSERT_MSG(!instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
"DC is not implemented");
const bool depth_compare =
instr.texs.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
u32 num_coordinates = TextureCoordinates(texture_type);
if (depth_compare)
num_coordinates += 1;
switch (num_coordinates) {
case 2: {
switch (texture_type) {
case Tegra::Shader::TextureType::Texture2D: {
if (is_array) {
const std::string index = regs.GetRegisterAsInteger(instr.gpr8);
const std::string x = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
@@ -2180,25 +2000,17 @@ private:
}
break;
}
case 3: {
if (is_array) {
UNIMPLEMENTED_MSG("3-coordinate arrays not fully implemented");
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr20);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
texture_type = Tegra::Shader::TextureType::Texture2D;
is_array = false;
} else {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
const std::string z = regs.GetRegisterAsFloat(instr.gpr20);
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
}
case Tegra::Shader::TextureType::TextureCube: {
ASSERT_MSG(!is_array, "Unimplemented");
std::string x = regs.GetRegisterAsFloat(instr.gpr8);
std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
std::string z = regs.GetRegisterAsFloat(instr.gpr20);
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
break;
}
default:
LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}",
static_cast<u32>(num_coordinates));
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
static_cast<u32>(texture_type));
UNREACHABLE();
// Fallback to interpreting as a 2D texture for now
@@ -2208,35 +2020,9 @@ private:
texture_type = Tegra::Shader::TextureType::Texture2D;
is_array = false;
}
const std::string sampler =
GetSampler(instr.sampler, texture_type, is_array, depth_compare);
std::string texture;
switch (instr.texs.GetTextureProcessMode()) {
case Tegra::Shader::TextureProcessMode::None: {
texture = "texture(" + sampler + ", coords)";
break;
}
case Tegra::Shader::TextureProcessMode::LZ: {
texture = "textureLod(" + sampler + ", coords, 0.0)";
break;
}
case Tegra::Shader::TextureProcessMode::LL: {
const std::string op_c = regs.GetRegisterAsFloat(instr.gpr20.Value() + 1);
texture = "textureLod(" + sampler + ", coords, " + op_c + ')';
break;
}
default: {
texture = "texture(" + sampler + ", coords)";
LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}",
static_cast<u32>(instr.texs.GetTextureProcessMode()));
UNREACHABLE();
}
}
if (!depth_compare) {
WriteTexsInstruction(instr, coord, texture);
} else {
WriteTexsInstruction(instr, coord, "vec4(" + texture + ')');
}
const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
const std::string texture = "texture(" + sampler + ", coords)";
WriteTexsInstruction(instr, coord, texture);
break;
}
case OpCode::Id::TLDS: {
@@ -2276,26 +2062,9 @@ private:
static_cast<u32>(texture_type));
UNREACHABLE();
}
const std::string sampler =
GetSampler(instr.sampler, texture_type, is_array, false);
std::string texture = "texelFetch(" + sampler + ", coords, 0)";
const std::string op_c = regs.GetRegisterAsInteger(instr.gpr20.Value() + 1);
switch (instr.tlds.GetTextureProcessMode()) {
case Tegra::Shader::TextureProcessMode::LZ: {
texture = "texelFetch(" + sampler + ", coords, 0)";
break;
}
case Tegra::Shader::TextureProcessMode::LL: {
texture = "texelFetch(" + sampler + ", coords, " + op_c + ')';
break;
}
default: {
texture = "texelFetch(" + sampler + ", coords, 0)";
LOG_CRITICAL(HW_GPU, "Unhandled texture process mode {}",
static_cast<u32>(instr.tlds.GetTextureProcessMode()));
UNREACHABLE();
}
}
const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
const std::string texture = "texelFetch(" + sampler + ", coords, 0)";
WriteTexsInstruction(instr, coord, texture);
break;
}
@@ -2308,43 +2077,28 @@ private:
"NODEP is not implemented");
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
"AOFFI is not implemented");
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
"DC is not implemented");
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::NDV),
"NDV is not implemented");
ASSERT_MSG(!instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::PTP),
"PTP is not implemented");
const bool depth_compare =
instr.tld4.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
auto texture_type = instr.tld4.texture_type.Value();
u32 num_coordinates = TextureCoordinates(texture_type);
if (depth_compare)
num_coordinates += 1;
switch (num_coordinates) {
case 2: {
switch (instr.tld4.texture_type) {
case Tegra::Shader::TextureType::Texture2D: {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
break;
}
case 3: {
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
const std::string z = regs.GetRegisterAsFloat(instr.gpr8.Value() + 2);
coord = "vec3 coords = vec3(" + x + ", " + y + ", " + z + ");";
break;
}
default:
LOG_CRITICAL(HW_GPU, "Unhandled coordinates number {}",
static_cast<u32>(num_coordinates));
LOG_CRITICAL(HW_GPU, "Unhandled texture type {}",
static_cast<u32>(instr.tld4.texture_type.Value()));
UNREACHABLE();
const std::string x = regs.GetRegisterAsFloat(instr.gpr8);
const std::string y = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec2 coords = vec2(" + x + ", " + y + ");";
texture_type = Tegra::Shader::TextureType::Texture2D;
}
const std::string sampler =
GetSampler(instr.sampler, texture_type, false, depth_compare);
GetSampler(instr.sampler, instr.tld4.texture_type, false);
// Add an extra scope and declare the texture coords inside to prevent
// overwriting them in case they are used as outputs of the texs instruction.
shader.AddLine("{");
@@ -2352,18 +2106,15 @@ private:
shader.AddLine(coord);
const std::string texture = "textureGather(" + sampler + ", coords, " +
std::to_string(instr.tld4.component) + ')';
if (!depth_compare) {
std::size_t dest_elem{};
for (std::size_t elem = 0; elem < 4; ++elem) {
if (!instr.tex.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
++dest_elem;
std::size_t dest_elem{};
for (std::size_t elem = 0; elem < 4; ++elem) {
if (!instr.tex.IsComponentEnabled(elem)) {
// Skip disabled components
continue;
}
} else {
regs.SetRegisterToFloat(instr.gpr0, 0, texture, 1, 1, false);
regs.SetRegisterToFloat(instr.gpr0, elem, texture, 1, 4, false, dest_elem);
++dest_elem;
}
--shader.scope;
shader.AddLine("}");
@@ -2374,30 +2125,18 @@ private:
"NODEP is not implemented");
ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::AOFFI),
"AOFFI is not implemented");
ASSERT_MSG(!instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC),
"DC is not implemented");
const bool depth_compare =
instr.tld4s.UsesMiscMode(Tegra::Shader::TextureMiscMode::DC);
const std::string op_a = regs.GetRegisterAsFloat(instr.gpr8);
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr20);
// TODO(Subv): Figure out how the sampler type is encoded in the TLD4S instruction.
const std::string sampler = GetSampler(
instr.sampler, Tegra::Shader::TextureType::Texture2D, false, depth_compare);
std::string coord;
if (!depth_compare) {
coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
} else {
// Note: TLD4S coordinate encoding works just like TEXS's
const std::string op_c = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
coord = "vec3 coords = vec3(" + op_a + ", " + op_c + ", " + op_b + ");";
}
const std::string sampler =
GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
const std::string coord = "vec2 coords = vec2(" + op_a + ", " + op_b + ");";
const std::string texture = "textureGather(" + sampler + ", coords, " +
std::to_string(instr.tld4s.component) + ')';
if (!depth_compare) {
WriteTexsInstruction(instr, coord, texture);
} else {
WriteTexsInstruction(instr, coord, "vec4(" + texture + ')');
}
WriteTexsInstruction(instr, coord, texture);
break;
}
case OpCode::Id::TXQ: {
@@ -2408,7 +2147,7 @@ private:
// Sadly, not all texture instructions specify the type of texture their sampler
// uses. This must be fixed at a later instance.
const std::string sampler =
GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false, false);
GetSampler(instr.sampler, Tegra::Shader::TextureType::Texture2D, false);
switch (instr.txq.query_type) {
case Tegra::Shader::TextureQueryType::Dimension: {
const std::string texture = "textureQueryLevels(" + sampler + ')';
@@ -2433,8 +2172,7 @@ private:
const std::string op_b = regs.GetRegisterAsFloat(instr.gpr8.Value() + 1);
const bool is_array = instr.tmml.array != 0;
auto texture_type = instr.tmml.texture_type.Value();
const std::string sampler =
GetSampler(instr.sampler, texture_type, is_array, false);
const std::string sampler = GetSampler(instr.sampler, texture_type, is_array);
// TODO: add coordinates for different samplers once other texture types are
// implemented.
@@ -2868,52 +2606,6 @@ private:
break;
}
case OpCode::Id::OUT_R: {
ASSERT(instr.gpr20.Value() == Register::ZeroIndex);
ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
"OUT is expected to be used in a geometry shader.");
if (instr.out.emit) {
// gpr0 is used to store the next address. Hardware returns a pointer but
// we just return the next index with a cyclic cap.
const std::string current{regs.GetRegisterAsInteger(instr.gpr8, 0, false)};
const std::string next = "((" + current + " + 1" + ") % " +
std::to_string(MAX_GEOMETRY_BUFFERS) + ')';
shader.AddLine("emit_vertex(" + current + ");");
regs.SetRegisterToInteger(instr.gpr0, false, 0, next, 1, 1);
}
if (instr.out.cut) {
shader.AddLine("EndPrimitive();");
}
break;
}
case OpCode::Id::MOV_SYS: {
switch (instr.sys20) {
case Tegra::Shader::SystemVariable::InvocationInfo: {
LOG_WARNING(HW_GPU, "MOV_SYS instruction with InvocationInfo is incomplete");
regs.SetRegisterToInteger(instr.gpr0, false, 0, "0u", 1, 1);
break;
}
default: {
LOG_CRITICAL(HW_GPU, "Unhandled system move: {}",
static_cast<u32>(instr.sys20.Value()));
UNREACHABLE();
}
}
break;
}
case OpCode::Id::ISBERD: {
ASSERT(instr.isberd.o == 0);
ASSERT(instr.isberd.skew == 0);
ASSERT(instr.isberd.shift == Tegra::Shader::IsberdShift::None);
ASSERT(instr.isberd.mode == Tegra::Shader::IsberdMode::None);
ASSERT_MSG(stage == Maxwell3D::Regs::ShaderStage::Geometry,
"ISBERD is expected to be used in a geometry shader.");
LOG_WARNING(HW_GPU, "ISBERD instruction is incomplete");
regs.SetRegisterToFloat(instr.gpr0, 0, regs.GetRegisterAsFloat(instr.gpr8), 1, 1);
break;
}
case OpCode::Id::BRA: {
ASSERT_MSG(instr.bra.constant_buffer == 0,
"BRA with constant buffers are not implemented");
@@ -3087,7 +2779,7 @@ private:
ShaderWriter shader;
ShaderWriter declarations;
GLSLRegisterManager regs{shader, declarations, stage, suffix, header};
GLSLRegisterManager regs{shader, declarations, stage, suffix};
// Declarations
std::set<std::string> declr_predicates;

View File

@@ -17,18 +17,7 @@ ProgramResult GenerateVertexShader(const ShaderSetup& setup) {
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
out += R"(
out gl_PerVertex {
vec4 gl_Position;
};
layout(std140) uniform vs_config {
vec4 viewport_flip;
uvec4 instance_id;
uvec4 flip_stage;
};
)";
out += "bool exec_vertex();\n";
if (setup.IsDualProgram()) {
out += "bool exec_vertex_b();\n";
@@ -39,18 +28,19 @@ layout(std140) uniform vs_config {
Maxwell3D::Regs::ShaderStage::Vertex, "vertex")
.get_value_or({});
out += program.first;
if (setup.IsDualProgram()) {
ProgramResult program_b =
Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
.get_value_or({});
out += program_b.first;
}
out += R"(
out gl_PerVertex {
vec4 gl_Position;
};
out vec4 position;
layout (std140) uniform vs_config {
vec4 viewport_flip;
uvec4 instance_id;
};
void main() {
position = vec4(0.0, 0.0, 0.0, 0.0);
exec_vertex();
@@ -62,52 +52,27 @@ void main() {
out += R"(
// Check if the flip stage is VertexB
if (flip_stage[0] == 1) {
// Viewport can be flipped, which is unsupported by glViewport
position.xy *= viewport_flip.xy;
}
// Viewport can be flipped, which is unsupported by glViewport
position.xy *= viewport_flip.xy;
gl_Position = position;
// TODO(bunnei): This is likely a hack, position.w should be interpolated as 1.0
// For now, this is here to bring order in lieu of proper emulation
if (flip_stage[0] == 1) {
position.w = 1.0;
}
position.w = 1.0;
}
)";
return {out, program.second};
}
ProgramResult GenerateGeometryShader(const ShaderSetup& setup) {
std::string out = "#version 430 core\n";
out += "#extension GL_ARB_separate_shader_objects : enable\n\n";
out += Decompiler::GetCommonDeclarations();
out += "bool exec_geometry();\n";
ProgramResult program =
Decompiler::DecompileProgram(setup.program.code, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Geometry, "geometry")
.get_value_or({});
out += R"(
out gl_PerVertex {
vec4 gl_Position;
};
layout (std140) uniform gs_config {
vec4 viewport_flip;
uvec4 instance_id;
uvec4 flip_stage;
};
void main() {
exec_geometry();
}
)";
out += program.first;
if (setup.IsDualProgram()) {
ProgramResult program_b =
Decompiler::DecompileProgram(setup.program.code_b, PROGRAM_OFFSET,
Maxwell3D::Regs::ShaderStage::Vertex, "vertex_b")
.get_value_or({});
out += program_b.first;
}
return {out, program.second};
}
@@ -122,6 +87,7 @@ ProgramResult GenerateFragmentShader(const ShaderSetup& setup) {
Maxwell3D::Regs::ShaderStage::Fragment, "fragment")
.get_value_or({});
out += R"(
in vec4 position;
layout(location = 0) out vec4 FragColor0;
layout(location = 1) out vec4 FragColor1;
layout(location = 2) out vec4 FragColor2;
@@ -134,7 +100,6 @@ layout(location = 7) out vec4 FragColor7;
layout (std140) uniform fs_config {
vec4 viewport_flip;
uvec4 instance_id;
uvec4 flip_stage;
};
void main() {
@@ -145,4 +110,5 @@ void main() {
out += program.first;
return {out, program.second};
}
} // namespace OpenGL::GLShader
} // namespace OpenGL::GLShader

View File

@@ -75,9 +75,8 @@ class SamplerEntry {
public:
SamplerEntry(Maxwell::ShaderStage stage, std::size_t offset, std::size_t index,
Tegra::Shader::TextureType type, bool is_array, bool is_shadow)
: offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array),
is_shadow(is_shadow) {}
Tegra::Shader::TextureType type, bool is_array)
: offset(offset), stage(stage), sampler_index(index), type(type), is_array(is_array) {}
std::size_t GetOffset() const {
return offset;
@@ -118,8 +117,6 @@ public:
}
if (is_array)
glsl_type += "Array";
if (is_shadow)
glsl_type += "Shadow";
return glsl_type;
}
@@ -131,10 +128,6 @@ public:
return is_array;
}
bool IsShadow() const {
return is_shadow;
}
u32 GetHash() const {
return (static_cast<u32>(stage) << 16) | static_cast<u32>(sampler_index);
}
@@ -154,8 +147,7 @@ private:
Maxwell::ShaderStage stage; ///< Shader stage where this sampler was used.
std::size_t sampler_index; ///< Value used to index into the generated GLSL sampler array.
Tegra::Shader::TextureType type; ///< The type used to sample this texture (Texture2D, etc)
bool is_array; ///< Whether the texture is being sampled as an array texture or not.
bool is_shadow; ///< Whether the texture is being sampled as a depth texture or not.
bool is_array; ///< Whether the texture is being sampled as an array texture or not.
};
struct ShaderEntries {
@@ -195,12 +187,6 @@ private:
*/
ProgramResult GenerateVertexShader(const ShaderSetup& setup);
/**
* Generates the GLSL geometry shader program source code for the given GS program
* @returns String of the shader source code
*/
ProgramResult GenerateGeometryShader(const ShaderSetup& setup);
/**
* Generates the GLSL fragment shader program source code for the given FS program
* @returns String of the shader source code

View File

@@ -18,14 +18,6 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& sh
// We only assign the instance to the first component of the vector, the rest is just padding.
instance_id[0] = state.current_instance;
// Assign in which stage the position has to be flipped
// (the last stage before the fragment shader).
if (gpu.regs.shader_config[static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry)].enable) {
flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::Geometry);
} else {
flip_stage[0] = static_cast<u32>(Maxwell3D::Regs::ShaderProgram::VertexB);
}
}
} // namespace OpenGL::GLShader

View File

@@ -21,9 +21,8 @@ struct MaxwellUniformData {
void SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage);
alignas(16) GLvec4 viewport_flip;
alignas(16) GLuvec4 instance_id;
alignas(16) GLuvec4 flip_stage;
};
static_assert(sizeof(MaxwellUniformData) == 48, "MaxwellUniformData structure size is incorrect");
static_assert(sizeof(MaxwellUniformData) == 32, "MaxwellUniformData structure size is incorrect");
static_assert(sizeof(MaxwellUniformData) < 16384,
"MaxwellUniformData structure must be less than 16kb as per the OpenGL spec");
@@ -37,10 +36,6 @@ public:
vs = program;
}
void UseProgrammableGeometryShader(GLuint program) {
gs = program;
}
void UseProgrammableFragmentShader(GLuint program) {
fs = program;
}

View File

@@ -159,31 +159,6 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
return {};
}
inline GLenum DepthCompareFunc(Tegra::Texture::DepthCompareFunc func) {
switch (func) {
case Tegra::Texture::DepthCompareFunc::Never:
return GL_NEVER;
case Tegra::Texture::DepthCompareFunc::Less:
return GL_LESS;
case Tegra::Texture::DepthCompareFunc::LessEqual:
return GL_LEQUAL;
case Tegra::Texture::DepthCompareFunc::Equal:
return GL_EQUAL;
case Tegra::Texture::DepthCompareFunc::NotEqual:
return GL_NOTEQUAL;
case Tegra::Texture::DepthCompareFunc::Greater:
return GL_GREATER;
case Tegra::Texture::DepthCompareFunc::GreaterEqual:
return GL_GEQUAL;
case Tegra::Texture::DepthCompareFunc::Always:
return GL_ALWAYS;
}
LOG_CRITICAL(Render_OpenGL, "Unimplemented texture depth compare function ={}",
static_cast<u32>(func));
UNREACHABLE();
return {};
}
inline GLenum BlendEquation(Maxwell::Blend::Equation equation) {
switch (equation) {
case Maxwell::Blend::Equation::Add:

View File

@@ -227,17 +227,6 @@ enum class WrapMode : u32 {
MirrorOnceClampOGL = 7,
};
enum class DepthCompareFunc : u32 {
Never = 0,
Less = 1,
Equal = 2,
LessEqual = 3,
Greater = 4,
NotEqual = 5,
GreaterEqual = 6,
Always = 7,
};
enum class TextureFilter : u32 {
Nearest = 1,
Linear = 2,
@@ -255,7 +244,7 @@ struct TSCEntry {
BitField<3, 3, WrapMode> wrap_v;
BitField<6, 3, WrapMode> wrap_p;
BitField<9, 1, u32> depth_compare_enabled;
BitField<10, 3, DepthCompareFunc> depth_compare_func;
BitField<10, 3, u32> depth_compare_func;
};
union {
BitField<0, 2, TextureFilter> mag_filter;

View File

@@ -169,20 +169,16 @@ static void LabelGLObject(GLenum identifier, GLuint handle, VAddr addr,
const std::string nice_addr = fmt::format("0x{:016x}", addr);
std::string object_label;
if (extra_info.empty()) {
switch (identifier) {
case GL_TEXTURE:
object_label = "Texture@" + nice_addr;
break;
case GL_PROGRAM:
object_label = "Shader@" + nice_addr;
break;
default:
object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
break;
}
} else {
object_label = extra_info + '@' + nice_addr;
switch (identifier) {
case GL_TEXTURE:
object_label = extra_info + "@" + nice_addr;
break;
case GL_PROGRAM:
object_label = "ShaderProgram@" + nice_addr;
break;
default:
object_label = fmt::format("Object(0x{:x})@{}", identifier, nice_addr);
break;
}
glObjectLabel(identifier, handle, -1, static_cast<const GLchar*>(object_label.c_str()));
}

View File

@@ -1,16 +0,0 @@
add_library(web_service STATIC
telemetry_json.cpp
telemetry_json.h
verify_login.cpp
verify_login.h
web_backend.cpp
web_backend.h
)
create_target_directory_groups(web_service)
get_directory_property(OPENSSL_LIBS
DIRECTORY ${CMAKE_SOURCE_DIR}/externals/libressl
DEFINITION OPENSSL_LIBS)
target_compile_definitions(web_service PUBLIC -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(web_service PRIVATE common json-headers ${OPENSSL_LIBS} httplib lurlparser)

View File

@@ -1,99 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <thread>
#include "common/assert.h"
#include "common/detached_tasks.h"
#include "web_service/telemetry_json.h"
#include "web_service/web_backend.h"
namespace WebService {
TelemetryJson::TelemetryJson(const std::string& host, const std::string& username,
const std::string& token)
: host(std::move(host)), username(std::move(username)), token(std::move(token)) {}
TelemetryJson::~TelemetryJson() = default;
template <class T>
void TelemetryJson::Serialize(Telemetry::FieldType type, const std::string& name, T value) {
sections[static_cast<u8>(type)][name] = value;
}
void TelemetryJson::SerializeSection(Telemetry::FieldType type, const std::string& name) {
TopSection()[name] = sections[static_cast<unsigned>(type)];
}
void TelemetryJson::Visit(const Telemetry::Field<bool>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<double>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<float>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<u8>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<u16>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<u32>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<u64>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<s8>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<s16>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<s32>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<s64>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<std::string>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue());
}
void TelemetryJson::Visit(const Telemetry::Field<const char*>& field) {
Serialize(field.GetType(), field.GetName(), std::string(field.GetValue()));
}
void TelemetryJson::Visit(const Telemetry::Field<std::chrono::microseconds>& field) {
Serialize(field.GetType(), field.GetName(), field.GetValue().count());
}
void TelemetryJson::Complete() {
SerializeSection(Telemetry::FieldType::App, "App");
SerializeSection(Telemetry::FieldType::Session, "Session");
SerializeSection(Telemetry::FieldType::Performance, "Performance");
SerializeSection(Telemetry::FieldType::UserFeedback, "UserFeedback");
SerializeSection(Telemetry::FieldType::UserConfig, "UserConfig");
SerializeSection(Telemetry::FieldType::UserSystem, "UserSystem");
auto content = TopSection().dump();
// Send the telemetry async but don't handle the errors since they were written to the log
Common::DetachedTasks::AddTask(
[host{this->host}, username{this->username}, token{this->token}, content]() {
Client{host, username, token}.PostJson("/telemetry", content, true);
});
}
} // namespace WebService

View File

@@ -1,58 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <array>
#include <string>
#include <json.hpp>
#include "common/telemetry.h"
#include "common/web_result.h"
namespace WebService {
/**
* Implementation of VisitorInterface that serialized telemetry into JSON, and submits it to the
* yuzu web service
*/
class TelemetryJson : public Telemetry::VisitorInterface {
public:
TelemetryJson(const std::string& host, const std::string& username, const std::string& token);
~TelemetryJson();
void Visit(const Telemetry::Field<bool>& field) override;
void Visit(const Telemetry::Field<double>& field) override;
void Visit(const Telemetry::Field<float>& field) override;
void Visit(const Telemetry::Field<u8>& field) override;
void Visit(const Telemetry::Field<u16>& field) override;
void Visit(const Telemetry::Field<u32>& field) override;
void Visit(const Telemetry::Field<u64>& field) override;
void Visit(const Telemetry::Field<s8>& field) override;
void Visit(const Telemetry::Field<s16>& field) override;
void Visit(const Telemetry::Field<s32>& field) override;
void Visit(const Telemetry::Field<s64>& field) override;
void Visit(const Telemetry::Field<std::string>& field) override;
void Visit(const Telemetry::Field<const char*>& field) override;
void Visit(const Telemetry::Field<std::chrono::microseconds>& field) override;
void Complete() override;
private:
nlohmann::json& TopSection() {
return sections[static_cast<u8>(Telemetry::FieldType::None)];
}
template <class T>
void Serialize(Telemetry::FieldType type, const std::string& name, T value);
void SerializeSection(Telemetry::FieldType type, const std::string& name);
nlohmann::json output;
std::array<nlohmann::json, 7> sections;
std::string host;
std::string username;
std::string token;
};
} // namespace WebService

View File

@@ -1,27 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <json.hpp>
#include "web_service/verify_login.h"
#include "web_service/web_backend.h"
namespace WebService {
bool VerifyLogin(const std::string& host, const std::string& username, const std::string& token) {
Client client(host, username, token);
auto reply = client.GetJson("/profile", false).returned_data;
if (reply.empty()) {
return false;
}
nlohmann::json json = nlohmann::json::parse(reply);
const auto iter = json.find("username");
if (iter == json.end()) {
return username.empty();
}
return username == *iter;
}
} // namespace WebService

View File

@@ -1,22 +0,0 @@
// Copyright 2017 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <functional>
#include <future>
#include <string>
namespace WebService {
/**
* Checks if username and token is valid
* @param host the web API URL
* @param username yuzu username to use for authentication.
* @param token yuzu token to use for authentication.
* @returns a bool indicating whether the verification succeeded
*/
bool VerifyLogin(const std::string& host, const std::string& username, const std::string& token);
} // namespace WebService

Some files were not shown because too many files have changed in this diff Show More