Compare commits
6 Commits
__refs_pul
...
__refs_pul
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
110d578470 | ||
|
|
70bd2bb1d3 | ||
|
|
9669cdb710 | ||
|
|
8886f2e55e | ||
|
|
306739c2c4 | ||
|
|
f62227aa95 |
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -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
|
||||
|
||||
@@ -10,7 +10,3 @@ TRAVIS_JOB_ID
|
||||
TRAVIS_JOB_NUMBER
|
||||
TRAVIS_REPO_SLUG
|
||||
TRAVIS_TAG
|
||||
|
||||
# yuzu specific flags
|
||||
ENABLE_COMPATIBILITY_REPORTING
|
||||
USE_DISCORD_PRESENCE
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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 ..
|
||||
|
||||
|
||||
25
externals/CMakeLists.txt
vendored
25
externals/CMakeLists.txt
vendored
@@ -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()
|
||||
|
||||
1
externals/discord-rpc
vendored
1
externals/discord-rpc
vendored
Submodule externals/discord-rpc deleted from e32d001809
2
externals/fmt
vendored
2
externals/fmt
vendored
Submodule externals/fmt updated: 3e75ad9822...62010520ed
15
externals/httplib/README.md
vendored
15
externals/httplib/README.md
vendored
@@ -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
|
||||
2344
externals/httplib/httplib.h
vendored
2344
externals/httplib/httplib.h
vendored
File diff suppressed because it is too large
Load Diff
9
externals/json/README.md
vendored
9
externals/json/README.md
vendored
@@ -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
17300
externals/json/json.hpp
vendored
File diff suppressed because it is too large
Load Diff
1
externals/libressl
vendored
1
externals/libressl
vendored
Submodule externals/libressl deleted from 7d01cb01cb
8
externals/lurlparser/CMakeLists.txt
vendored
8
externals/lurlparser/CMakeLists.txt
vendored
@@ -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})
|
||||
265
externals/lurlparser/LUrlParser.cpp
vendored
265
externals/lurlparser/LUrlParser.cpp
vendored
@@ -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;
|
||||
}
|
||||
78
externals/lurlparser/LUrlParser.h
vendored
78
externals/lurlparser/LUrlParser.h
vendored
@@ -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
|
||||
19
externals/lurlparser/README.md
vendored
19
externals/lurlparser/README.md
vendored
@@ -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++.
|
||||
@@ -13,6 +13,3 @@ endif()
|
||||
if (ENABLE_QT)
|
||||
add_subdirectory(yuzu)
|
||||
endif()
|
||||
if (ENABLE_WEB_SERVICE)
|
||||
add_subdirectory(web_service)
|
||||
endif()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)};
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user