Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 82cd87f6c2 | |||
| 80a8456af8 | |||
| 908a5a00c5 | |||
| 26fcdf087d | |||
| c28694d907 | |||
| ca470890a3 | |||
| 5cef446f42 | |||
| e78d069a81 | |||
| 8893d63612 | |||
| 0c64a6f0f2 | |||
| c5b20a108d | |||
| 908ca1fc72 | |||
| f375e10411 | |||
| 5c665fcc5b | |||
| 61d2498f00 | |||
| 5799404b78 | |||
| c7b5c245e1 | |||
| 6ca20ad7ba | |||
| 50e3269f3b | |||
| 5fb6781c61 | |||
| 335127af69 | |||
| c0e7b91145 | |||
| c140b6ae2c | |||
| 0360c40e90 | |||
| a9521c983b | |||
| 0f08f2d562 | |||
| 18c1d91920 | |||
| 969cd6dc1d | |||
| a62088539e | |||
| c7daddb715 | |||
| 0af3b4d9f4 | |||
| 7e2bcf04b4 | |||
| f981efdf8d | |||
| a602bcaaf8 | |||
| 7bdef6106e | |||
| 81d361d9f8 | |||
| e34368249f | |||
| 288d027e89 | |||
| 825ffd7b1f | |||
| bf35138d1d | |||
| 364932df3a | |||
| 4486103e1d | |||
| fea6568955 | |||
| 3f87664d8f | |||
| c1a8f684df | |||
| 81b1102090 | |||
| f79823fda7 | |||
| 834e07d639 | |||
| 9a84f428e1 | |||
| 9738dc3058 | |||
| ac54f1a967 | |||
| 528c15051c | |||
| 17d5fb6d06 | |||
| 5669ff3cbd | |||
| 2f2a61887a | |||
| 357ea15a39 | |||
| 11f2f0f45c | |||
| de33ad25f5 | |||
| fe8e6618f2 | |||
| cd2d9628c9 | |||
| dec1cbaf7f | |||
| 769a50661a | |||
| e1b3be7ced | |||
| 04ac7a637a | |||
| adb8a9152b | |||
| 7322c8bd7c | |||
| 03d9bbaa90 | |||
| bf4dfb3ad4 | |||
| a20ba09bfd | |||
| 55c5029171 | |||
| e4fea833d4 | |||
| 8304aaf282 | |||
| d7d5bffa18 | |||
| 5b93290183 | |||
| b5e1e87922 | |||
| 19dcb50692 | |||
| 0935c2d97b | |||
| 6ac4490751 | |||
| a89cc0bafc | |||
| b76df62c00 | |||
| df509486c4 | |||
| 7259f7a733 | |||
| 52b80d231c | |||
| e0920ef4ba | |||
| 33ac193bf6 | |||
| f424b46036 | |||
| a4c5e3e339 | |||
| dec3c981d0 |
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
patreon: yuzuteam
|
||||
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash -ex
|
||||
mkdir "$HOME/.ccache" || true
|
||||
docker run --env-file .travis/common/travis-ci.env -v $(pwd):/yuzu -v "$HOME/.ccache":/root/.ccache yuzuemu/build-environments:linux-mingw /bin/bash -ex /yuzu/.travis/linux-mingw/docker.sh
|
||||
|
||||
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 yuzuemu/build-environments:linux-mingw /bin/bash /yuzu/.travis/linux-mingw/docker.sh
|
||||
|
||||
+12
-16
@@ -7,6 +7,18 @@ include(CMakeDependentOption)
|
||||
|
||||
project(yuzu)
|
||||
|
||||
# Get Git submodule dependencies
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_SUBMOD_RESULT)
|
||||
if(NOT GIT_SUBMOD_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "git submodule update --init --recursive failed with ${GIT_SUBMOD_RESULT}, "
|
||||
"please checkout submodules manually with \"git submodule update --init --recursive\"")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Set bundled sdl2/qt as dependent options.
|
||||
# OFF by default, but if ENABLE_SDL2 and MSVC are true then ON
|
||||
option(ENABLE_SDL2 "Enable the SDL2 frontend" ON)
|
||||
@@ -33,22 +45,6 @@ if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/hooks/pre-commit)
|
||||
DESTINATION ${PROJECT_SOURCE_DIR}/.git/hooks)
|
||||
endif()
|
||||
|
||||
# Sanity check : Check that all submodules are present
|
||||
# =======================================================================
|
||||
|
||||
function(check_submodules_present)
|
||||
file(READ "${PROJECT_SOURCE_DIR}/.gitmodules" gitmodules)
|
||||
string(REGEX MATCHALL "path *= *[^ \t\r\n]*" gitmodules ${gitmodules})
|
||||
foreach(module ${gitmodules})
|
||||
string(REGEX REPLACE "path *= *" "" module ${module})
|
||||
if (NOT EXISTS "${PROJECT_SOURCE_DIR}/${module}/.git")
|
||||
message(FATAL_ERROR "Git submodule ${module} not found. "
|
||||
"Please run: git submodule update --init --recursive")
|
||||
endif()
|
||||
endforeach()
|
||||
endfunction()
|
||||
check_submodules_present()
|
||||
|
||||
configure_file(${PROJECT_SOURCE_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
${PROJECT_BINARY_DIR}/dist/compatibility_list/compatibility_list.qrc
|
||||
COPYONLY)
|
||||
|
||||
@@ -82,6 +82,9 @@ set(HASH_FILES
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/node.h"
|
||||
"${VIDEO_CORE}/shader/node_helper.cpp"
|
||||
"${VIDEO_CORE}/shader/node_helper.h"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
|
||||
Vendored
+1
-1
Submodule externals/Vulkan-Headers updated: 15e5c4db75...d05c8df88d
@@ -51,6 +51,10 @@ void Stream::Stop() {
|
||||
UNIMPLEMENTED();
|
||||
}
|
||||
|
||||
void Stream::SetVolume(float volume) {
|
||||
game_volume = volume;
|
||||
}
|
||||
|
||||
Stream::State Stream::GetState() const {
|
||||
return state;
|
||||
}
|
||||
@@ -62,8 +66,8 @@ s64 Stream::GetBufferReleaseCycles(const Buffer& buffer) const {
|
||||
return Core::Timing::usToCycles(us);
|
||||
}
|
||||
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples) {
|
||||
const float volume{std::clamp(Settings::values.volume, 0.0f, 1.0f)};
|
||||
static void VolumeAdjustSamples(std::vector<s16>& samples, float game_volume) {
|
||||
const float volume{std::clamp(Settings::values.volume - (1.0f - game_volume), 0.0f, 1.0f)};
|
||||
|
||||
if (volume == 1.0f) {
|
||||
return;
|
||||
@@ -97,7 +101,7 @@ void Stream::PlayNextBuffer() {
|
||||
active_buffer = queued_buffers.front();
|
||||
queued_buffers.pop();
|
||||
|
||||
VolumeAdjustSamples(active_buffer->GetSamples());
|
||||
VolumeAdjustSamples(active_buffer->GetSamples(), game_volume);
|
||||
|
||||
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
|
||||
|
||||
|
||||
@@ -61,6 +61,12 @@ public:
|
||||
/// Returns a vector of recently released buffers specified by tag
|
||||
std::vector<Buffer::Tag> GetTagsAndReleaseBuffers(std::size_t max_count);
|
||||
|
||||
void SetVolume(float volume);
|
||||
|
||||
float GetVolume() const {
|
||||
return game_volume;
|
||||
}
|
||||
|
||||
/// Returns true if the stream is currently playing
|
||||
bool IsPlaying() const {
|
||||
return state == State::Playing;
|
||||
@@ -94,6 +100,7 @@ private:
|
||||
|
||||
u32 sample_rate; ///< Sample rate of the stream
|
||||
Format format; ///< Format of the stream
|
||||
float game_volume = 1.0f; ///< The volume the game currently has set
|
||||
ReleaseCallback release_callback; ///< Buffer release callback for the stream
|
||||
State state{State::Stopped}; ///< Playback state of the stream
|
||||
Core::Timing::EventType* release_event{}; ///< Core timing release event for the stream
|
||||
|
||||
@@ -56,6 +56,9 @@ add_custom_command(OUTPUT scm_rev.cpp
|
||||
"${VIDEO_CORE}/shader/decode/video.cpp"
|
||||
"${VIDEO_CORE}/shader/decode/xmad.cpp"
|
||||
"${VIDEO_CORE}/shader/decode.cpp"
|
||||
"${VIDEO_CORE}/shader/node.h"
|
||||
"${VIDEO_CORE}/shader/node_helper.cpp"
|
||||
"${VIDEO_CORE}/shader/node_helper.h"
|
||||
"${VIDEO_CORE}/shader/shader_ir.cpp"
|
||||
"${VIDEO_CORE}/shader/shader_ir.h"
|
||||
"${VIDEO_CORE}/shader/track.cpp"
|
||||
|
||||
@@ -30,13 +30,6 @@ std::vector<u8> HexStringToVector(std::string_view str, bool little_endian) {
|
||||
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,
|
||||
|
||||
+12
-4
@@ -7,6 +7,7 @@
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <fmt/format.h>
|
||||
#include "common/common_types.h"
|
||||
@@ -30,13 +31,20 @@ std::array<u8, Size> HexStringToArray(std::string_view str) {
|
||||
return out;
|
||||
}
|
||||
|
||||
std::string HexVectorToString(const std::vector<u8>& vector, bool upper = true);
|
||||
template <typename ContiguousContainer>
|
||||
std::string HexToString(const ContiguousContainer& data, bool upper = true) {
|
||||
static_assert(std::is_same_v<typename ContiguousContainer::value_type, u8>,
|
||||
"Underlying type within the contiguous container must be u8.");
|
||||
|
||||
constexpr std::size_t pad_width = 2;
|
||||
|
||||
template <std::size_t Size>
|
||||
std::string HexArrayToString(std::array<u8, Size> array, bool upper = true) {
|
||||
std::string out;
|
||||
for (u8 c : array)
|
||||
out.reserve(std::size(data) * pad_width);
|
||||
|
||||
for (const u8 c : data) {
|
||||
out += fmt::format(upper ? "{:02X}" : "{:02x}", c);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ add_library(core STATIC
|
||||
arm/exclusive_monitor.h
|
||||
arm/unicorn/arm_unicorn.cpp
|
||||
arm/unicorn/arm_unicorn.h
|
||||
constants.cpp
|
||||
constants.h
|
||||
core.cpp
|
||||
core.h
|
||||
core_cpu.cpp
|
||||
@@ -461,9 +463,6 @@ add_library(core STATIC
|
||||
settings.h
|
||||
telemetry_session.cpp
|
||||
telemetry_session.h
|
||||
tracer/citrace.h
|
||||
tracer/recorder.cpp
|
||||
tracer/recorder.h
|
||||
)
|
||||
|
||||
create_target_directory_groups(core)
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "core/constants.h"
|
||||
|
||||
namespace Core::Constants {
|
||||
const std::array<u8, 107> ACCOUNT_BACKUP_JPEG{{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
|
||||
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
|
||||
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
|
||||
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
|
||||
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
|
||||
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
|
||||
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
}};
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
// This is to consolidate system-wide constants that are used by multiple components of yuzu.
|
||||
// This is especially to prevent the case of something in frontend duplicating a constexpr array or
|
||||
// directly including some service header for the sole purpose of data.
|
||||
namespace Core::Constants {
|
||||
|
||||
// ACC Service - Blank JPEG used as user icon in absentia of real one.
|
||||
extern const std::array<u8, 107> ACCOUNT_BACKUP_JPEG;
|
||||
|
||||
} // namespace Core::Constants
|
||||
@@ -572,7 +572,7 @@ void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname,
|
||||
<< "# If you are experiencing issues involving keys, it may help to delete this file\n";
|
||||
}
|
||||
|
||||
file << fmt::format("\n{} = {}", keyname, Common::HexArrayToString(key));
|
||||
file << fmt::format("\n{} = {}", keyname, Common::HexToString(key));
|
||||
AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title);
|
||||
}
|
||||
|
||||
@@ -583,7 +583,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
|
||||
Key128 rights_id;
|
||||
std::memcpy(rights_id.data(), &field2, sizeof(u64));
|
||||
std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
|
||||
WriteKeyToFile(KeyCategory::Title, Common::HexArrayToString(rights_id), key);
|
||||
WriteKeyToFile(KeyCategory::Title, Common::HexToString(rights_id), key);
|
||||
}
|
||||
|
||||
auto category = KeyCategory::Standard;
|
||||
|
||||
@@ -18,11 +18,16 @@
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", "logo"};
|
||||
constexpr std::array partition_names{
|
||||
"update",
|
||||
"normal",
|
||||
"secure",
|
||||
"logo",
|
||||
};
|
||||
|
||||
XCI::XCI(VirtualFile file_)
|
||||
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
|
||||
partitions(0x4) {
|
||||
partitions(partition_names.size()) {
|
||||
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
|
||||
status = Loader::ResultStatus::ErrorBadXCIHeader;
|
||||
return;
|
||||
@@ -43,23 +48,24 @@ XCI::XCI(VirtualFile file_)
|
||||
|
||||
for (XCIPartition partition :
|
||||
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
|
||||
auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]);
|
||||
if (raw != nullptr)
|
||||
partitions[static_cast<std::size_t>(partition)] =
|
||||
std::make_shared<PartitionFilesystem>(raw);
|
||||
const auto partition_idx = static_cast<std::size_t>(partition);
|
||||
auto raw = main_hfs.GetFile(partition_names[partition_idx]);
|
||||
|
||||
if (raw != nullptr) {
|
||||
partitions[partition_idx] = std::make_shared<PartitionFilesystem>(std::move(raw));
|
||||
}
|
||||
}
|
||||
|
||||
secure_partition = std::make_shared<NSP>(
|
||||
main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
|
||||
|
||||
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
|
||||
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
|
||||
|
||||
ncas = secure_partition->GetNCAsCollapsed();
|
||||
program =
|
||||
secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
|
||||
program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID());
|
||||
if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA)
|
||||
if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) {
|
||||
program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
|
||||
}
|
||||
|
||||
auto result = AddNCAFromPartition(XCIPartition::Update);
|
||||
if (result != Loader::ResultStatus::Success) {
|
||||
@@ -147,8 +153,9 @@ std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
|
||||
|
||||
VirtualFile XCI::GetNCAFileByType(NCAContentType type) const {
|
||||
auto nca = GetNCAByType(type);
|
||||
if (nca != nullptr)
|
||||
if (nca != nullptr) {
|
||||
return nca->GetBaseFile();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -169,17 +176,22 @@ VirtualDir XCI::GetParentDirectory() const {
|
||||
}
|
||||
|
||||
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
if (partitions[static_cast<std::size_t>(part)] == nullptr) {
|
||||
const auto partition_index = static_cast<std::size_t>(part);
|
||||
const auto& partition = partitions[partition_index];
|
||||
|
||||
if (partition == nullptr) {
|
||||
return Loader::ResultStatus::ErrorXCIMissingPartition;
|
||||
}
|
||||
|
||||
for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
|
||||
if (file->GetExtension() != "nca")
|
||||
for (const VirtualFile& file : partition->GetFiles()) {
|
||||
if (file->GetExtension() != "nca") {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto nca = std::make_shared<NCA>(file, nullptr, 0, keys);
|
||||
// TODO(DarkLordZach): Add proper Rev1+ Support
|
||||
if (nca->IsUpdate())
|
||||
if (nca->IsUpdate()) {
|
||||
continue;
|
||||
}
|
||||
if (nca->GetType() == NCAContentType::Program) {
|
||||
program_nca_status = nca->GetStatus();
|
||||
}
|
||||
@@ -188,7 +200,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
|
||||
} else {
|
||||
const u16 error_id = static_cast<u16>(nca->GetStatus());
|
||||
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
|
||||
partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id,
|
||||
partition_names[partition_index], nca->GetName(), error_id,
|
||||
nca->GetStatus());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -452,13 +452,13 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
|
||||
|
||||
switch (s_header.raw.header.crypto_type) {
|
||||
case NCASectionCryptoType::NONE:
|
||||
LOG_DEBUG(Crypto, "called with mode=NONE");
|
||||
LOG_TRACE(Crypto, "called with mode=NONE");
|
||||
return in;
|
||||
case NCASectionCryptoType::CTR:
|
||||
// During normal BKTR decryption, this entire function is skipped. This is for the metadata,
|
||||
// which uses the same CTR as usual.
|
||||
case NCASectionCryptoType::BKTR:
|
||||
LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
|
||||
LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
|
||||
{
|
||||
std::optional<Core::Crypto::Key128> key = {};
|
||||
if (has_rights_id) {
|
||||
|
||||
@@ -87,6 +87,10 @@ u64 NACP::GetDefaultJournalSaveSize() const {
|
||||
return raw.user_account_save_data_journal_size;
|
||||
}
|
||||
|
||||
bool NACP::GetUserAccountSwitchLock() const {
|
||||
return raw.user_account_switch_lock != 0;
|
||||
}
|
||||
|
||||
u32 NACP::GetSupportedLanguages() const {
|
||||
return raw.supported_languages;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,8 @@ struct RawNACP {
|
||||
std::array<LanguageEntry, 16> language_entries;
|
||||
std::array<u8, 0x25> isbn;
|
||||
u8 startup_user_account;
|
||||
INSERT_PADDING_BYTES(2);
|
||||
u8 user_account_switch_lock;
|
||||
u8 addon_content_registration_type;
|
||||
u32_le application_attribute;
|
||||
u32_le supported_languages;
|
||||
u32_le parental_control;
|
||||
@@ -111,6 +112,7 @@ public:
|
||||
u64 GetDefaultJournalSaveSize() const;
|
||||
u32 GetSupportedLanguages() const;
|
||||
std::vector<u8> GetRawBytes() const;
|
||||
bool GetUserAccountSwitchLock() const;
|
||||
|
||||
private:
|
||||
RawNACP raw{};
|
||||
|
||||
@@ -287,7 +287,6 @@ void IPSwitchCompiler::Parse() {
|
||||
} else {
|
||||
// hex replacement
|
||||
const auto value = patch_line.substr(9);
|
||||
replace.reserve(value.size() / 2);
|
||||
replace = Common::HexStringToVector(value, is_little_endian);
|
||||
}
|
||||
|
||||
@@ -295,7 +294,7 @@ void IPSwitchCompiler::Parse() {
|
||||
LOG_INFO(Loader,
|
||||
"[IPSwitchCompiler ('{}')] - Patching value at offset 0x{:08X} "
|
||||
"with byte string '{}'",
|
||||
patch_text->GetName(), offset, Common::HexVectorToString(replace));
|
||||
patch_text->GetName(), offset, Common::HexToString(replace));
|
||||
}
|
||||
|
||||
patch.records.insert_or_assign(offset, std::move(replace));
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include "common/common_funcs.h"
|
||||
@@ -69,11 +70,15 @@ struct CNMTHeader {
|
||||
u64_le title_id;
|
||||
u32_le title_version;
|
||||
TitleType type;
|
||||
INSERT_PADDING_BYTES(1);
|
||||
u8 reserved;
|
||||
u16_le table_offset;
|
||||
u16_le number_content_entries;
|
||||
u16_le number_meta_entries;
|
||||
INSERT_PADDING_BYTES(12);
|
||||
u8 attributes;
|
||||
std::array<u8, 2> reserved2;
|
||||
u8 is_committed;
|
||||
u32_le required_download_system_version;
|
||||
std::array<u8, 4> reserved3;
|
||||
};
|
||||
static_assert(sizeof(CNMTHeader) == 0x20, "CNMTHeader has incorrect size.");
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ std::vector<VirtualFile> PatchManager::CollectPatches(const std::vector<VirtualD
|
||||
if (!compiler.IsValid())
|
||||
continue;
|
||||
|
||||
auto this_build_id = Common::HexArrayToString(compiler.GetBuildID());
|
||||
auto this_build_id = Common::HexToString(compiler.GetBuildID());
|
||||
this_build_id =
|
||||
this_build_id.substr(0, this_build_id.find_last_not_of('0') + 1);
|
||||
|
||||
@@ -168,7 +168,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
return nso;
|
||||
}
|
||||
|
||||
const auto build_id_raw = Common::HexArrayToString(header.build_id);
|
||||
const auto build_id_raw = Common::HexToString(header.build_id);
|
||||
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
|
||||
|
||||
if (Settings::values.dump_nso) {
|
||||
@@ -219,7 +219,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
|
||||
}
|
||||
|
||||
bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
|
||||
const auto build_id_raw = Common::HexArrayToString(build_id_);
|
||||
const auto build_id_raw = Common::HexToString(build_id_);
|
||||
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
|
||||
|
||||
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
|
||||
@@ -235,7 +235,7 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
|
||||
static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& system, u64 title_id,
|
||||
const std::array<u8, 0x20>& build_id_,
|
||||
const VirtualDir& base_path, bool upper) {
|
||||
const auto build_id_raw = Common::HexArrayToString(build_id_, upper);
|
||||
const auto build_id_raw = Common::HexToString(build_id_, upper);
|
||||
const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
|
||||
const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
|
||||
|
||||
|
||||
@@ -53,13 +53,14 @@ static bool FollowsNcaIdFormat(std::string_view name) {
|
||||
|
||||
static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
|
||||
bool within_two_digit) {
|
||||
if (!within_two_digit)
|
||||
return fmt::format("/{}.nca", Common::HexArrayToString(nca_id, second_hex_upper));
|
||||
if (!within_two_digit) {
|
||||
return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
|
||||
}
|
||||
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
return fmt::format("/000000{:02X}/{}.nca", hash[0],
|
||||
Common::HexArrayToString(nca_id, second_hex_upper));
|
||||
Common::HexToString(nca_id, second_hex_upper));
|
||||
}
|
||||
|
||||
static std::string GetCNMTName(TitleType type, u64 title_id) {
|
||||
@@ -376,10 +377,11 @@ std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
|
||||
}
|
||||
|
||||
static std::shared_ptr<NCA> GetNCAFromNSPForID(const NSP& nsp, const NcaID& id) {
|
||||
const auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
|
||||
if (file == nullptr)
|
||||
auto file = nsp.GetFile(fmt::format("{}.nca", Common::HexToString(id, false)));
|
||||
if (file == nullptr) {
|
||||
return nullptr;
|
||||
return std::make_shared<NCA>(file);
|
||||
}
|
||||
return std::make_shared<NCA>(std::move(file));
|
||||
}
|
||||
|
||||
InstallResult RegisteredCache::InstallEntry(const XCI& xci, bool overwrite_if_exists,
|
||||
|
||||
@@ -235,16 +235,18 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
const auto section0 = nca->GetSubdirectories()[0];
|
||||
|
||||
for (const auto& inner_file : section0->GetFiles()) {
|
||||
if (inner_file->GetExtension() != "cnmt")
|
||||
if (inner_file->GetExtension() != "cnmt") {
|
||||
continue;
|
||||
}
|
||||
|
||||
const CNMT cnmt(inner_file);
|
||||
auto& ncas_title = ncas[cnmt.GetTitleID()];
|
||||
|
||||
ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
|
||||
for (const auto& rec : cnmt.GetContentRecords()) {
|
||||
const auto id_string = Common::HexArrayToString(rec.nca_id, false);
|
||||
const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
|
||||
const auto id_string = Common::HexToString(rec.nca_id, false);
|
||||
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
|
||||
|
||||
if (next_file == nullptr) {
|
||||
LOG_WARNING(Service_FS,
|
||||
"NCA with ID {}.nca is listed in content metadata, but cannot "
|
||||
@@ -253,9 +255,10 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto next_nca = std::make_shared<NCA>(next_file, nullptr, 0, keys);
|
||||
if (next_nca->GetType() == NCAContentType::Program)
|
||||
auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0, keys);
|
||||
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)) {
|
||||
|
||||
@@ -66,7 +66,7 @@ NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
|
||||
Core::Crypto::SHA256Hash hash{};
|
||||
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
|
||||
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
|
||||
Common::HexArrayToString(nca_id, false)));
|
||||
Common::HexToString(nca_id, false)));
|
||||
}
|
||||
|
||||
NAX::~NAX() = default;
|
||||
|
||||
@@ -48,7 +48,7 @@ void SetupMainThread(Process& owner_process, KernelCore& kernel, u32 priority) {
|
||||
}
|
||||
} // Anonymous namespace
|
||||
|
||||
SharedPtr<Process> Process::Create(Core::System& system, std::string&& name) {
|
||||
SharedPtr<Process> Process::Create(Core::System& system, std::string name) {
|
||||
auto& kernel = system.Kernel();
|
||||
|
||||
SharedPtr<Process> process(new Process(system));
|
||||
@@ -72,10 +72,26 @@ SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
|
||||
return resource_limit;
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryAvailable() const {
|
||||
return vm_manager.GetTotalPhysicalMemoryAvailable();
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryAvailableWithoutMmHeap() const {
|
||||
// TODO: Subtract the personal heap size from this when the
|
||||
// personal heap is implemented.
|
||||
return GetTotalPhysicalMemoryAvailable();
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryUsed() const {
|
||||
return vm_manager.GetCurrentHeapSize() + main_thread_stack_size + code_memory_size;
|
||||
}
|
||||
|
||||
u64 Process::GetTotalPhysicalMemoryUsedWithoutMmHeap() const {
|
||||
// TODO: Subtract the personal heap size from this when the
|
||||
// personal heap is implemented.
|
||||
return GetTotalPhysicalMemoryUsed();
|
||||
}
|
||||
|
||||
void Process::RegisterThread(const Thread* thread) {
|
||||
thread_list.push_back(thread);
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/hle/kernel/address_arbiter.h"
|
||||
#include "core/hle/kernel/handle_table.h"
|
||||
@@ -76,7 +75,7 @@ public:
|
||||
|
||||
static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
|
||||
|
||||
static SharedPtr<Process> Create(Core::System& system, std::string&& name);
|
||||
static SharedPtr<Process> Create(Core::System& system, std::string name);
|
||||
|
||||
std::string GetTypeName() const override {
|
||||
return "Process";
|
||||
@@ -187,9 +186,20 @@ public:
|
||||
return random_entropy.at(index);
|
||||
}
|
||||
|
||||
/// Retrieves the total physical memory available to this process in bytes.
|
||||
u64 GetTotalPhysicalMemoryAvailable() const;
|
||||
|
||||
/// Retrieves the total physical memory available to this process in bytes,
|
||||
/// without the size of the personal heap added to it.
|
||||
u64 GetTotalPhysicalMemoryAvailableWithoutMmHeap() const;
|
||||
|
||||
/// Retrieves the total physical memory used by this process in bytes.
|
||||
u64 GetTotalPhysicalMemoryUsed() const;
|
||||
|
||||
/// Retrieves the total physical memory used by this process in bytes,
|
||||
/// without the size of the personal heap added to it.
|
||||
u64 GetTotalPhysicalMemoryUsedWithoutMmHeap() const;
|
||||
|
||||
/// Gets the list of all threads created with this process as their owner.
|
||||
const std::list<const Thread*>& GetThreadList() const {
|
||||
return thread_list;
|
||||
|
||||
@@ -710,13 +710,13 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
MapRegionSize = 3,
|
||||
HeapRegionBaseAddr = 4,
|
||||
HeapRegionSize = 5,
|
||||
TotalMemoryUsage = 6,
|
||||
TotalPhysicalMemoryAvailable = 6,
|
||||
TotalPhysicalMemoryUsed = 7,
|
||||
IsCurrentProcessBeingDebugged = 8,
|
||||
RegisterResourceLimit = 9,
|
||||
IdleTickCount = 10,
|
||||
RandomEntropy = 11,
|
||||
PerformanceCounter = 0xF0000002,
|
||||
ThreadTickCount = 0xF0000002,
|
||||
// 2.0.0+
|
||||
ASLRRegionBaseAddr = 12,
|
||||
ASLRRegionSize = 13,
|
||||
@@ -730,7 +730,9 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
PrivilegedProcessId = 19,
|
||||
// 5.0.0+
|
||||
UserExceptionContextAddr = 20,
|
||||
ThreadTickCount = 0xF0000002,
|
||||
// 6.0.0+
|
||||
TotalPhysicalMemoryAvailableWithoutMmHeap = 21,
|
||||
TotalPhysicalMemoryUsedWithoutMmHeap = 22,
|
||||
};
|
||||
|
||||
const auto info_id_type = static_cast<GetInfoType>(info_id);
|
||||
@@ -746,12 +748,14 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
case GetInfoType::ASLRRegionSize:
|
||||
case GetInfoType::NewMapRegionBaseAddr:
|
||||
case GetInfoType::NewMapRegionSize:
|
||||
case GetInfoType::TotalMemoryUsage:
|
||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||
case GetInfoType::TotalPhysicalMemoryUsed:
|
||||
case GetInfoType::IsVirtualAddressMemoryEnabled:
|
||||
case GetInfoType::PersonalMmHeapUsage:
|
||||
case GetInfoType::TitleId:
|
||||
case GetInfoType::UserExceptionContextAddr: {
|
||||
case GetInfoType::UserExceptionContextAddr:
|
||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
|
||||
case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap: {
|
||||
if (info_sub_id != 0) {
|
||||
return ERR_INVALID_ENUM_VALUE;
|
||||
}
|
||||
@@ -804,8 +808,8 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
*result = process->VMManager().GetNewMapRegionSize();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalMemoryUsage:
|
||||
*result = process->VMManager().GetTotalMemoryUsage();
|
||||
case GetInfoType::TotalPhysicalMemoryAvailable:
|
||||
*result = process->GetTotalPhysicalMemoryAvailable();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryUsed:
|
||||
@@ -826,6 +830,14 @@ static ResultCode GetInfo(Core::System& system, u64* result, u64 info_id, u64 ha
|
||||
*result = 0;
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryAvailableWithoutMmHeap:
|
||||
*result = process->GetTotalPhysicalMemoryAvailable();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
case GetInfoType::TotalPhysicalMemoryUsedWithoutMmHeap:
|
||||
*result = process->GetTotalPhysicalMemoryUsedWithoutMmHeap();
|
||||
return RESULT_SUCCESS;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -68,9 +68,7 @@ VMManager::VMManager(Core::System& system) : system{system} {
|
||||
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
|
||||
}
|
||||
|
||||
VMManager::~VMManager() {
|
||||
Reset(FileSys::ProgramAddressSpaceType::Is39Bit);
|
||||
}
|
||||
VMManager::~VMManager() = default;
|
||||
|
||||
void VMManager::Reset(FileSys::ProgramAddressSpaceType type) {
|
||||
Clear();
|
||||
@@ -758,7 +756,7 @@ VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, Memo
|
||||
std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
|
||||
}
|
||||
|
||||
u64 VMManager::GetTotalMemoryUsage() const {
|
||||
u64 VMManager::GetTotalPhysicalMemoryAvailable() const {
|
||||
LOG_WARNING(Kernel, "(STUBBED) called");
|
||||
return 0xF8000000;
|
||||
}
|
||||
|
||||
@@ -499,7 +499,7 @@ public:
|
||||
void LogLayout() const;
|
||||
|
||||
/// Gets the total memory usage, used by svcGetInfo
|
||||
u64 GetTotalMemoryUsage() const;
|
||||
u64 GetTotalPhysicalMemoryAvailable() const;
|
||||
|
||||
/// Gets the address space base address
|
||||
VAddr GetAddressSpaceBaseAddress() const;
|
||||
|
||||
@@ -10,30 +10,22 @@
|
||||
#include "common/logging/log.h"
|
||||
#include "common/string_util.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/constants.h"
|
||||
#include "core/core_timing.h"
|
||||
#include "core/file_sys/control_metadata.h"
|
||||
#include "core/file_sys/patch_manager.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/kernel/process.h"
|
||||
#include "core/hle/service/acc/acc.h"
|
||||
#include "core/hle/service/acc/acc_aa.h"
|
||||
#include "core/hle/service/acc/acc_su.h"
|
||||
#include "core/hle/service/acc/acc_u0.h"
|
||||
#include "core/hle/service/acc/acc_u1.h"
|
||||
#include "core/hle/service/acc/profile_manager.h"
|
||||
#include "core/loader/loader.h"
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
|
||||
// used as a backup should the one on disk not exist
|
||||
constexpr u32 backup_jpeg_size = 107;
|
||||
constexpr std::array<u8, backup_jpeg_size> backup_jpeg{{
|
||||
0xff, 0xd8, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x03, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x02, 0x02,
|
||||
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x06, 0x04, 0x04, 0x04, 0x04, 0x04, 0x08, 0x06, 0x06, 0x05,
|
||||
0x06, 0x09, 0x08, 0x0a, 0x0a, 0x09, 0x08, 0x09, 0x09, 0x0a, 0x0c, 0x0f, 0x0c, 0x0a, 0x0b, 0x0e,
|
||||
0x0b, 0x09, 0x09, 0x0d, 0x11, 0x0d, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x10, 0x0a, 0x0c, 0x12, 0x13,
|
||||
0x12, 0x10, 0x13, 0x0f, 0x10, 0x10, 0x10, 0xff, 0xc9, 0x00, 0x0b, 0x08, 0x00, 0x01, 0x00, 0x01,
|
||||
0x01, 0x01, 0x11, 0x00, 0xff, 0xcc, 0x00, 0x06, 0x00, 0x10, 0x10, 0x05, 0xff, 0xda, 0x00, 0x08,
|
||||
0x01, 0x01, 0x00, 0x00, 0x3f, 0x00, 0xd2, 0xcf, 0x20, 0xff, 0xd9,
|
||||
}};
|
||||
|
||||
static std::string GetImagePath(Common::UUID uuid) {
|
||||
return FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
|
||||
"/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg";
|
||||
@@ -101,8 +93,8 @@ private:
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
ctx.WriteBuffer(backup_jpeg);
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
ctx.WriteBuffer(Core::Constants::ACCOUNT_BACKUP_JPEG);
|
||||
rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -124,9 +116,9 @@ private:
|
||||
if (!image.IsOpen()) {
|
||||
LOG_WARNING(Service_ACC,
|
||||
"Failed to load user provided image! Falling back to built-in backup...");
|
||||
rb.Push<u32>(backup_jpeg_size);
|
||||
rb.Push(SanitizeJPEGSize(Core::Constants::ACCOUNT_BACKUP_JPEG.size()));
|
||||
} else {
|
||||
rb.Push<u32>(SanitizeJPEGSize(image.GetSize()));
|
||||
rb.Push(SanitizeJPEGSize(image.GetSize()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,7 +217,7 @@ void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestCon
|
||||
rb.Push(profile_manager->CanSystemRegisterUser());
|
||||
}
|
||||
|
||||
void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx) {
|
||||
void Module::Interface::InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx) {
|
||||
LOG_WARNING(Service_ACC, "(STUBBED) called");
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
@@ -238,6 +230,31 @@ void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestCo
|
||||
rb.PushIpcInterface<IManagerForApplication>();
|
||||
}
|
||||
|
||||
void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
FileSys::NACP nacp;
|
||||
const auto res = system.GetAppLoader().ReadControlData(nacp);
|
||||
|
||||
bool is_locked = false;
|
||||
|
||||
if (res != Loader::ResultStatus::Success) {
|
||||
FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
|
||||
auto nacp_unique = pm.GetControlMetadata().first;
|
||||
|
||||
if (nacp_unique != nullptr) {
|
||||
is_locked = nacp_unique->GetUserAccountSwitchLock();
|
||||
} else {
|
||||
LOG_ERROR(Service_ACC, "nacp_unique is null!");
|
||||
}
|
||||
} else {
|
||||
is_locked = nacp.GetUserAccountSwitchLock();
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(is_locked);
|
||||
}
|
||||
|
||||
void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_ACC, "called");
|
||||
// A u8 is passed into this function which we can safely ignore. It's to determine if we have
|
||||
@@ -263,19 +280,25 @@ void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContex
|
||||
}
|
||||
|
||||
Module::Interface::Interface(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager, const char* name)
|
||||
std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
|
||||
const char* name)
|
||||
: ServiceFramework(name), module(std::move(module)),
|
||||
profile_manager(std::move(profile_manager)) {}
|
||||
profile_manager(std::move(profile_manager)), system(system) {}
|
||||
|
||||
Module::Interface::~Interface() = default;
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager) {
|
||||
void InstallInterfaces(Core::System& system) {
|
||||
auto module = std::make_shared<Module>();
|
||||
auto profile_manager = std::make_shared<ProfileManager>();
|
||||
std::make_shared<ACC_AA>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_SU>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_U0>(module, profile_manager)->InstallAsService(service_manager);
|
||||
std::make_shared<ACC_U1>(module, profile_manager)->InstallAsService(service_manager);
|
||||
|
||||
std::make_shared<ACC_AA>(module, profile_manager, system)
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<ACC_SU>(module, profile_manager, system)
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<ACC_U0>(module, profile_manager, system)
|
||||
->InstallAsService(system.ServiceManager());
|
||||
std::make_shared<ACC_U1>(module, profile_manager, system)
|
||||
->InstallAsService(system.ServiceManager());
|
||||
}
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -15,7 +15,8 @@ public:
|
||||
class Interface : public ServiceFramework<Interface> {
|
||||
public:
|
||||
explicit Interface(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager, const char* name);
|
||||
std::shared_ptr<ProfileManager> profile_manager, Core::System& system,
|
||||
const char* name);
|
||||
~Interface() override;
|
||||
|
||||
void GetUserCount(Kernel::HLERequestContext& ctx);
|
||||
@@ -24,18 +25,20 @@ public:
|
||||
void ListOpenUsers(Kernel::HLERequestContext& ctx);
|
||||
void GetLastOpenedUser(Kernel::HLERequestContext& ctx);
|
||||
void GetProfile(Kernel::HLERequestContext& ctx);
|
||||
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
|
||||
void InitializeApplicationInfoOld(Kernel::HLERequestContext& ctx);
|
||||
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
|
||||
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
|
||||
void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
|
||||
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
|
||||
|
||||
protected:
|
||||
std::shared_ptr<Module> module;
|
||||
std::shared_ptr<ProfileManager> profile_manager;
|
||||
Core::System& system;
|
||||
};
|
||||
};
|
||||
|
||||
/// Registers all ACC services with the specified service manager.
|
||||
void InstallInterfaces(SM::ServiceManager& service_manager);
|
||||
void InstallInterfaces(Core::System& system);
|
||||
|
||||
} // namespace Service::Account
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:aa") {
|
||||
ACC_AA::ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:aa") {
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "EnsureCacheAsync"},
|
||||
{1, nullptr, "LoadCache"},
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_AA final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_AA(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
explicit ACC_AA(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system);
|
||||
~ACC_AA() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:su") {
|
||||
ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:su") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_SU::GetUserCount, "GetUserCount"},
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_SU final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_SU(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
explicit ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system);
|
||||
~ACC_SU() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u0") {
|
||||
ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u0") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_U0::GetUserCount, "GetUserCount"},
|
||||
@@ -21,7 +22,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
|
||||
{60, nullptr, "ListOpenContextStoredUsers"},
|
||||
{99, nullptr, "DebugActivateOpenContextRetention"},
|
||||
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
|
||||
{100, &ACC_U0::InitializeApplicationInfoOld, "InitializeApplicationInfoOld"},
|
||||
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
|
||||
{102, nullptr, "AuthenticateApplicationAsync"},
|
||||
{103, nullptr, "CheckNetworkServiceAvailabilityAsync"},
|
||||
@@ -32,7 +33,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
|
||||
{131, nullptr, "ListOpenContextStoredUsers"},
|
||||
{140, nullptr, "InitializeApplicationInfo"},
|
||||
{141, nullptr, "ListQualifiedUsers"},
|
||||
{150, nullptr, "IsUserAccountSwitchLocked"},
|
||||
{150, &ACC_U0::IsUserAccountSwitchLocked, "IsUserAccountSwitchLocked"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_U0 final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_U0(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
explicit ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system);
|
||||
~ACC_U0() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
namespace Service::Account {
|
||||
|
||||
ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), "acc:u1") {
|
||||
ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system)
|
||||
: Module::Interface(std::move(module), std::move(profile_manager), system, "acc:u1") {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, &ACC_U1::GetUserCount, "GetUserCount"},
|
||||
|
||||
@@ -10,8 +10,8 @@ namespace Service::Account {
|
||||
|
||||
class ACC_U1 final : public Module::Interface {
|
||||
public:
|
||||
explicit ACC_U1(std::shared_ptr<Module> module,
|
||||
std::shared_ptr<ProfileManager> profile_manager);
|
||||
explicit ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> profile_manager,
|
||||
Core::System& system);
|
||||
~ACC_U1() override;
|
||||
};
|
||||
|
||||
|
||||
@@ -271,7 +271,7 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
{71, nullptr, "GetCurrentIlluminanceEx"},
|
||||
{80, nullptr, "SetWirelessPriorityMode"},
|
||||
{90, nullptr, "GetAccumulatedSuspendedTickValue"},
|
||||
{91, nullptr, "GetAccumulatedSuspendedTickChangedEvent"},
|
||||
{91, &ISelfController::GetAccumulatedSuspendedTickChangedEvent, "GetAccumulatedSuspendedTickChangedEvent"},
|
||||
{100, nullptr, "SetAlbumImageTakenNotificationEnabled"},
|
||||
{1000, nullptr, "GetDebugStorageChannel"},
|
||||
};
|
||||
@@ -282,6 +282,11 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
"ISelfController:LaunchableEvent");
|
||||
|
||||
// TODO(ogniK): Figure out where, when and why this event gets signalled
|
||||
accumulated_suspended_tick_changed_event = Kernel::WritableEvent::CreateEventPair(
|
||||
kernel, Kernel::ResetType::Manual, "ISelfController:AccumulatedSuspendedTickChangedEvent");
|
||||
accumulated_suspended_tick_changed_event.writable->Signal(); // Is signalled on creation
|
||||
}
|
||||
|
||||
ISelfController::~ISelfController() = default;
|
||||
@@ -444,6 +449,17 @@ void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& c
|
||||
rb.Push<u32>(idle_time_detection_extension);
|
||||
}
|
||||
|
||||
void ISelfController::GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx) {
|
||||
// The implementation of this function is fine as is, the reason we're labelling it as stubbed
|
||||
// is because we're currently unsure when and where accumulated_suspended_tick_changed_event is
|
||||
// actually signalled for the time being.
|
||||
LOG_WARNING(Service_AM, "(STUBBED) called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2, 1};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.PushCopyObjects(accumulated_suspended_tick_changed_event.readable);
|
||||
}
|
||||
|
||||
AppletMessageQueue::AppletMessageQueue() {
|
||||
auto& kernel = Core::System::GetInstance().Kernel();
|
||||
on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Manual,
|
||||
|
||||
@@ -133,9 +133,12 @@ private:
|
||||
void SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx);
|
||||
void SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
|
||||
void GetAccumulatedSuspendedTickChangedEvent(Kernel::HLERequestContext& ctx);
|
||||
|
||||
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
|
||||
Kernel::EventPair launchable_event;
|
||||
Kernel::EventPair accumulated_suspended_tick_changed_event;
|
||||
|
||||
u32 idle_time_detection_extension = 0;
|
||||
u64 num_fatal_sections_entered = 0;
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/hex_util.h"
|
||||
@@ -16,21 +16,21 @@
|
||||
|
||||
namespace Service::AM::Applets {
|
||||
|
||||
static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
|
||||
static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix) {
|
||||
std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
"called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexVectorToString(data));
|
||||
"called (STUBBED), during {} received normal data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexToString(data));
|
||||
}
|
||||
|
||||
storage = broker.PopInteractiveDataToApplet();
|
||||
for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
|
||||
const auto data = storage->GetData();
|
||||
LOG_INFO(Service_AM,
|
||||
"called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexVectorToString(data));
|
||||
"called (STUBBED), during {} received interactive data with size={:08X}, data={}",
|
||||
prefix, data.size(), Common::HexToString(data));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +58,8 @@ public:
|
||||
{9, &IAudioOut::GetAudioOutBufferCount, "GetAudioOutBufferCount"},
|
||||
{10, nullptr, "GetAudioOutPlayedSampleCount"},
|
||||
{11, nullptr, "FlushAudioOutBuffers"},
|
||||
{12, nullptr, "SetAudioOutVolume"},
|
||||
{13, nullptr, "GetAudioOutVolume"},
|
||||
{12, &IAudioOut::SetAudioOutVolume, "SetAudioOutVolume"},
|
||||
{13, &IAudioOut::GetAudioOutVolume, "GetAudioOutVolume"},
|
||||
};
|
||||
// clang-format on
|
||||
RegisterHandlers(functions);
|
||||
@@ -183,6 +183,25 @@ private:
|
||||
rb.Push(static_cast<u32>(stream->GetQueueSize()));
|
||||
}
|
||||
|
||||
void SetAudioOutVolume(Kernel::HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
const float volume = rp.Pop<float>();
|
||||
LOG_DEBUG(Service_Audio, "called, volume={}", volume);
|
||||
|
||||
stream->SetVolume(volume);
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
}
|
||||
|
||||
void GetAudioOutVolume(Kernel::HLERequestContext& ctx) {
|
||||
LOG_DEBUG(Service_Audio, "called");
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(RESULT_SUCCESS);
|
||||
rb.Push(stream->GetVolume());
|
||||
}
|
||||
|
||||
AudioCore::AudioOut& audio_core;
|
||||
AudioCore::StreamPtr stream;
|
||||
std::string device_name;
|
||||
|
||||
@@ -310,7 +310,7 @@ public:
|
||||
if (!IsValidNROHash(hash)) {
|
||||
LOG_ERROR(Service_LDR,
|
||||
"NRO hash is not present in any currently loaded NRRs (hash={})!",
|
||||
Common::HexArrayToString(hash));
|
||||
Common::HexToString(hash));
|
||||
IPC::ResponseBuilder rb{ctx, 2};
|
||||
rb.Push(ERROR_MISSING_NRR_HASH);
|
||||
return;
|
||||
|
||||
@@ -4,15 +4,89 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "core/file_sys/romfs_factory.h"
|
||||
#include "core/hle/ipc_helpers.h"
|
||||
#include "core/hle/service/ncm/ncm.h"
|
||||
#include "core/hle/service/service.h"
|
||||
#include "core/hle/service/sm/sm.h"
|
||||
|
||||
namespace Service::NCM {
|
||||
|
||||
class LocationResolver final : public ServiceFramework<LocationResolver> {
|
||||
class ILocationResolver final : public ServiceFramework<ILocationResolver> {
|
||||
public:
|
||||
explicit LocationResolver() : ServiceFramework{"lr"} {
|
||||
explicit ILocationResolver(FileSys::StorageId id)
|
||||
: ServiceFramework{"ILocationResolver"}, storage(id) {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveProgramPath"},
|
||||
{1, nullptr, "RedirectProgramPath"},
|
||||
{2, nullptr, "ResolveApplicationControlPath"},
|
||||
{3, nullptr, "ResolveApplicationHtmlDocumentPath"},
|
||||
{4, nullptr, "ResolveDataPath"},
|
||||
{5, nullptr, "RedirectApplicationControlPath"},
|
||||
{6, nullptr, "RedirectApplicationHtmlDocumentPath"},
|
||||
{7, nullptr, "ResolveApplicationLegalInformationPath"},
|
||||
{8, nullptr, "RedirectApplicationLegalInformationPath"},
|
||||
{9, nullptr, "Refresh"},
|
||||
{10, nullptr, "RedirectProgramPath2"},
|
||||
{11, nullptr, "Refresh2"},
|
||||
{12, nullptr, "DeleteProgramPath"},
|
||||
{13, nullptr, "DeleteApplicationControlPath"},
|
||||
{14, nullptr, "DeleteApplicationHtmlDocumentPath"},
|
||||
{15, nullptr, "DeleteApplicationLegalInformationPath"},
|
||||
{16, nullptr, ""},
|
||||
{17, nullptr, ""},
|
||||
{18, nullptr, ""},
|
||||
{19, nullptr, ""},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
|
||||
private:
|
||||
FileSys::StorageId storage;
|
||||
};
|
||||
|
||||
class IRegisteredLocationResolver final : public ServiceFramework<IRegisteredLocationResolver> {
|
||||
public:
|
||||
explicit IRegisteredLocationResolver() : ServiceFramework{"IRegisteredLocationResolver"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveProgramPath"},
|
||||
{1, nullptr, "RegisterProgramPath"},
|
||||
{2, nullptr, "UnregisterProgramPath"},
|
||||
{3, nullptr, "RedirectProgramPath"},
|
||||
{4, nullptr, "ResolveHtmlDocumentPath"},
|
||||
{5, nullptr, "RegisterHtmlDocumentPath"},
|
||||
{6, nullptr, "UnregisterHtmlDocumentPath"},
|
||||
{7, nullptr, "RedirectHtmlDocumentPath"},
|
||||
{8, nullptr, ""},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class IAddOnContentLocationResolver final : public ServiceFramework<IAddOnContentLocationResolver> {
|
||||
public:
|
||||
explicit IAddOnContentLocationResolver() : ServiceFramework{"IAddOnContentLocationResolver"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "ResolveAddOnContentPath"},
|
||||
{1, nullptr, "RegisterAddOnContentStorage"},
|
||||
{2, nullptr, "UnregisterAllAddOnContentPath"},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
RegisterHandlers(functions);
|
||||
}
|
||||
};
|
||||
|
||||
class LR final : public ServiceFramework<LR> {
|
||||
public:
|
||||
explicit LR() : ServiceFramework{"lr"} {
|
||||
// clang-format off
|
||||
static const FunctionInfo functions[] = {
|
||||
{0, nullptr, "OpenLocationResolver"},
|
||||
@@ -52,7 +126,7 @@ public:
|
||||
};
|
||||
|
||||
void InstallInterfaces(SM::ServiceManager& sm) {
|
||||
std::make_shared<LocationResolver>()->InstallAsService(sm);
|
||||
std::make_shared<LR>()->InstallAsService(sm);
|
||||
std::make_shared<NCM>()->InstallAsService(sm);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,13 +11,13 @@ namespace Service::NS {
|
||||
class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
|
||||
public:
|
||||
explicit IAccountProxyInterface();
|
||||
~IAccountProxyInterface();
|
||||
~IAccountProxyInterface() override;
|
||||
};
|
||||
|
||||
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
|
||||
public:
|
||||
explicit IApplicationManagerInterface();
|
||||
~IApplicationManagerInterface();
|
||||
~IApplicationManagerInterface() override;
|
||||
|
||||
ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
|
||||
ResultVal<u64> ConvertApplicationLanguageToLanguageCode(u8 application_language);
|
||||
@@ -31,43 +31,43 @@ private:
|
||||
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
|
||||
public:
|
||||
explicit IApplicationVersionInterface();
|
||||
~IApplicationVersionInterface();
|
||||
~IApplicationVersionInterface() override;
|
||||
};
|
||||
|
||||
class IContentManagerInterface final : public ServiceFramework<IContentManagerInterface> {
|
||||
public:
|
||||
explicit IContentManagerInterface();
|
||||
~IContentManagerInterface();
|
||||
~IContentManagerInterface() override;
|
||||
};
|
||||
|
||||
class IDocumentInterface final : public ServiceFramework<IDocumentInterface> {
|
||||
public:
|
||||
explicit IDocumentInterface();
|
||||
~IDocumentInterface();
|
||||
~IDocumentInterface() override;
|
||||
};
|
||||
|
||||
class IDownloadTaskInterface final : public ServiceFramework<IDownloadTaskInterface> {
|
||||
public:
|
||||
explicit IDownloadTaskInterface();
|
||||
~IDownloadTaskInterface();
|
||||
~IDownloadTaskInterface() override;
|
||||
};
|
||||
|
||||
class IECommerceInterface final : public ServiceFramework<IECommerceInterface> {
|
||||
public:
|
||||
explicit IECommerceInterface();
|
||||
~IECommerceInterface();
|
||||
~IECommerceInterface() override;
|
||||
};
|
||||
|
||||
class IFactoryResetInterface final : public ServiceFramework<IFactoryResetInterface> {
|
||||
public:
|
||||
explicit IFactoryResetInterface();
|
||||
~IFactoryResetInterface();
|
||||
~IFactoryResetInterface() override;
|
||||
};
|
||||
|
||||
class NS final : public ServiceFramework<NS> {
|
||||
public:
|
||||
explicit NS(const char* name);
|
||||
~NS();
|
||||
~NS() override;
|
||||
|
||||
std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system,
|
||||
|
||||
SM::ServiceManager::InstallInterfaces(sm);
|
||||
|
||||
Account::InstallInterfaces(*sm);
|
||||
Account::InstallInterfaces(system);
|
||||
AM::InstallInterfaces(*sm, nv_flinger);
|
||||
AOC::InstallInterfaces(*sm);
|
||||
APM::InstallInterfaces(*sm);
|
||||
|
||||
@@ -152,8 +152,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
|
||||
auto& system = Core::System::GetInstance();
|
||||
const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
|
||||
if (!cheats.empty()) {
|
||||
system.RegisterCheatList(cheats, Common::HexArrayToString(nso_header.build_id),
|
||||
load_base, load_base + program_image.size());
|
||||
system.RegisterCheatList(cheats, Common::HexToString(nso_header.build_id), load_base,
|
||||
load_base + program_image.size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -90,7 +90,6 @@ void LogSettings() {
|
||||
LogSetting("Renderer_UseResolutionFactor", Settings::values.resolution_factor);
|
||||
LogSetting("Renderer_UseFrameLimit", Settings::values.use_frame_limit);
|
||||
LogSetting("Renderer_FrameLimit", Settings::values.frame_limit);
|
||||
LogSetting("Renderer_UseCompatibilityProfile", Settings::values.use_compatibility_profile);
|
||||
LogSetting("Renderer_UseDiskShaderCache", Settings::values.use_disk_shader_cache);
|
||||
LogSetting("Renderer_UseAccurateGpuEmulation", Settings::values.use_accurate_gpu_emulation);
|
||||
LogSetting("Renderer_UseAsynchronousGpuEmulation",
|
||||
|
||||
@@ -390,7 +390,6 @@ struct Values {
|
||||
float resolution_factor;
|
||||
bool use_frame_limit;
|
||||
u16 frame_limit;
|
||||
bool use_compatibility_profile;
|
||||
bool use_disk_shader_cache;
|
||||
bool use_accurate_gpu_emulation;
|
||||
bool use_asynchronous_gpu_emulation;
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace CiTrace {
|
||||
|
||||
// NOTE: Things are stored in little-endian
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
struct CTHeader {
|
||||
static const char* ExpectedMagicWord() {
|
||||
return "CiTr";
|
||||
}
|
||||
|
||||
static u32 ExpectedVersion() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
char magic[4];
|
||||
u32 version;
|
||||
u32 header_size;
|
||||
|
||||
struct {
|
||||
// NOTE: Register range sizes are technically hardware-constants, but the actual limits
|
||||
// aren't known. Hence we store the presumed limits along the offsets.
|
||||
// Sizes are given in u32 units.
|
||||
u32 gpu_registers;
|
||||
u32 gpu_registers_size;
|
||||
u32 lcd_registers;
|
||||
u32 lcd_registers_size;
|
||||
u32 pica_registers;
|
||||
u32 pica_registers_size;
|
||||
u32 default_attributes;
|
||||
u32 default_attributes_size;
|
||||
u32 vs_program_binary;
|
||||
u32 vs_program_binary_size;
|
||||
u32 vs_swizzle_data;
|
||||
u32 vs_swizzle_data_size;
|
||||
u32 vs_float_uniforms;
|
||||
u32 vs_float_uniforms_size;
|
||||
u32 gs_program_binary;
|
||||
u32 gs_program_binary_size;
|
||||
u32 gs_swizzle_data;
|
||||
u32 gs_swizzle_data_size;
|
||||
u32 gs_float_uniforms;
|
||||
u32 gs_float_uniforms_size;
|
||||
|
||||
// Other things we might want to store here:
|
||||
// - Initial framebuffer data, maybe even a full copy of FCRAM/VRAM
|
||||
// - Lookup tables for fragment lighting
|
||||
// - Lookup tables for procedural textures
|
||||
} initial_state_offsets;
|
||||
|
||||
u32 stream_offset;
|
||||
u32 stream_size;
|
||||
};
|
||||
|
||||
enum CTStreamElementType : u32 {
|
||||
FrameMarker = 0xE1,
|
||||
MemoryLoad = 0xE2,
|
||||
RegisterWrite = 0xE3,
|
||||
};
|
||||
|
||||
struct CTMemoryLoad {
|
||||
u32 file_offset;
|
||||
u32 size;
|
||||
u32 physical_address;
|
||||
u32 pad;
|
||||
};
|
||||
|
||||
struct CTRegisterWrite {
|
||||
u32 physical_address;
|
||||
|
||||
enum : u32 {
|
||||
SIZE_8 = 0xD1,
|
||||
SIZE_16 = 0xD2,
|
||||
SIZE_32 = 0xD3,
|
||||
SIZE_64 = 0xD4,
|
||||
} size;
|
||||
|
||||
// TODO: Make it clearer which bits of this member are used for sizes other than 32 bits
|
||||
u64 value;
|
||||
};
|
||||
|
||||
struct CTStreamElement {
|
||||
CTStreamElementType type;
|
||||
|
||||
union {
|
||||
CTMemoryLoad memory_load;
|
||||
CTRegisterWrite register_write;
|
||||
};
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
} // namespace CiTrace
|
||||
@@ -1,208 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <cstring>
|
||||
#include "common/assert.h"
|
||||
#include "common/file_util.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "core/tracer/recorder.h"
|
||||
|
||||
namespace CiTrace {
|
||||
|
||||
Recorder::Recorder(const InitialState& initial_state) : initial_state(initial_state) {}
|
||||
|
||||
void Recorder::Finish(const std::string& filename) {
|
||||
// Setup CiTrace header
|
||||
CTHeader header;
|
||||
std::memcpy(header.magic, CTHeader::ExpectedMagicWord(), 4);
|
||||
header.version = CTHeader::ExpectedVersion();
|
||||
header.header_size = sizeof(CTHeader);
|
||||
|
||||
// Calculate file offsets
|
||||
auto& initial = header.initial_state_offsets;
|
||||
|
||||
initial.gpu_registers_size = static_cast<u32>(initial_state.gpu_registers.size());
|
||||
initial.lcd_registers_size = static_cast<u32>(initial_state.lcd_registers.size());
|
||||
initial.pica_registers_size = static_cast<u32>(initial_state.pica_registers.size());
|
||||
initial.default_attributes_size = static_cast<u32>(initial_state.default_attributes.size());
|
||||
initial.vs_program_binary_size = static_cast<u32>(initial_state.vs_program_binary.size());
|
||||
initial.vs_swizzle_data_size = static_cast<u32>(initial_state.vs_swizzle_data.size());
|
||||
initial.vs_float_uniforms_size = static_cast<u32>(initial_state.vs_float_uniforms.size());
|
||||
initial.gs_program_binary_size = static_cast<u32>(initial_state.gs_program_binary.size());
|
||||
initial.gs_swizzle_data_size = static_cast<u32>(initial_state.gs_swizzle_data.size());
|
||||
initial.gs_float_uniforms_size = static_cast<u32>(initial_state.gs_float_uniforms.size());
|
||||
header.stream_size = static_cast<u32>(stream.size());
|
||||
|
||||
initial.gpu_registers = sizeof(header);
|
||||
initial.lcd_registers = initial.gpu_registers + initial.gpu_registers_size * sizeof(u32);
|
||||
initial.pica_registers = initial.lcd_registers + initial.lcd_registers_size * sizeof(u32);
|
||||
;
|
||||
initial.default_attributes = initial.pica_registers + initial.pica_registers_size * sizeof(u32);
|
||||
initial.vs_program_binary =
|
||||
initial.default_attributes + initial.default_attributes_size * sizeof(u32);
|
||||
initial.vs_swizzle_data =
|
||||
initial.vs_program_binary + initial.vs_program_binary_size * sizeof(u32);
|
||||
initial.vs_float_uniforms =
|
||||
initial.vs_swizzle_data + initial.vs_swizzle_data_size * sizeof(u32);
|
||||
initial.gs_program_binary =
|
||||
initial.vs_float_uniforms + initial.vs_float_uniforms_size * sizeof(u32);
|
||||
initial.gs_swizzle_data =
|
||||
initial.gs_program_binary + initial.gs_program_binary_size * sizeof(u32);
|
||||
initial.gs_float_uniforms =
|
||||
initial.gs_swizzle_data + initial.gs_swizzle_data_size * sizeof(u32);
|
||||
header.stream_offset = initial.gs_float_uniforms + initial.gs_float_uniforms_size * sizeof(u32);
|
||||
|
||||
// Iterate through stream elements, update relevant stream element data
|
||||
for (auto& stream_element : stream) {
|
||||
switch (stream_element.data.type) {
|
||||
case MemoryLoad: {
|
||||
auto& file_offset = memory_regions[stream_element.hash];
|
||||
if (!stream_element.uses_existing_data) {
|
||||
file_offset = header.stream_offset;
|
||||
}
|
||||
stream_element.data.memory_load.file_offset = file_offset;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// Other commands don't use any extra data
|
||||
DEBUG_ASSERT(stream_element.extra_data.size() == 0);
|
||||
break;
|
||||
}
|
||||
header.stream_offset += static_cast<u32>(stream_element.extra_data.size());
|
||||
}
|
||||
|
||||
try {
|
||||
// Open file and write header
|
||||
FileUtil::IOFile file(filename, "wb");
|
||||
std::size_t written = file.WriteObject(header);
|
||||
if (written != 1 || file.Tell() != initial.gpu_registers)
|
||||
throw "Failed to write header";
|
||||
|
||||
// Write initial state
|
||||
written =
|
||||
file.WriteArray(initial_state.gpu_registers.data(), initial_state.gpu_registers.size());
|
||||
if (written != initial_state.gpu_registers.size() || file.Tell() != initial.lcd_registers)
|
||||
throw "Failed to write GPU registers";
|
||||
|
||||
written =
|
||||
file.WriteArray(initial_state.lcd_registers.data(), initial_state.lcd_registers.size());
|
||||
if (written != initial_state.lcd_registers.size() || file.Tell() != initial.pica_registers)
|
||||
throw "Failed to write LCD registers";
|
||||
|
||||
written = file.WriteArray(initial_state.pica_registers.data(),
|
||||
initial_state.pica_registers.size());
|
||||
if (written != initial_state.pica_registers.size() ||
|
||||
file.Tell() != initial.default_attributes)
|
||||
throw "Failed to write Pica registers";
|
||||
|
||||
written = file.WriteArray(initial_state.default_attributes.data(),
|
||||
initial_state.default_attributes.size());
|
||||
if (written != initial_state.default_attributes.size() ||
|
||||
file.Tell() != initial.vs_program_binary)
|
||||
throw "Failed to write default vertex attributes";
|
||||
|
||||
written = file.WriteArray(initial_state.vs_program_binary.data(),
|
||||
initial_state.vs_program_binary.size());
|
||||
if (written != initial_state.vs_program_binary.size() ||
|
||||
file.Tell() != initial.vs_swizzle_data)
|
||||
throw "Failed to write vertex shader program binary";
|
||||
|
||||
written = file.WriteArray(initial_state.vs_swizzle_data.data(),
|
||||
initial_state.vs_swizzle_data.size());
|
||||
if (written != initial_state.vs_swizzle_data.size() ||
|
||||
file.Tell() != initial.vs_float_uniforms)
|
||||
throw "Failed to write vertex shader swizzle data";
|
||||
|
||||
written = file.WriteArray(initial_state.vs_float_uniforms.data(),
|
||||
initial_state.vs_float_uniforms.size());
|
||||
if (written != initial_state.vs_float_uniforms.size() ||
|
||||
file.Tell() != initial.gs_program_binary)
|
||||
throw "Failed to write vertex shader float uniforms";
|
||||
|
||||
written = file.WriteArray(initial_state.gs_program_binary.data(),
|
||||
initial_state.gs_program_binary.size());
|
||||
if (written != initial_state.gs_program_binary.size() ||
|
||||
file.Tell() != initial.gs_swizzle_data)
|
||||
throw "Failed to write geomtry shader program binary";
|
||||
|
||||
written = file.WriteArray(initial_state.gs_swizzle_data.data(),
|
||||
initial_state.gs_swizzle_data.size());
|
||||
if (written != initial_state.gs_swizzle_data.size() ||
|
||||
file.Tell() != initial.gs_float_uniforms)
|
||||
throw "Failed to write geometry shader swizzle data";
|
||||
|
||||
written = file.WriteArray(initial_state.gs_float_uniforms.data(),
|
||||
initial_state.gs_float_uniforms.size());
|
||||
if (written != initial_state.gs_float_uniforms.size() ||
|
||||
file.Tell() != initial.gs_float_uniforms + sizeof(u32) * initial.gs_float_uniforms_size)
|
||||
throw "Failed to write geometry shader float uniforms";
|
||||
|
||||
// Iterate through stream elements, write "extra data"
|
||||
for (const auto& stream_element : stream) {
|
||||
if (stream_element.extra_data.size() == 0)
|
||||
continue;
|
||||
|
||||
written =
|
||||
file.WriteBytes(stream_element.extra_data.data(), stream_element.extra_data.size());
|
||||
if (written != stream_element.extra_data.size())
|
||||
throw "Failed to write extra data";
|
||||
}
|
||||
|
||||
if (file.Tell() != header.stream_offset)
|
||||
throw "Unexpected end of extra data";
|
||||
|
||||
// Write actual stream elements
|
||||
for (const auto& stream_element : stream) {
|
||||
if (1 != file.WriteObject(stream_element.data))
|
||||
throw "Failed to write stream element";
|
||||
}
|
||||
} catch (const char* str) {
|
||||
LOG_ERROR(HW_GPU, "Writing CiTrace file failed: {}", str);
|
||||
}
|
||||
}
|
||||
|
||||
void Recorder::FrameFinished() {
|
||||
stream.push_back({{FrameMarker}});
|
||||
}
|
||||
|
||||
void Recorder::MemoryAccessed(const u8* data, u32 size, u32 physical_address) {
|
||||
StreamElement element = {{MemoryLoad}};
|
||||
element.data.memory_load.size = size;
|
||||
element.data.memory_load.physical_address = physical_address;
|
||||
|
||||
// Compute hash over given memory region to check if the contents are already stored internally
|
||||
boost::crc_32_type result;
|
||||
result.process_bytes(data, size);
|
||||
element.hash = result.checksum();
|
||||
|
||||
element.uses_existing_data = (memory_regions.find(element.hash) != memory_regions.end());
|
||||
if (!element.uses_existing_data) {
|
||||
element.extra_data.resize(size);
|
||||
memcpy(element.extra_data.data(), data, size);
|
||||
memory_regions.insert({element.hash, 0}); // file offset will be initialized in Finish()
|
||||
}
|
||||
|
||||
stream.push_back(element);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Recorder::RegisterWritten(u32 physical_address, T value) {
|
||||
StreamElement element = {{RegisterWrite}};
|
||||
element.data.register_write.size =
|
||||
(sizeof(T) == 1) ? CTRegisterWrite::SIZE_8
|
||||
: (sizeof(T) == 2) ? CTRegisterWrite::SIZE_16
|
||||
: (sizeof(T) == 4) ? CTRegisterWrite::SIZE_32
|
||||
: CTRegisterWrite::SIZE_64;
|
||||
element.data.register_write.physical_address = physical_address;
|
||||
element.data.register_write.value = value;
|
||||
|
||||
stream.push_back(element);
|
||||
}
|
||||
|
||||
template void Recorder::RegisterWritten(u32, u8);
|
||||
template void Recorder::RegisterWritten(u32, u16);
|
||||
template void Recorder::RegisterWritten(u32, u32);
|
||||
template void Recorder::RegisterWritten(u32, u64);
|
||||
} // namespace CiTrace
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright 2015 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <boost/crc.hpp>
|
||||
#include "common/common_types.h"
|
||||
#include "core/tracer/citrace.h"
|
||||
|
||||
namespace CiTrace {
|
||||
|
||||
class Recorder {
|
||||
public:
|
||||
struct InitialState {
|
||||
std::vector<u32> gpu_registers;
|
||||
std::vector<u32> lcd_registers;
|
||||
std::vector<u32> pica_registers;
|
||||
std::vector<u32> default_attributes;
|
||||
std::vector<u32> vs_program_binary;
|
||||
std::vector<u32> vs_swizzle_data;
|
||||
std::vector<u32> vs_float_uniforms;
|
||||
std::vector<u32> gs_program_binary;
|
||||
std::vector<u32> gs_swizzle_data;
|
||||
std::vector<u32> gs_float_uniforms;
|
||||
};
|
||||
|
||||
/**
|
||||
* Recorder constructor
|
||||
* @param initial_state Initial recorder state
|
||||
*/
|
||||
explicit Recorder(const InitialState& initial_state);
|
||||
|
||||
/// Finish recording of this Citrace and save it using the given filename.
|
||||
void Finish(const std::string& filename);
|
||||
|
||||
/// Mark end of a frame
|
||||
void FrameFinished();
|
||||
|
||||
/**
|
||||
* Store a copy of the given memory range in the recording.
|
||||
* @note Use this whenever the GPU is about to access a particular memory region.
|
||||
* @note The implementation will make sure to minimize redundant memory updates.
|
||||
*/
|
||||
void MemoryAccessed(const u8* data, u32 size, u32 physical_address);
|
||||
|
||||
/**
|
||||
* Record a register write.
|
||||
* @note Use this whenever a GPU-related MMIO register has been written to.
|
||||
*/
|
||||
template <typename T>
|
||||
void RegisterWritten(u32 physical_address, T value);
|
||||
|
||||
private:
|
||||
// Initial state of recording start
|
||||
InitialState initial_state;
|
||||
|
||||
// Command stream
|
||||
struct StreamElement {
|
||||
CTStreamElement data;
|
||||
|
||||
/**
|
||||
* Extra data to store along "core" data.
|
||||
* This is e.g. used for data used in MemoryUpdates.
|
||||
*/
|
||||
std::vector<u8> extra_data;
|
||||
|
||||
/// Optional CRC hash (e.g. for hashing memory regions)
|
||||
boost::crc_32_type::value_type hash;
|
||||
|
||||
/// If true, refer to data already written to the output file instead of extra_data
|
||||
bool uses_existing_data;
|
||||
};
|
||||
|
||||
std::vector<StreamElement> stream;
|
||||
|
||||
/**
|
||||
* Internal cache which maps hashes of memory contents to file offsets at which those memory
|
||||
* contents are stored.
|
||||
*/
|
||||
std::unordered_map<boost::crc_32_type::value_type /*hash*/, u32 /*file_offset*/> memory_regions;
|
||||
};
|
||||
|
||||
} // namespace CiTrace
|
||||
@@ -3,6 +3,7 @@ add_library(video_core STATIC
|
||||
dma_pusher.h
|
||||
debug_utils/debug_utils.cpp
|
||||
debug_utils/debug_utils.h
|
||||
engines/const_buffer_info.h
|
||||
engines/engine_upload.cpp
|
||||
engines/engine_upload.h
|
||||
engines/fermi_2d.cpp
|
||||
@@ -42,8 +43,6 @@ add_library(video_core STATIC
|
||||
renderer_opengl/gl_device.h
|
||||
renderer_opengl/gl_global_cache.cpp
|
||||
renderer_opengl/gl_global_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
|
||||
@@ -102,6 +101,9 @@ add_library(video_core STATIC
|
||||
shader/decode/xmad.cpp
|
||||
shader/decode/other.cpp
|
||||
shader/decode.cpp
|
||||
shader/node_helper.cpp
|
||||
shader/node_helper.h
|
||||
shader/node.h
|
||||
shader/shader_ir.cpp
|
||||
shader/shader_ir.h
|
||||
shader/track.cpp
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace Tegra::Engines {
|
||||
|
||||
struct ConstBufferInfo {
|
||||
GPUVAddr address;
|
||||
u32 size;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
} // namespace Tegra::Engines
|
||||
@@ -140,7 +140,7 @@ public:
|
||||
|
||||
BitField<0, 16, u32> shared_alloc;
|
||||
|
||||
BitField<0, 31, u32> block_dim_x;
|
||||
BitField<16, 16, u32> block_dim_x;
|
||||
union {
|
||||
BitField<0, 16, u32> block_dim_y;
|
||||
BitField<16, 16, u32> block_dim_z;
|
||||
@@ -153,7 +153,7 @@ public:
|
||||
|
||||
INSERT_PADDING_WORDS(0x8);
|
||||
|
||||
struct {
|
||||
struct ConstBufferConfig {
|
||||
u32 address_low;
|
||||
union {
|
||||
BitField<0, 8, u32> address_high;
|
||||
@@ -163,7 +163,8 @@ public:
|
||||
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high.Value()) << 32) |
|
||||
address_low);
|
||||
}
|
||||
} const_buffer_config[8];
|
||||
};
|
||||
std::array<ConstBufferConfig, NumConstBuffers> const_buffer_config;
|
||||
|
||||
union {
|
||||
BitField<0, 20, u32> local_pos_alloc;
|
||||
|
||||
@@ -396,12 +396,10 @@ void Maxwell3D::ProcessCBBind(Regs::ShaderStage stage) {
|
||||
auto& shader = state.shader_stages[static_cast<std::size_t>(stage)];
|
||||
auto& bind_data = regs.cb_bind[static_cast<std::size_t>(stage)];
|
||||
|
||||
ASSERT(bind_data.index < Regs::MaxConstBuffers);
|
||||
auto& buffer = shader.const_buffers[bind_data.index];
|
||||
|
||||
ASSERT(bind_data.index < Regs::MaxConstBuffers);
|
||||
|
||||
buffer.enabled = bind_data.valid.Value() != 0;
|
||||
buffer.index = bind_data.index;
|
||||
buffer.address = regs.const_buffer.BufferAddress();
|
||||
buffer.size = regs.const_buffer.cb_size;
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/math_util.h"
|
||||
#include "video_core/engines/const_buffer_info.h"
|
||||
#include "video_core/engines/engine_upload.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/macro_interpreter.h"
|
||||
@@ -1112,13 +1113,6 @@ public:
|
||||
static_assert(std::is_trivially_copyable_v<Regs>, "Maxwell3D Regs must be trivially copyable");
|
||||
|
||||
struct State {
|
||||
struct ConstBufferInfo {
|
||||
GPUVAddr address;
|
||||
u32 index;
|
||||
u32 size;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct ShaderStageInfo {
|
||||
std::array<ConstBufferInfo, Regs::MaxConstBuffers> const_buffers;
|
||||
};
|
||||
|
||||
@@ -163,8 +163,8 @@ private:
|
||||
static constexpr u64 page_size{1 << page_bits};
|
||||
static constexpr u64 page_mask{page_size - 1};
|
||||
|
||||
/// Address space in bits, this is fairly arbitrary but sufficiently large.
|
||||
static constexpr u32 address_space_width{39};
|
||||
/// Address space in bits, according to Tegra X1 TRM
|
||||
static constexpr u32 address_space_width{40};
|
||||
/// Start address for mapping, this is fairly arbitrary but must be non-zero.
|
||||
static constexpr GPUVAddr address_space_base{0x100000};
|
||||
/// End of address space, based on address space in bits.
|
||||
|
||||
@@ -71,16 +71,6 @@ GLintptr OGLBufferCache::UploadHostMemory(const void* raw_pointer, std::size_t s
|
||||
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);
|
||||
}
|
||||
|
||||
bool OGLBufferCache::Map(std::size_t max_size) {
|
||||
bool invalidate;
|
||||
std::tie(buffer_ptr, buffer_offset_base, invalidate) =
|
||||
|
||||
@@ -61,9 +61,6 @@ public:
|
||||
/// 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);
|
||||
|
||||
bool Map(std::size_t max_size);
|
||||
void Unmap();
|
||||
|
||||
|
||||
@@ -1,63 +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/core.h"
|
||||
#include "video_core/memory_manager.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(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 u8* source{memory_manager.GetPointer(gpu_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,31 +0,0 @@
|
||||
// Copyright 2018 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.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(GPUVAddr gpu_addr, std::size_t index_size, u32 count);
|
||||
|
||||
private:
|
||||
OGLBufferCache& buffer_cache;
|
||||
};
|
||||
|
||||
} // namespace OpenGL
|
||||
@@ -246,29 +246,6 @@ DrawParameters RasterizerOpenGL::SetupDraw() {
|
||||
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, regs.vertex_buffer.count);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
params.use_indexed = is_indexed;
|
||||
params.primitive_mode = MaxwellToGL::PrimitiveTopology(regs.draw.topology);
|
||||
|
||||
@@ -345,9 +322,9 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
|
||||
}
|
||||
|
||||
const auto stage_enum = static_cast<Maxwell::ShaderStage>(stage);
|
||||
SetupConstBuffers(stage_enum, shader, program_handle, base_bindings);
|
||||
SetupGlobalRegions(stage_enum, shader, program_handle, base_bindings);
|
||||
SetupTextures(stage_enum, shader, program_handle, base_bindings);
|
||||
SetupDrawConstBuffers(stage_enum, shader);
|
||||
SetupGlobalRegions(stage_enum, shader);
|
||||
SetupTextures(stage_enum, shader, base_bindings);
|
||||
|
||||
// Workaround for Intel drivers.
|
||||
// When a clip distance is enabled but not set in the shader it crops parts of the screen
|
||||
@@ -686,30 +663,19 @@ void RasterizerOpenGL::DrawArrays() {
|
||||
SyncCullMode();
|
||||
SyncPrimitiveRestart();
|
||||
SyncScissorTest(state);
|
||||
// Alpha Testing is synced on shaders.
|
||||
SyncTransformFeedback();
|
||||
SyncPointState();
|
||||
CheckAlphaTests();
|
||||
SyncPolygonOffset();
|
||||
// TODO(bunnei): Sync framebuffer_scale uniform here
|
||||
// TODO(bunnei): Sync scissorbox uniform(s) here
|
||||
SyncAlphaTest();
|
||||
|
||||
// Draw the vertex batch
|
||||
const bool is_indexed = accelerate_draw == AccelDraw::Indexed;
|
||||
|
||||
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(buffer_size, 4) +
|
||||
primitive_assembler.CalculateQuadSize(regs.vertex_buffer.count);
|
||||
break;
|
||||
default:
|
||||
if (is_indexed) {
|
||||
buffer_size = Common::AlignUp(buffer_size, 4) + CalculateIndexBufferSize();
|
||||
}
|
||||
break;
|
||||
// Add space for index buffer
|
||||
if (is_indexed) {
|
||||
buffer_size = Common::AlignUp(buffer_size, 4) + CalculateIndexBufferSize();
|
||||
}
|
||||
|
||||
// Uniform space for the 5 shader stages
|
||||
@@ -810,57 +776,55 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
|
||||
return true;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLuint program_handle,
|
||||
BaseBindings base_bindings) {
|
||||
void RasterizerOpenGL::SetupDrawConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader) {
|
||||
MICROPROFILE_SCOPE(OpenGL_UBO);
|
||||
const auto& gpu = system.GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
const auto& shader_stage = maxwell3d.state.shader_stages[static_cast<std::size_t>(stage)];
|
||||
const auto stage_index = static_cast<std::size_t>(stage);
|
||||
const auto& shader_stage = system.GPU().Maxwell3D().state.shader_stages[stage_index];
|
||||
const auto& entries = shader->GetShaderEntries().const_buffers;
|
||||
|
||||
// Upload only the enabled buffers from the 16 constbuffers of each shader stage
|
||||
for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& used_buffer = entries[bindpoint];
|
||||
const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()];
|
||||
|
||||
if (!buffer.enabled) {
|
||||
// Set values to zero to unbind buffers
|
||||
bind_ubo_pushbuffer.Push(0, 0, 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::size_t size = 0;
|
||||
|
||||
if (used_buffer.IsIndirect()) {
|
||||
// Buffer is accessed indirectly, so upload the entire thing
|
||||
size = buffer.size;
|
||||
|
||||
if (size > MaxConstbufferSize) {
|
||||
LOG_WARNING(Render_OpenGL, "Indirect constbuffer size {} exceeds maximum {}", size,
|
||||
MaxConstbufferSize);
|
||||
size = MaxConstbufferSize;
|
||||
}
|
||||
} else {
|
||||
// Buffer is accessed directly, upload just what we use
|
||||
size = used_buffer.GetSize();
|
||||
}
|
||||
|
||||
// Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140
|
||||
// UBO alignment requirements.
|
||||
size = Common::AlignUp(size, sizeof(GLvec4));
|
||||
ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big");
|
||||
|
||||
const GLintptr const_buffer_offset =
|
||||
buffer_cache.UploadMemory(buffer.address, size, device.GetUniformBufferAlignment());
|
||||
|
||||
bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), const_buffer_offset, size);
|
||||
const auto& entry = entries[bindpoint];
|
||||
SetupConstBuffer(shader_stage.const_buffers[entry.GetIndex()], entry);
|
||||
}
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupConstBuffer(const Tegra::Engines::ConstBufferInfo& buffer,
|
||||
const GLShader::ConstBufferEntry& entry) {
|
||||
if (!buffer.enabled) {
|
||||
// Set values to zero to unbind buffers
|
||||
bind_ubo_pushbuffer.Push(0, 0, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
std::size_t size;
|
||||
if (entry.IsIndirect()) {
|
||||
// Buffer is accessed indirectly, so upload the entire thing
|
||||
size = buffer.size;
|
||||
|
||||
if (size > MaxConstbufferSize) {
|
||||
LOG_WARNING(Render_OpenGL, "Indirect constbuffer size {} exceeds maximum {}", size,
|
||||
MaxConstbufferSize);
|
||||
size = MaxConstbufferSize;
|
||||
}
|
||||
} else {
|
||||
// Buffer is accessed directly, upload just what we use
|
||||
size = entry.GetSize();
|
||||
}
|
||||
|
||||
// Align the actual size so it ends up being a multiple of vec4 to meet the OpenGL std140
|
||||
// UBO alignment requirements.
|
||||
size = Common::AlignUp(size, sizeof(GLvec4));
|
||||
ASSERT_MSG(size <= MaxConstbufferSize, "Constant buffer is too big");
|
||||
|
||||
const std::size_t alignment = device.GetUniformBufferAlignment();
|
||||
const GLintptr offset = buffer_cache.UploadMemory(buffer.address, size, alignment);
|
||||
bind_ubo_pushbuffer.Push(buffer_cache.GetHandle(), offset, size);
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLenum primitive_mode,
|
||||
BaseBindings base_bindings) {
|
||||
const Shader& shader) {
|
||||
const auto& entries = shader->GetShaderEntries().global_memory_entries;
|
||||
for (std::size_t bindpoint = 0; bindpoint < entries.size(); ++bindpoint) {
|
||||
const auto& entry{entries[bindpoint]};
|
||||
@@ -874,7 +838,7 @@ void RasterizerOpenGL::SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::Shade
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings) {
|
||||
BaseBindings base_bindings) {
|
||||
MICROPROFILE_SCOPE(OpenGL_Texture);
|
||||
const auto& gpu = system.GPU();
|
||||
const auto& maxwell3d = gpu.Maxwell3D();
|
||||
@@ -1152,10 +1116,17 @@ void RasterizerOpenGL::SyncPolygonOffset() {
|
||||
state.polygon_offset.clamp = regs.polygon_offset_clamp;
|
||||
}
|
||||
|
||||
void RasterizerOpenGL::CheckAlphaTests() {
|
||||
void RasterizerOpenGL::SyncAlphaTest() {
|
||||
const auto& regs = system.GPU().Maxwell3D().regs;
|
||||
UNIMPLEMENTED_IF_MSG(regs.alpha_test_enabled != 0 && regs.rt_control.count > 1,
|
||||
"Alpha Testing is enabled with more than one rendertarget");
|
||||
|
||||
state.alpha_test.enabled = regs.alpha_test_enabled;
|
||||
if (!state.alpha_test.enabled) {
|
||||
return;
|
||||
}
|
||||
state.alpha_test.func = MaxwellToGL::ComparisonOp(regs.alpha_test_func);
|
||||
state.alpha_test.ref = regs.alpha_test_ref;
|
||||
}
|
||||
|
||||
} // namespace OpenGL
|
||||
|
||||
@@ -17,17 +17,18 @@
|
||||
#include <glad/glad.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/const_buffer_info.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#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_device.h"
|
||||
#include "video_core/renderer_opengl/gl_global_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_sampler_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_cache.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
|
||||
#include "video_core/renderer_opengl/gl_shader_manager.h"
|
||||
#include "video_core/renderer_opengl/gl_state.h"
|
||||
#include "video_core/renderer_opengl/utils.h"
|
||||
@@ -106,17 +107,20 @@ private:
|
||||
bool preserve_contents = true, std::optional<std::size_t> single_color_target = {});
|
||||
|
||||
/// Configures the current constbuffers to use for the draw command.
|
||||
void SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings);
|
||||
void SetupDrawConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader);
|
||||
|
||||
/// Configures a constant buffer.
|
||||
void SetupConstBuffer(const Tegra::Engines::ConstBufferInfo& buffer,
|
||||
const GLShader::ConstBufferEntry& entry);
|
||||
|
||||
/// Configures the current global memory entries to use for the draw command.
|
||||
void SetupGlobalRegions(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage,
|
||||
const Shader& shader, GLenum primitive_mode,
|
||||
BaseBindings base_bindings);
|
||||
const Shader& shader);
|
||||
|
||||
/// Configures the current textures to use for the draw command.
|
||||
void SetupTextures(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, const Shader& shader,
|
||||
GLuint program_handle, BaseBindings base_bindings);
|
||||
BaseBindings base_bindings);
|
||||
|
||||
/// Syncs the viewport and depth range to match the guest state
|
||||
void SyncViewport(OpenGLState& current_state);
|
||||
@@ -167,8 +171,8 @@ private:
|
||||
/// Syncs the polygon offsets
|
||||
void SyncPolygonOffset();
|
||||
|
||||
/// Check asserts for alpha testing.
|
||||
void CheckAlphaTests();
|
||||
/// Syncs the alpha test state to match the guest state
|
||||
void SyncAlphaTest();
|
||||
|
||||
/// Check for extension that are not strictly required
|
||||
/// but are needed for correct emulation
|
||||
@@ -197,7 +201,6 @@ private:
|
||||
|
||||
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
|
||||
OGLBufferCache buffer_cache;
|
||||
PrimitiveAssembler primitive_assembler{buffer_cache};
|
||||
|
||||
BindBuffersRangePushBuffer bind_ubo_pushbuffer{GL_UNIFORM_BUFFER};
|
||||
BindBuffersRangePushBuffer bind_ssbo_pushbuffer{GL_SHADER_STORAGE_BUFFER};
|
||||
|
||||
@@ -45,7 +45,6 @@ struct TextureAoffi {};
|
||||
using TextureArgument = std::pair<Type, Node>;
|
||||
using TextureIR = std::variant<TextureAoffi, TextureArgument>;
|
||||
|
||||
enum : u32 { POSITION_VARYING_LOCATION = 0, GENERIC_VARYING_START_LOCATION = 1 };
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS =
|
||||
static_cast<u32>(RasterizerOpenGL::MaxConstbufferSize) / (4 * sizeof(float));
|
||||
|
||||
@@ -124,8 +123,8 @@ bool IsPrecise(Operation operand) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsPrecise(Node node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
bool IsPrecise(const Node& node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
||||
return IsPrecise(*operation);
|
||||
}
|
||||
return false;
|
||||
@@ -144,6 +143,24 @@ u32 GetGenericAttributeIndex(Attribute::Index index) {
|
||||
return static_cast<u32>(index) - static_cast<u32>(Attribute::Index::Attribute_0);
|
||||
}
|
||||
|
||||
constexpr const char* GetFlowStackPrefix(MetaStackClass stack) {
|
||||
switch (stack) {
|
||||
case MetaStackClass::Ssy:
|
||||
return "ssy";
|
||||
case MetaStackClass::Pbk:
|
||||
return "pbk";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string FlowStackName(MetaStackClass stack) {
|
||||
return fmt::format("{}_flow_stack", GetFlowStackPrefix(stack));
|
||||
}
|
||||
|
||||
std::string FlowStackTopName(MetaStackClass stack) {
|
||||
return fmt::format("{}_flow_stack_top", GetFlowStackPrefix(stack));
|
||||
}
|
||||
|
||||
class GLSLDecompiler final {
|
||||
public:
|
||||
explicit GLSLDecompiler(const Device& device, const ShaderIR& ir, ShaderStage stage,
|
||||
@@ -174,8 +191,10 @@ public:
|
||||
// TODO(Subv): Figure out the actual depth of the flow stack, for now it seems
|
||||
// unlikely that shaders will use 20 nested SSYs and PBKs.
|
||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
code.AddLine("uint flow_stack[{}];", FLOW_STACK_SIZE);
|
||||
code.AddLine("uint flow_stack_top = 0u;");
|
||||
for (const auto stack : std::array{MetaStackClass::Ssy, MetaStackClass::Pbk}) {
|
||||
code.AddLine("uint {}[{}];", FlowStackName(stack), FLOW_STACK_SIZE);
|
||||
code.AddLine("uint {} = 0u;", FlowStackTopName(stack));
|
||||
}
|
||||
|
||||
code.AddLine("while (true) {{");
|
||||
++code.scope;
|
||||
@@ -247,6 +266,12 @@ private:
|
||||
code.AddLine("layout ({}, max_vertices = {}) out;", topology, max_vertices);
|
||||
code.AddNewLine();
|
||||
|
||||
code.AddLine("in gl_PerVertex {{");
|
||||
++code.scope;
|
||||
code.AddLine("vec4 gl_Position;");
|
||||
--code.scope;
|
||||
code.AddLine("}} gl_in[];");
|
||||
|
||||
DeclareVertexRedeclarations();
|
||||
}
|
||||
|
||||
@@ -349,7 +374,7 @@ private:
|
||||
}
|
||||
|
||||
void DeclareInputAttribute(Attribute::Index index, bool skip_unused) {
|
||||
const u32 generic_index{GetGenericAttributeIndex(index)};
|
||||
const u32 location{GetGenericAttributeIndex(index)};
|
||||
|
||||
std::string name{GetInputAttribute(index)};
|
||||
if (stage == ShaderStage::Geometry) {
|
||||
@@ -358,19 +383,13 @@ private:
|
||||
|
||||
std::string suffix;
|
||||
if (stage == ShaderStage::Fragment) {
|
||||
const auto input_mode{header.ps.GetAttributeUse(generic_index)};
|
||||
const auto input_mode{header.ps.GetAttributeUse(location)};
|
||||
if (skip_unused && input_mode == AttributeUse::Unused) {
|
||||
return;
|
||||
}
|
||||
suffix = GetInputFlags(input_mode);
|
||||
}
|
||||
|
||||
u32 location = generic_index;
|
||||
if (stage != ShaderStage::Vertex) {
|
||||
// If inputs are varyings, add an offset
|
||||
location += GENERIC_VARYING_START_LOCATION;
|
||||
}
|
||||
|
||||
code.AddLine("layout (location = {}) {} in vec4 {};", location, suffix, name);
|
||||
}
|
||||
|
||||
@@ -395,7 +414,7 @@ private:
|
||||
}
|
||||
|
||||
void DeclareOutputAttribute(Attribute::Index index) {
|
||||
const u32 location{GetGenericAttributeIndex(index) + GENERIC_VARYING_START_LOCATION};
|
||||
const u32 location{GetGenericAttributeIndex(index)};
|
||||
code.AddLine("layout (location = {}) out vec4 {};", location, GetOutputAttribute(index));
|
||||
}
|
||||
|
||||
@@ -498,15 +517,15 @@ private:
|
||||
}
|
||||
|
||||
void VisitBlock(const NodeBlock& bb) {
|
||||
for (const Node node : bb) {
|
||||
for (const auto& node : bb) {
|
||||
if (const std::string expr = Visit(node); !expr.empty()) {
|
||||
code.AddLine(expr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Visit(Node node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
std::string Visit(const Node& node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
||||
const auto operation_index = static_cast<std::size_t>(operation->GetCode());
|
||||
if (operation_index >= operation_decompilers.size()) {
|
||||
UNREACHABLE_MSG("Out of bounds operation: {}", operation_index);
|
||||
@@ -520,7 +539,7 @@ private:
|
||||
return (this->*decompiler)(*operation);
|
||||
}
|
||||
|
||||
if (const auto gpr = std::get_if<GprNode>(node)) {
|
||||
if (const auto gpr = std::get_if<GprNode>(&*node)) {
|
||||
const u32 index = gpr->GetIndex();
|
||||
if (index == Register::ZeroIndex) {
|
||||
return "0";
|
||||
@@ -528,7 +547,7 @@ private:
|
||||
return GetRegister(index);
|
||||
}
|
||||
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(node)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
|
||||
const u32 value = immediate->GetValue();
|
||||
if (value < 10) {
|
||||
// For eyecandy avoid using hex numbers on single digits
|
||||
@@ -537,7 +556,7 @@ private:
|
||||
return fmt::format("utof(0x{:x}u)", immediate->GetValue());
|
||||
}
|
||||
|
||||
if (const auto predicate = std::get_if<PredicateNode>(node)) {
|
||||
if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
|
||||
const auto value = [&]() -> std::string {
|
||||
switch (const auto index = predicate->GetIndex(); index) {
|
||||
case Tegra::Shader::Pred::UnusedIndex:
|
||||
@@ -554,7 +573,7 @@ private:
|
||||
return value;
|
||||
}
|
||||
|
||||
if (const auto abuf = std::get_if<AbufNode>(node)) {
|
||||
if (const auto abuf = std::get_if<AbufNode>(&*node)) {
|
||||
UNIMPLEMENTED_IF_MSG(abuf->IsPhysicalBuffer() && stage == ShaderStage::Geometry,
|
||||
"Physical attributes in geometry shaders are not implemented");
|
||||
if (abuf->IsPhysicalBuffer()) {
|
||||
@@ -564,9 +583,9 @@ private:
|
||||
return ReadAttribute(abuf->GetIndex(), abuf->GetElement(), abuf->GetBuffer());
|
||||
}
|
||||
|
||||
if (const auto cbuf = std::get_if<CbufNode>(node)) {
|
||||
if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
|
||||
const Node offset = cbuf->GetOffset();
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
|
||||
// Direct access
|
||||
const u32 offset_imm = immediate->GetValue();
|
||||
ASSERT_MSG(offset_imm % 4 == 0, "Unaligned cbuf direct access");
|
||||
@@ -602,22 +621,22 @@ private:
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
}
|
||||
|
||||
if (const auto gmem = std::get_if<GmemNode>(node)) {
|
||||
if (const auto gmem = std::get_if<GmemNode>(&*node)) {
|
||||
const std::string real = Visit(gmem->GetRealAddress());
|
||||
const std::string base = Visit(gmem->GetBaseAddress());
|
||||
const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
|
||||
return fmt::format("{}[{}]", GetGlobalMemory(gmem->GetDescriptor()), final_offset);
|
||||
}
|
||||
|
||||
if (const auto lmem = std::get_if<LmemNode>(node)) {
|
||||
if (const auto lmem = std::get_if<LmemNode>(&*node)) {
|
||||
return fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
|
||||
}
|
||||
|
||||
if (const auto internal_flag = std::get_if<InternalFlagNode>(node)) {
|
||||
if (const auto internal_flag = std::get_if<InternalFlagNode>(&*node)) {
|
||||
return GetInternalFlag(internal_flag->GetFlag());
|
||||
}
|
||||
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(node)) {
|
||||
if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
|
||||
// It's invalid to call conditional on nested nodes, use an operation instead
|
||||
code.AddLine("if ({}) {{", Visit(conditional->GetCondition()));
|
||||
++code.scope;
|
||||
@@ -629,7 +648,7 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
if (const auto comment = std::get_if<CommentNode>(node)) {
|
||||
if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
return "// " + comment->GetText();
|
||||
}
|
||||
|
||||
@@ -637,7 +656,7 @@ private:
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string ReadAttribute(Attribute::Index attribute, u32 element, Node buffer = {}) {
|
||||
std::string ReadAttribute(Attribute::Index attribute, u32 element, const Node& buffer = {}) {
|
||||
const auto GeometryPass = [&](std::string_view name) {
|
||||
if (stage == ShaderStage::Geometry && buffer) {
|
||||
// TODO(Rodrigo): Guard geometry inputs against out of bound reads. Some games
|
||||
@@ -650,10 +669,14 @@ private:
|
||||
|
||||
switch (attribute) {
|
||||
case Attribute::Index::Position:
|
||||
if (stage != ShaderStage::Fragment) {
|
||||
return GeometryPass("position") + GetSwizzle(element);
|
||||
} else {
|
||||
switch (stage) {
|
||||
case ShaderStage::Geometry:
|
||||
return fmt::format("gl_in[ftou({})].gl_Position{}", Visit(buffer),
|
||||
GetSwizzle(element));
|
||||
case ShaderStage::Fragment:
|
||||
return element == 3 ? "1.0f" : ("gl_FragCoord"s + GetSwizzle(element));
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
case Attribute::Index::PointCoord:
|
||||
switch (element) {
|
||||
@@ -869,7 +892,7 @@ private:
|
||||
std::string expr = ", ";
|
||||
switch (type) {
|
||||
case Type::Int:
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
|
||||
// Inline the string as an immediate integer in GLSL (some extra arguments are
|
||||
// required to be constant)
|
||||
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
@@ -901,7 +924,7 @@ private:
|
||||
|
||||
for (std::size_t index = 0; index < aoffi.size(); ++index) {
|
||||
const auto operand{aoffi.at(index)};
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(operand)) {
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*operand)) {
|
||||
// Inline the string as an immediate integer in GLSL (AOFFI arguments are required
|
||||
// to be constant by the standard).
|
||||
expr += std::to_string(static_cast<s32>(immediate->GetValue()));
|
||||
@@ -922,23 +945,23 @@ private:
|
||||
}
|
||||
|
||||
std::string Assign(Operation operation) {
|
||||
const Node dest = operation[0];
|
||||
const Node src = operation[1];
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
|
||||
std::string target;
|
||||
if (const auto gpr = std::get_if<GprNode>(dest)) {
|
||||
if (const auto gpr = std::get_if<GprNode>(&*dest)) {
|
||||
if (gpr->GetIndex() == Register::ZeroIndex) {
|
||||
// Writing to Register::ZeroIndex is a no op
|
||||
return {};
|
||||
}
|
||||
target = GetRegister(gpr->GetIndex());
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(dest)) {
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
|
||||
UNIMPLEMENTED_IF(abuf->IsPhysicalBuffer());
|
||||
|
||||
target = [&]() -> std::string {
|
||||
switch (const auto attribute = abuf->GetIndex(); abuf->GetIndex()) {
|
||||
case Attribute::Index::Position:
|
||||
return "position"s + GetSwizzle(abuf->GetElement());
|
||||
return "gl_Position"s + GetSwizzle(abuf->GetElement());
|
||||
case Attribute::Index::PointSize:
|
||||
return "gl_PointSize";
|
||||
case Attribute::Index::ClipDistances0123:
|
||||
@@ -954,9 +977,9 @@ private:
|
||||
return "0";
|
||||
}
|
||||
}();
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(dest)) {
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
|
||||
target = fmt::format("{}[ftou({}) / 4]", GetLocalMemory(), Visit(lmem->GetAddress()));
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(dest)) {
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
|
||||
const std::string real = Visit(gmem->GetRealAddress());
|
||||
const std::string base = Visit(gmem->GetBaseAddress());
|
||||
const std::string final_offset = fmt::format("(ftou({}) - ftou({})) / 4", real, base);
|
||||
@@ -1233,12 +1256,12 @@ private:
|
||||
}
|
||||
|
||||
std::string LogicalAssign(Operation operation) {
|
||||
const Node dest = operation[0];
|
||||
const Node src = operation[1];
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
|
||||
std::string target;
|
||||
|
||||
if (const auto pred = std::get_if<PredicateNode>(dest)) {
|
||||
if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
|
||||
ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
|
||||
|
||||
const auto index = pred->GetIndex();
|
||||
@@ -1249,7 +1272,7 @@ private:
|
||||
return {};
|
||||
}
|
||||
target = GetPredicate(index);
|
||||
} else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
|
||||
} else if (const auto flag = std::get_if<InternalFlagNode>(&*dest)) {
|
||||
target = GetInternalFlag(flag->GetFlag());
|
||||
}
|
||||
|
||||
@@ -1426,7 +1449,7 @@ private:
|
||||
}
|
||||
|
||||
std::string Branch(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
|
||||
code.AddLine("jmp_to = 0x{:x}u;", target->GetValue());
|
||||
@@ -1435,15 +1458,18 @@ private:
|
||||
}
|
||||
|
||||
std::string PushFlowStack(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
|
||||
code.AddLine("flow_stack[flow_stack_top++] = 0x{:x}u;", target->GetValue());
|
||||
code.AddLine("{}[{}++] = 0x{:x}u;", FlowStackName(stack), FlowStackTopName(stack),
|
||||
target->GetValue());
|
||||
return {};
|
||||
}
|
||||
|
||||
std::string PopFlowStack(Operation operation) {
|
||||
code.AddLine("jmp_to = flow_stack[--flow_stack_top];");
|
||||
const auto stack = std::get<MetaStackClass>(operation.GetMeta());
|
||||
code.AddLine("jmp_to = {}[--{}];", FlowStackName(stack), FlowStackTopName(stack));
|
||||
code.AddLine("break;");
|
||||
return {};
|
||||
}
|
||||
@@ -1464,27 +1490,9 @@ private:
|
||||
|
||||
UNIMPLEMENTED_IF_MSG(header.ps.omap.sample_mask != 0, "Sample mask write is unimplemented");
|
||||
|
||||
code.AddLine("if (alpha_test[0] != 0) {{");
|
||||
++code.scope;
|
||||
// We start on the register containing the alpha value in the first RT.
|
||||
u32 current_reg = 3;
|
||||
for (u32 render_target = 0; render_target < Maxwell::NumRenderTargets; ++render_target) {
|
||||
// TODO(Blinkhawk): verify the behavior of alpha testing on hardware when
|
||||
// multiple render targets are used.
|
||||
if (header.ps.IsColorComponentOutputEnabled(render_target, 0) ||
|
||||
header.ps.IsColorComponentOutputEnabled(render_target, 1) ||
|
||||
header.ps.IsColorComponentOutputEnabled(render_target, 2) ||
|
||||
header.ps.IsColorComponentOutputEnabled(render_target, 3)) {
|
||||
code.AddLine("if (!AlphaFunc({})) discard;", SafeGetRegister(current_reg));
|
||||
current_reg += 4;
|
||||
}
|
||||
}
|
||||
--code.scope;
|
||||
code.AddLine("}}");
|
||||
|
||||
// Write the color outputs using the data in the shader registers, disabled
|
||||
// rendertargets/components are skipped in the register assignment.
|
||||
current_reg = 0;
|
||||
u32 current_reg = 0;
|
||||
for (u32 render_target = 0; render_target < Maxwell::NumRenderTargets; ++render_target) {
|
||||
// TODO(Subv): Figure out how dual-source blending is configured in the Switch.
|
||||
for (u32 component = 0; component < 4; ++component) {
|
||||
@@ -1523,9 +1531,7 @@ private:
|
||||
|
||||
// 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.
|
||||
code.AddLine("position.xy *= viewport_flip.xy;");
|
||||
code.AddLine("gl_Position = position;");
|
||||
code.AddLine("position.w = 1.0;");
|
||||
code.AddLine("gl_Position.xy *= viewport_flip.xy;");
|
||||
code.AddLine("EmitVertex();");
|
||||
return {};
|
||||
}
|
||||
@@ -1763,8 +1769,7 @@ private:
|
||||
}
|
||||
|
||||
u32 GetNumPhysicalVaryings() const {
|
||||
return std::min<u32>(device.GetMaxVaryings() - GENERIC_VARYING_START_LOCATION,
|
||||
Maxwell::NumVaryings);
|
||||
return std::min<u32>(device.GetMaxVaryings(), Maxwell::NumVaryings);
|
||||
}
|
||||
|
||||
const Device& device;
|
||||
|
||||
@@ -23,12 +23,9 @@ ProgramResult GenerateVertexShader(const Device& device, const ShaderSetup& setu
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
out += R"(
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
uvec4 alpha_test;
|
||||
};
|
||||
|
||||
)";
|
||||
@@ -48,7 +45,6 @@ layout (std140, binding = EMULATION_UBO_BINDING) uniform vs_config {
|
||||
|
||||
out += R"(
|
||||
void main() {
|
||||
position = vec4(0.0, 0.0, 0.0, 0.0);
|
||||
execute_vertex();
|
||||
)";
|
||||
|
||||
@@ -59,19 +55,12 @@ void main() {
|
||||
out += R"(
|
||||
|
||||
// Set Position Y direction
|
||||
position.y *= utof(config_pack[2]);
|
||||
gl_Position.y *= utof(config_pack[2]);
|
||||
// Check if the flip stage is VertexB
|
||||
// Config pack's second value is flip_stage
|
||||
if (config_pack[1] == 1) {
|
||||
// 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 (config_pack[1] == 1) {
|
||||
position.w = 1.0;
|
||||
gl_Position.xy *= viewport_flip.xy;
|
||||
}
|
||||
})";
|
||||
|
||||
@@ -85,13 +74,9 @@ ProgramResult GenerateGeometryShader(const Device& device, const ShaderSetup& se
|
||||
out += GetCommonDeclarations();
|
||||
|
||||
out += R"(
|
||||
layout (location = 0) in vec4 gs_position[];
|
||||
layout (location = 0) out vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform gs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
uvec4 alpha_test;
|
||||
};
|
||||
|
||||
)";
|
||||
@@ -124,38 +109,11 @@ layout (location = 5) out vec4 FragColor5;
|
||||
layout (location = 6) out vec4 FragColor6;
|
||||
layout (location = 7) out vec4 FragColor7;
|
||||
|
||||
layout (location = 0) in noperspective vec4 position;
|
||||
|
||||
layout (std140, binding = EMULATION_UBO_BINDING) uniform fs_config {
|
||||
vec4 viewport_flip;
|
||||
uvec4 config_pack; // instance_id, flip_stage, y_direction, padding
|
||||
uvec4 alpha_test;
|
||||
};
|
||||
|
||||
bool AlphaFunc(in float value) {
|
||||
float ref = uintBitsToFloat(alpha_test[2]);
|
||||
switch (alpha_test[1]) {
|
||||
case 1:
|
||||
return false;
|
||||
case 2:
|
||||
return value < ref;
|
||||
case 3:
|
||||
return value == ref;
|
||||
case 4:
|
||||
return value <= ref;
|
||||
case 5:
|
||||
return value > ref;
|
||||
case 6:
|
||||
return value != ref;
|
||||
case 7:
|
||||
return value >= ref;
|
||||
case 8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
)";
|
||||
const ShaderIR program_ir(setup.program.code, PROGRAM_OFFSET);
|
||||
ProgramResult program =
|
||||
|
||||
@@ -48,17 +48,6 @@ void MaxwellUniformData::SetFromRegs(const Maxwell3D& maxwell, std::size_t shade
|
||||
viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f;
|
||||
viewport_flip[1] = regs.viewport_transform[0].scale_y < 0.0 ? -1.0f : 1.0f;
|
||||
|
||||
auto func{static_cast<u32>(regs.alpha_test_func)};
|
||||
// Normalize the gl variants of opCompare to be the same as the normal variants
|
||||
const u32 op_gl_variant_base = static_cast<u32>(Maxwell3D::Regs::ComparisonOp::Never);
|
||||
if (func >= op_gl_variant_base) {
|
||||
func = func - op_gl_variant_base + 1U;
|
||||
}
|
||||
|
||||
alpha_test.enabled = regs.alpha_test_enabled;
|
||||
alpha_test.func = func;
|
||||
alpha_test.ref = regs.alpha_test_ref;
|
||||
|
||||
instance_id = state.current_instance;
|
||||
|
||||
// Assign in which stage the position has to be flipped
|
||||
|
||||
@@ -27,14 +27,8 @@ struct MaxwellUniformData {
|
||||
GLuint flip_stage;
|
||||
GLfloat y_direction;
|
||||
};
|
||||
struct alignas(16) {
|
||||
GLuint enabled;
|
||||
GLuint func;
|
||||
GLfloat ref;
|
||||
GLuint padding;
|
||||
} alpha_test;
|
||||
};
|
||||
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");
|
||||
|
||||
|
||||
@@ -156,6 +156,10 @@ OpenGLState::OpenGLState() {
|
||||
polygon_offset.factor = 0.0f;
|
||||
polygon_offset.units = 0.0f;
|
||||
polygon_offset.clamp = 0.0f;
|
||||
|
||||
alpha_test.enabled = false;
|
||||
alpha_test.func = GL_ALWAYS;
|
||||
alpha_test.ref = 0.0f;
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyDefaultState() {
|
||||
@@ -461,6 +465,14 @@ void OpenGLState::ApplyPolygonOffset() const {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyAlphaTest() const {
|
||||
Enable(GL_ALPHA_TEST, cur_state.alpha_test.enabled, alpha_test.enabled);
|
||||
if (UpdateTie(std::tie(cur_state.alpha_test.func, cur_state.alpha_test.ref),
|
||||
std::tie(alpha_test.func, alpha_test.ref))) {
|
||||
glAlphaFunc(alpha_test.func, alpha_test.ref);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLState::ApplyTextures() const {
|
||||
bool has_delta{};
|
||||
std::size_t first{};
|
||||
@@ -533,6 +545,7 @@ void OpenGLState::Apply() const {
|
||||
ApplyTextures();
|
||||
ApplySamplers();
|
||||
ApplyPolygonOffset();
|
||||
ApplyAlphaTest();
|
||||
}
|
||||
|
||||
void OpenGLState::EmulateViewportWithScissor() {
|
||||
|
||||
@@ -172,6 +172,12 @@ public:
|
||||
GLfloat clamp;
|
||||
} polygon_offset;
|
||||
|
||||
struct {
|
||||
bool enabled; // GL_ALPHA_TEST
|
||||
GLenum func; // GL_ALPHA_TEST_FUNC
|
||||
GLfloat ref; // GL_ALPHA_TEST_REF
|
||||
} alpha_test;
|
||||
|
||||
std::array<bool, 8> clip_distance; // GL_CLIP_DISTANCE
|
||||
|
||||
OpenGLState();
|
||||
@@ -215,6 +221,7 @@ public:
|
||||
void ApplySamplers() const;
|
||||
void ApplyDepthClamp() const;
|
||||
void ApplyPolygonOffset() const;
|
||||
void ApplyAlphaTest() const;
|
||||
|
||||
/// Set the initial OpenGL state
|
||||
static void ApplyDefaultState();
|
||||
|
||||
@@ -128,6 +128,8 @@ inline GLenum PrimitiveTopology(Maxwell::PrimitiveTopology topology) {
|
||||
return GL_TRIANGLE_STRIP;
|
||||
case Maxwell::PrimitiveTopology::TriangleFan:
|
||||
return GL_TRIANGLE_FAN;
|
||||
case Maxwell::PrimitiveTopology::Quads:
|
||||
return GL_QUADS;
|
||||
default:
|
||||
LOG_CRITICAL(Render_OpenGL, "Unimplemented topology={}", static_cast<u32>(topology));
|
||||
UNREACHABLE();
|
||||
@@ -173,11 +175,8 @@ inline GLenum WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
return GL_CLAMP_TO_EDGE;
|
||||
case Tegra::Texture::WrapMode::Border:
|
||||
return GL_CLAMP_TO_BORDER;
|
||||
case Tegra::Texture::WrapMode::ClampOGL:
|
||||
// TODO(Subv): GL_CLAMP was removed as of OpenGL 3.1, to implement GL_CLAMP, we can use
|
||||
// GL_CLAMP_TO_BORDER to get the border color of the texture, and then sample the edge to
|
||||
// manually mix them. However the shader part of this is not yet implemented.
|
||||
return GL_CLAMP_TO_BORDER;
|
||||
case Tegra::Texture::WrapMode::Clamp:
|
||||
return GL_CLAMP;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceClampToEdge:
|
||||
return GL_MIRROR_CLAMP_TO_EDGE;
|
||||
case Tegra::Texture::WrapMode::MirrorOnceBorder:
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#include "core/perf_stats.h"
|
||||
#include "core/settings.h"
|
||||
#include "core/telemetry_session.h"
|
||||
#include "core/tracer/recorder.h"
|
||||
#include "video_core/morton.h"
|
||||
#include "video_core/renderer_opengl/gl_rasterizer.h"
|
||||
#include "video_core/renderer_opengl/renderer_opengl.h"
|
||||
|
||||
@@ -52,7 +52,7 @@ vk::SamplerAddressMode WrapMode(Tegra::Texture::WrapMode wrap_mode) {
|
||||
return vk::SamplerAddressMode::eClampToEdge;
|
||||
case Tegra::Texture::WrapMode::Border:
|
||||
return vk::SamplerAddressMode::eClampToBorder;
|
||||
case Tegra::Texture::WrapMode::ClampOGL:
|
||||
case Tegra::Texture::WrapMode::Clamp:
|
||||
// TODO(Rodrigo): GL_CLAMP was removed as of OpenGL 3.1, to implement GL_CLAMP, we can use
|
||||
// eClampToBorder to get the border color of the texture, and then sample the edge to
|
||||
// manually mix them. However the shader part of this is not yet implemented.
|
||||
|
||||
@@ -18,6 +18,7 @@ constexpr std::array<vk::Format, 3> Depth24UnormS8Uint = {
|
||||
vk::Format::eD32SfloatS8Uint, vk::Format::eD16UnormS8Uint, {}};
|
||||
constexpr std::array<vk::Format, 3> Depth16UnormS8Uint = {
|
||||
vk::Format::eD24UnormS8Uint, vk::Format::eD32SfloatS8Uint, {}};
|
||||
constexpr std::array<vk::Format, 2> Astc = {vk::Format::eA8B8G8R8UnormPack32, {}};
|
||||
|
||||
} // namespace Alternatives
|
||||
|
||||
@@ -51,15 +52,19 @@ VKDevice::VKDevice(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice phy
|
||||
: physical{physical}, format_properties{GetFormatProperties(dldi, physical)} {
|
||||
SetupFamilies(dldi, surface);
|
||||
SetupProperties(dldi);
|
||||
SetupFeatures(dldi);
|
||||
}
|
||||
|
||||
VKDevice::~VKDevice() = default;
|
||||
|
||||
bool VKDevice::Create(const vk::DispatchLoaderDynamic& dldi, vk::Instance instance) {
|
||||
const auto queue_cis = GetDeviceQueueCreateInfos();
|
||||
vk::PhysicalDeviceFeatures device_features{};
|
||||
vk::PhysicalDeviceFeatures device_features;
|
||||
device_features.vertexPipelineStoresAndAtomics = true;
|
||||
device_features.independentBlend = true;
|
||||
device_features.textureCompressionASTC_LDR = is_optimal_astc_supported;
|
||||
|
||||
const std::vector<const char*> extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||
const auto queue_cis = GetDeviceQueueCreateInfos();
|
||||
const std::vector<const char*> extensions = LoadExtensions(dldi);
|
||||
const vk::DeviceCreateInfo device_ci({}, static_cast<u32>(queue_cis.size()), queue_cis.data(),
|
||||
0, nullptr, static_cast<u32>(extensions.size()),
|
||||
extensions.data(), &device_features);
|
||||
@@ -90,7 +95,7 @@ vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format,
|
||||
LOG_CRITICAL(Render_Vulkan,
|
||||
"Format={} with usage={} and type={} has no defined alternatives and host "
|
||||
"hardware does not support it",
|
||||
static_cast<u32>(wanted_format), static_cast<u32>(wanted_usage),
|
||||
vk::to_string(wanted_format), vk::to_string(wanted_usage),
|
||||
static_cast<u32>(format_type));
|
||||
UNREACHABLE();
|
||||
return wanted_format;
|
||||
@@ -118,6 +123,30 @@ vk::Format VKDevice::GetSupportedFormat(vk::Format wanted_format,
|
||||
return wanted_format;
|
||||
}
|
||||
|
||||
bool VKDevice::IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
|
||||
const vk::DispatchLoaderDynamic& dldi) const {
|
||||
if (!features.textureCompressionASTC_LDR) {
|
||||
return false;
|
||||
}
|
||||
const auto format_feature_usage{
|
||||
vk::FormatFeatureFlagBits::eSampledImage | vk::FormatFeatureFlagBits::eBlitSrc |
|
||||
vk::FormatFeatureFlagBits::eBlitDst | vk::FormatFeatureFlagBits::eTransferSrc |
|
||||
vk::FormatFeatureFlagBits::eTransferDst};
|
||||
constexpr std::array<vk::Format, 9> astc_formats = {
|
||||
vk::Format::eAstc4x4UnormBlock, vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock, vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc5x4SrgbBlock, vk::Format::eAstc5x5UnormBlock,
|
||||
vk::Format::eAstc5x5SrgbBlock, vk::Format::eAstc10x8UnormBlock,
|
||||
vk::Format::eAstc10x8SrgbBlock};
|
||||
for (const auto format : astc_formats) {
|
||||
const auto format_properties{physical.getFormatProperties(format, dldi)};
|
||||
if (!(format_properties.optimalTilingFeatures & format_feature_usage)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
||||
FormatType format_type) const {
|
||||
const auto it = format_properties.find(wanted_format);
|
||||
@@ -132,11 +161,9 @@ bool VKDevice::IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlag
|
||||
|
||||
bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
||||
vk::SurfaceKHR surface) {
|
||||
const std::string swapchain_extension = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
|
||||
|
||||
bool has_swapchain{};
|
||||
for (const auto& prop : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
|
||||
has_swapchain |= prop.extensionName == swapchain_extension;
|
||||
has_swapchain |= prop.extensionName == std::string(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
}
|
||||
if (!has_swapchain) {
|
||||
// The device doesn't support creating swapchains.
|
||||
@@ -160,8 +187,14 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Check if the device matches all requeriments.
|
||||
const vk::PhysicalDeviceProperties props = physical.getProperties(dldi);
|
||||
if (props.limits.maxUniformBufferRange < 65536) {
|
||||
const auto properties{physical.getProperties(dldi)};
|
||||
const auto limits{properties.limits};
|
||||
if (limits.maxUniformBufferRange < 65536) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const vk::PhysicalDeviceFeatures features{physical.getFeatures(dldi)};
|
||||
if (!features.vertexPipelineStoresAndAtomics || !features.independentBlend) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -169,6 +202,30 @@ bool VKDevice::IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDev
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<const char*> VKDevice::LoadExtensions(const vk::DispatchLoaderDynamic& dldi) {
|
||||
std::vector<const char*> extensions;
|
||||
extensions.reserve(2);
|
||||
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
|
||||
const auto Test = [&](const vk::ExtensionProperties& extension,
|
||||
std::optional<std::reference_wrapper<bool>> status, const char* name,
|
||||
u32 revision) {
|
||||
if (extension.extensionName != std::string(name)) {
|
||||
return;
|
||||
}
|
||||
extensions.push_back(name);
|
||||
if (status) {
|
||||
status->get() = true;
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& extension : physical.enumerateDeviceExtensionProperties(nullptr, dldi)) {
|
||||
Test(extension, ext_scalar_block_layout, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, 1);
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
void VKDevice::SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface) {
|
||||
std::optional<u32> graphics_family_, present_family_;
|
||||
|
||||
@@ -196,10 +253,16 @@ void VKDevice::SetupProperties(const vk::DispatchLoaderDynamic& dldi) {
|
||||
const vk::PhysicalDeviceProperties props = physical.getProperties(dldi);
|
||||
device_type = props.deviceType;
|
||||
uniform_buffer_alignment = static_cast<u64>(props.limits.minUniformBufferOffsetAlignment);
|
||||
max_storage_buffer_range = static_cast<u64>(props.limits.maxStorageBufferRange);
|
||||
}
|
||||
|
||||
void VKDevice::SetupFeatures(const vk::DispatchLoaderDynamic& dldi) {
|
||||
const auto supported_features{physical.getFeatures(dldi)};
|
||||
is_optimal_astc_supported = IsOptimalAstcSupported(supported_features, dldi);
|
||||
}
|
||||
|
||||
std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() const {
|
||||
static const float QUEUE_PRIORITY = 1.f;
|
||||
static const float QUEUE_PRIORITY = 1.0f;
|
||||
|
||||
std::set<u32> unique_queue_families = {graphics_family, present_family};
|
||||
std::vector<vk::DeviceQueueCreateInfo> queue_cis;
|
||||
@@ -212,26 +275,43 @@ std::vector<vk::DeviceQueueCreateInfo> VKDevice::GetDeviceQueueCreateInfos() con
|
||||
|
||||
std::map<vk::Format, vk::FormatProperties> VKDevice::GetFormatProperties(
|
||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical) {
|
||||
static constexpr std::array formats{vk::Format::eA8B8G8R8UnormPack32,
|
||||
vk::Format::eB5G6R5UnormPack16,
|
||||
vk::Format::eA2B10G10R10UnormPack32,
|
||||
vk::Format::eR32G32B32A32Sfloat,
|
||||
vk::Format::eR16G16Unorm,
|
||||
vk::Format::eR16G16Snorm,
|
||||
vk::Format::eR8G8B8A8Srgb,
|
||||
vk::Format::eR8Unorm,
|
||||
vk::Format::eB10G11R11UfloatPack32,
|
||||
vk::Format::eR32Sfloat,
|
||||
vk::Format::eR16Sfloat,
|
||||
vk::Format::eR16G16B16A16Sfloat,
|
||||
vk::Format::eD32Sfloat,
|
||||
vk::Format::eD16Unorm,
|
||||
vk::Format::eD16UnormS8Uint,
|
||||
vk::Format::eD24UnormS8Uint,
|
||||
vk::Format::eD32SfloatS8Uint,
|
||||
vk::Format::eBc1RgbaUnormBlock,
|
||||
vk::Format::eBc2UnormBlock,
|
||||
vk::Format::eBc3UnormBlock,
|
||||
vk::Format::eBc4UnormBlock,
|
||||
vk::Format::eBc5UnormBlock,
|
||||
vk::Format::eBc5SnormBlock,
|
||||
vk::Format::eBc7UnormBlock,
|
||||
vk::Format::eAstc4x4UnormBlock,
|
||||
vk::Format::eAstc4x4SrgbBlock,
|
||||
vk::Format::eAstc8x8SrgbBlock,
|
||||
vk::Format::eAstc8x6SrgbBlock,
|
||||
vk::Format::eAstc5x4SrgbBlock,
|
||||
vk::Format::eAstc5x5UnormBlock,
|
||||
vk::Format::eAstc5x5SrgbBlock,
|
||||
vk::Format::eAstc10x8UnormBlock,
|
||||
vk::Format::eAstc10x8SrgbBlock};
|
||||
std::map<vk::Format, vk::FormatProperties> format_properties;
|
||||
|
||||
const auto AddFormatQuery = [&format_properties, &dldi, physical](vk::Format format) {
|
||||
for (const auto format : formats) {
|
||||
format_properties.emplace(format, physical.getFormatProperties(format, dldi));
|
||||
};
|
||||
AddFormatQuery(vk::Format::eA8B8G8R8UnormPack32);
|
||||
AddFormatQuery(vk::Format::eB5G6R5UnormPack16);
|
||||
AddFormatQuery(vk::Format::eA2B10G10R10UnormPack32);
|
||||
AddFormatQuery(vk::Format::eR8G8B8A8Srgb);
|
||||
AddFormatQuery(vk::Format::eR8Unorm);
|
||||
AddFormatQuery(vk::Format::eD32Sfloat);
|
||||
AddFormatQuery(vk::Format::eD16Unorm);
|
||||
AddFormatQuery(vk::Format::eD16UnormS8Uint);
|
||||
AddFormatQuery(vk::Format::eD24UnormS8Uint);
|
||||
AddFormatQuery(vk::Format::eD32SfloatS8Uint);
|
||||
AddFormatQuery(vk::Format::eBc1RgbaUnormBlock);
|
||||
AddFormatQuery(vk::Format::eBc2UnormBlock);
|
||||
AddFormatQuery(vk::Format::eBc3UnormBlock);
|
||||
AddFormatQuery(vk::Format::eBc4UnormBlock);
|
||||
|
||||
}
|
||||
return format_properties;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
namespace Vulkan {
|
||||
|
||||
/// Format usage descriptor
|
||||
/// Format usage descriptor.
|
||||
enum class FormatType { Linear, Optimal, Buffer };
|
||||
|
||||
/// Handles data specific to a physical device.
|
||||
@@ -34,12 +34,12 @@ public:
|
||||
vk::Format GetSupportedFormat(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
||||
FormatType format_type) const;
|
||||
|
||||
/// Returns the dispatch loader with direct function pointers of the device
|
||||
/// Returns the dispatch loader with direct function pointers of the device.
|
||||
const vk::DispatchLoaderDynamic& GetDispatchLoader() const {
|
||||
return dld;
|
||||
}
|
||||
|
||||
/// Returns the logical device
|
||||
/// Returns the logical device.
|
||||
vk::Device GetLogical() const {
|
||||
return logical.get();
|
||||
}
|
||||
@@ -69,30 +69,55 @@ public:
|
||||
return present_family;
|
||||
}
|
||||
|
||||
/// Returns if the device is integrated with the host CPU
|
||||
/// Returns if the device is integrated with the host CPU.
|
||||
bool IsIntegrated() const {
|
||||
return device_type == vk::PhysicalDeviceType::eIntegratedGpu;
|
||||
}
|
||||
|
||||
/// Returns uniform buffer alignment requeriment
|
||||
/// Returns uniform buffer alignment requeriment.
|
||||
u64 GetUniformBufferAlignment() const {
|
||||
return uniform_buffer_alignment;
|
||||
}
|
||||
|
||||
/// Returns the maximum range for storage buffers.
|
||||
u64 GetMaxStorageBufferRange() const {
|
||||
return max_storage_buffer_range;
|
||||
}
|
||||
|
||||
/// Returns true if ASTC is natively supported.
|
||||
bool IsOptimalAstcSupported() const {
|
||||
return is_optimal_astc_supported;
|
||||
}
|
||||
|
||||
/// Returns true if the device supports VK_EXT_scalar_block_layout.
|
||||
bool IsExtScalarBlockLayoutSupported() const {
|
||||
return ext_scalar_block_layout;
|
||||
}
|
||||
|
||||
/// Checks if the physical device is suitable.
|
||||
static bool IsSuitable(const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical,
|
||||
vk::SurfaceKHR surface);
|
||||
|
||||
private:
|
||||
/// Loads extensions into a vector and stores available ones in this object.
|
||||
std::vector<const char*> LoadExtensions(const vk::DispatchLoaderDynamic& dldi);
|
||||
|
||||
/// Sets up queue families.
|
||||
void SetupFamilies(const vk::DispatchLoaderDynamic& dldi, vk::SurfaceKHR surface);
|
||||
|
||||
/// Sets up device properties.
|
||||
void SetupProperties(const vk::DispatchLoaderDynamic& dldi);
|
||||
|
||||
/// Sets up device features.
|
||||
void SetupFeatures(const vk::DispatchLoaderDynamic& dldi);
|
||||
|
||||
/// Returns a list of queue initialization descriptors.
|
||||
std::vector<vk::DeviceQueueCreateInfo> GetDeviceQueueCreateInfos() const;
|
||||
|
||||
/// Returns true if ASTC textures are natively supported.
|
||||
bool IsOptimalAstcSupported(const vk::PhysicalDeviceFeatures& features,
|
||||
const vk::DispatchLoaderDynamic& dldi) const;
|
||||
|
||||
/// Returns true if a format is supported.
|
||||
bool IsFormatSupported(vk::Format wanted_format, vk::FormatFeatureFlags wanted_usage,
|
||||
FormatType format_type) const;
|
||||
@@ -101,16 +126,19 @@ private:
|
||||
static std::map<vk::Format, vk::FormatProperties> GetFormatProperties(
|
||||
const vk::DispatchLoaderDynamic& dldi, vk::PhysicalDevice physical);
|
||||
|
||||
const vk::PhysicalDevice physical; ///< Physical device
|
||||
vk::DispatchLoaderDynamic dld; ///< Device function pointers
|
||||
UniqueDevice logical; ///< Logical device
|
||||
vk::Queue graphics_queue; ///< Main graphics queue
|
||||
vk::Queue present_queue; ///< Main present queue
|
||||
u32 graphics_family{}; ///< Main graphics queue family index
|
||||
u32 present_family{}; ///< Main present queue family index
|
||||
vk::PhysicalDeviceType device_type; ///< Physical device type
|
||||
u64 uniform_buffer_alignment{}; ///< Uniform buffer alignment requeriment
|
||||
std::map<vk::Format, vk::FormatProperties> format_properties; ///< Format properties dictionary
|
||||
const vk::PhysicalDevice physical; ///< Physical device.
|
||||
vk::DispatchLoaderDynamic dld; ///< Device function pointers.
|
||||
UniqueDevice logical; ///< Logical device.
|
||||
vk::Queue graphics_queue; ///< Main graphics queue.
|
||||
vk::Queue present_queue; ///< Main present queue.
|
||||
u32 graphics_family{}; ///< Main graphics queue family index.
|
||||
u32 present_family{}; ///< Main present queue family index.
|
||||
vk::PhysicalDeviceType device_type; ///< Physical device type.
|
||||
u64 uniform_buffer_alignment{}; ///< Uniform buffer alignment requeriment.
|
||||
u64 max_storage_buffer_range{}; ///< Max storage buffer size.
|
||||
bool is_optimal_astc_supported{}; ///< Support for native ASTC.
|
||||
bool ext_scalar_block_layout{}; ///< Support for VK_EXT_scalar_block_layout.
|
||||
std::map<vk::Format, vk::FormatProperties> format_properties; ///< Format properties dictionary.
|
||||
};
|
||||
|
||||
} // namespace Vulkan
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/engines/shader_header.h"
|
||||
#include "video_core/renderer_vulkan/vk_device.h"
|
||||
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
@@ -33,7 +34,8 @@ using ShaderStage = Tegra::Engines::Maxwell3D::Regs::ShaderStage;
|
||||
using Operation = const OperationNode&;
|
||||
|
||||
// TODO(Rodrigo): Use rasterizer's value
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS = 0x1000;
|
||||
constexpr u32 MAX_CONSTBUFFER_FLOATS = 0x4000;
|
||||
constexpr u32 MAX_CONSTBUFFER_ELEMENTS = MAX_CONSTBUFFER_FLOATS / 4;
|
||||
constexpr u32 STAGE_BINDING_STRIDE = 0x100;
|
||||
|
||||
enum class Type { Bool, Bool2, Float, Int, Uint, HalfFloat };
|
||||
@@ -87,8 +89,8 @@ bool IsPrecise(Operation operand) {
|
||||
|
||||
class SPIRVDecompiler : public Sirit::Module {
|
||||
public:
|
||||
explicit SPIRVDecompiler(const ShaderIR& ir, ShaderStage stage)
|
||||
: Module(0x00010300), ir{ir}, stage{stage}, header{ir.GetHeader()} {
|
||||
explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderStage stage)
|
||||
: Module(0x00010300), device{device}, ir{ir}, stage{stage}, header{ir.GetHeader()} {
|
||||
AddCapability(spv::Capability::Shader);
|
||||
AddExtension("SPV_KHR_storage_buffer_storage_class");
|
||||
AddExtension("SPV_KHR_variable_pointers");
|
||||
@@ -130,20 +132,16 @@ public:
|
||||
branch_labels.push_back(label);
|
||||
}
|
||||
|
||||
// TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely
|
||||
// that shaders will use 20 nested SSYs and PBKs.
|
||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
const Id flow_stack_type = TypeArray(t_uint, Constant(t_uint, FLOW_STACK_SIZE));
|
||||
jmp_to = Emit(OpVariable(TypePointer(spv::StorageClass::Function, t_uint),
|
||||
spv::StorageClass::Function, Constant(t_uint, first_address)));
|
||||
flow_stack = Emit(OpVariable(TypePointer(spv::StorageClass::Function, flow_stack_type),
|
||||
spv::StorageClass::Function, ConstantNull(flow_stack_type)));
|
||||
flow_stack_top =
|
||||
Emit(OpVariable(t_func_uint, spv::StorageClass::Function, Constant(t_uint, 0)));
|
||||
std::tie(ssy_flow_stack, ssy_flow_stack_top) = CreateFlowStack();
|
||||
std::tie(pbk_flow_stack, pbk_flow_stack_top) = CreateFlowStack();
|
||||
|
||||
Name(jmp_to, "jmp_to");
|
||||
Name(flow_stack, "flow_stack");
|
||||
Name(flow_stack_top, "flow_stack_top");
|
||||
Name(ssy_flow_stack, "ssy_flow_stack");
|
||||
Name(ssy_flow_stack_top, "ssy_flow_stack_top");
|
||||
Name(pbk_flow_stack, "pbk_flow_stack");
|
||||
Name(pbk_flow_stack_top, "pbk_flow_stack_top");
|
||||
|
||||
Emit(OpBranch(loop_label));
|
||||
Emit(loop_label);
|
||||
@@ -195,7 +193,9 @@ public:
|
||||
entries.samplers.emplace_back(sampler);
|
||||
}
|
||||
for (const auto& attribute : ir.GetInputAttributes()) {
|
||||
entries.attributes.insert(GetGenericAttributeLocation(attribute));
|
||||
if (IsGenericAttribute(attribute)) {
|
||||
entries.attributes.insert(GetGenericAttributeLocation(attribute));
|
||||
}
|
||||
}
|
||||
entries.clip_distances = ir.GetClipDistances();
|
||||
entries.shader_length = ir.GetLength();
|
||||
@@ -210,7 +210,6 @@ private:
|
||||
std::array<OperationDecompilerFn, static_cast<std::size_t>(OperationCode::Amount)>;
|
||||
|
||||
static constexpr auto INTERNAL_FLAGS_COUNT = static_cast<std::size_t>(InternalFlag::Amount);
|
||||
static constexpr u32 CBUF_STRIDE = 16;
|
||||
|
||||
void AllocateBindings() {
|
||||
const u32 binding_base = static_cast<u32>(stage) * STAGE_BINDING_STRIDE;
|
||||
@@ -315,6 +314,7 @@ private:
|
||||
constexpr std::array<const char*, INTERNAL_FLAGS_COUNT> names = {"zero", "sign", "carry",
|
||||
"overflow"};
|
||||
for (std::size_t flag = 0; flag < INTERNAL_FLAGS_COUNT; ++flag) {
|
||||
const auto flag_code = static_cast<InternalFlag>(flag);
|
||||
const Id id = OpVariable(t_prv_bool, spv::StorageClass::Private, v_false);
|
||||
internal_flags[flag] = AddGlobalVariable(Name(id, names[flag]));
|
||||
}
|
||||
@@ -374,7 +374,9 @@ private:
|
||||
u32 binding = const_buffers_base_binding;
|
||||
for (const auto& entry : ir.GetConstantBuffers()) {
|
||||
const auto [index, size] = entry;
|
||||
const Id id = OpVariable(t_cbuf_ubo, spv::StorageClass::Uniform);
|
||||
const Id type =
|
||||
device.IsExtScalarBlockLayoutSupported() ? t_cbuf_scalar_ubo : t_cbuf_std140_ubo;
|
||||
const Id id = OpVariable(type, spv::StorageClass::Uniform);
|
||||
AddGlobalVariable(Name(id, fmt::format("cbuf_{}", index)));
|
||||
|
||||
Decorate(id, spv::Decoration::Binding, binding++);
|
||||
@@ -475,13 +477,13 @@ private:
|
||||
}
|
||||
|
||||
void VisitBasicBlock(const NodeBlock& bb) {
|
||||
for (const Node node : bb) {
|
||||
for (const auto& node : bb) {
|
||||
static_cast<void>(Visit(node));
|
||||
}
|
||||
}
|
||||
|
||||
Id Visit(Node node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(node)) {
|
||||
Id Visit(const Node& node) {
|
||||
if (const auto operation = std::get_if<OperationNode>(&*node)) {
|
||||
const auto operation_index = static_cast<std::size_t>(operation->GetCode());
|
||||
const auto decompiler = operation_decompilers[operation_index];
|
||||
if (decompiler == nullptr) {
|
||||
@@ -489,17 +491,17 @@ private:
|
||||
}
|
||||
return (this->*decompiler)(*operation);
|
||||
|
||||
} else if (const auto gpr = std::get_if<GprNode>(node)) {
|
||||
} else if (const auto gpr = std::get_if<GprNode>(&*node)) {
|
||||
const u32 index = gpr->GetIndex();
|
||||
if (index == Register::ZeroIndex) {
|
||||
return Constant(t_float, 0.0f);
|
||||
}
|
||||
return Emit(OpLoad(t_float, registers.at(index)));
|
||||
|
||||
} else if (const auto immediate = std::get_if<ImmediateNode>(node)) {
|
||||
} else if (const auto immediate = std::get_if<ImmediateNode>(&*node)) {
|
||||
return BitcastTo<Type::Float>(Constant(t_uint, immediate->GetValue()));
|
||||
|
||||
} else if (const auto predicate = std::get_if<PredicateNode>(node)) {
|
||||
} else if (const auto predicate = std::get_if<PredicateNode>(&*node)) {
|
||||
const auto value = [&]() -> Id {
|
||||
switch (const auto index = predicate->GetIndex(); index) {
|
||||
case Tegra::Shader::Pred::UnusedIndex:
|
||||
@@ -515,7 +517,7 @@ private:
|
||||
}
|
||||
return value;
|
||||
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(node)) {
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(&*node)) {
|
||||
const auto attribute = abuf->GetIndex();
|
||||
const auto element = abuf->GetElement();
|
||||
|
||||
@@ -565,40 +567,42 @@ private:
|
||||
}
|
||||
UNIMPLEMENTED_MSG("Unhandled input attribute: {}", static_cast<u32>(attribute));
|
||||
|
||||
} else if (const auto cbuf = std::get_if<CbufNode>(node)) {
|
||||
const Node offset = cbuf->GetOffset();
|
||||
} else if (const auto cbuf = std::get_if<CbufNode>(&*node)) {
|
||||
const Node& offset = cbuf->GetOffset();
|
||||
const Id buffer_id = constant_buffers.at(cbuf->GetIndex());
|
||||
|
||||
Id buffer_index{};
|
||||
Id buffer_element{};
|
||||
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(offset)) {
|
||||
// Direct access
|
||||
const u32 offset_imm = immediate->GetValue();
|
||||
ASSERT(offset_imm % 4 == 0);
|
||||
buffer_index = Constant(t_uint, offset_imm / 16);
|
||||
buffer_element = Constant(t_uint, (offset_imm / 4) % 4);
|
||||
|
||||
} else if (std::holds_alternative<OperationNode>(*offset)) {
|
||||
// Indirect access
|
||||
// TODO(Rodrigo): Use a uniform buffer stride of 4 and drop this slow math (which
|
||||
// emits sub-optimal code on GLSL from my testing).
|
||||
const Id offset_id = BitcastTo<Type::Uint>(Visit(offset));
|
||||
const Id unsafe_offset = Emit(OpUDiv(t_uint, offset_id, Constant(t_uint, 4)));
|
||||
const Id final_offset = Emit(
|
||||
OpUMod(t_uint, unsafe_offset, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS - 1)));
|
||||
buffer_index = Emit(OpUDiv(t_uint, final_offset, Constant(t_uint, 4)));
|
||||
buffer_element = Emit(OpUMod(t_uint, final_offset, Constant(t_uint, 4)));
|
||||
|
||||
Id pointer{};
|
||||
if (device.IsExtScalarBlockLayoutSupported()) {
|
||||
const Id buffer_offset = Emit(OpShiftRightLogical(
|
||||
t_uint, BitcastTo<Type::Uint>(Visit(offset)), Constant(t_uint, 2u)));
|
||||
pointer = Emit(
|
||||
OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0u), buffer_offset));
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
Id buffer_index{};
|
||||
Id buffer_element{};
|
||||
if (const auto immediate = std::get_if<ImmediateNode>(&*offset)) {
|
||||
// Direct access
|
||||
const u32 offset_imm = immediate->GetValue();
|
||||
ASSERT(offset_imm % 4 == 0);
|
||||
buffer_index = Constant(t_uint, offset_imm / 16);
|
||||
buffer_element = Constant(t_uint, (offset_imm / 4) % 4);
|
||||
} else if (std::holds_alternative<OperationNode>(*offset)) {
|
||||
// Indirect access
|
||||
const Id offset_id = BitcastTo<Type::Uint>(Visit(offset));
|
||||
const Id unsafe_offset = Emit(OpUDiv(t_uint, offset_id, Constant(t_uint, 4)));
|
||||
const Id final_offset = Emit(OpUMod(
|
||||
t_uint, unsafe_offset, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS - 1)));
|
||||
buffer_index = Emit(OpUDiv(t_uint, final_offset, Constant(t_uint, 4)));
|
||||
buffer_element = Emit(OpUMod(t_uint, final_offset, Constant(t_uint, 4)));
|
||||
} else {
|
||||
UNREACHABLE_MSG("Unmanaged offset node type");
|
||||
}
|
||||
pointer = Emit(OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0),
|
||||
buffer_index, buffer_element));
|
||||
}
|
||||
|
||||
const Id pointer = Emit(OpAccessChain(t_cbuf_float, buffer_id, Constant(t_uint, 0),
|
||||
buffer_index, buffer_element));
|
||||
return Emit(OpLoad(t_float, pointer));
|
||||
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(node)) {
|
||||
} else if (const auto gmem = std::get_if<GmemNode>(&*node)) {
|
||||
const Id gmem_buffer = global_buffers.at(gmem->GetDescriptor());
|
||||
const Id real = BitcastTo<Type::Uint>(Visit(gmem->GetRealAddress()));
|
||||
const Id base = BitcastTo<Type::Uint>(Visit(gmem->GetBaseAddress()));
|
||||
@@ -608,11 +612,13 @@ private:
|
||||
return Emit(OpLoad(t_float, Emit(OpAccessChain(t_gmem_float, gmem_buffer,
|
||||
Constant(t_uint, 0u), offset))));
|
||||
|
||||
} else if (const auto conditional = std::get_if<ConditionalNode>(node)) {
|
||||
} else if (const auto conditional = std::get_if<ConditionalNode>(&*node)) {
|
||||
// It's invalid to call conditional on nested nodes, use an operation instead
|
||||
const Id true_label = OpLabel();
|
||||
const Id skip_label = OpLabel();
|
||||
Emit(OpBranchConditional(Visit(conditional->GetCondition()), true_label, skip_label));
|
||||
const Id condition = Visit(conditional->GetCondition());
|
||||
Emit(OpSelectionMerge(skip_label, spv::SelectionControlMask::MaskNone));
|
||||
Emit(OpBranchConditional(condition, true_label, skip_label));
|
||||
Emit(true_label);
|
||||
|
||||
VisitBasicBlock(conditional->GetCode());
|
||||
@@ -621,7 +627,7 @@ private:
|
||||
Emit(skip_label);
|
||||
return {};
|
||||
|
||||
} else if (const auto comment = std::get_if<CommentNode>(node)) {
|
||||
} else if (const auto comment = std::get_if<CommentNode>(&*node)) {
|
||||
Name(Emit(OpUndef(t_void)), comment->GetText());
|
||||
return {};
|
||||
}
|
||||
@@ -689,18 +695,18 @@ private:
|
||||
}
|
||||
|
||||
Id Assign(Operation operation) {
|
||||
const Node dest = operation[0];
|
||||
const Node src = operation[1];
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
|
||||
Id target{};
|
||||
if (const auto gpr = std::get_if<GprNode>(dest)) {
|
||||
if (const auto gpr = std::get_if<GprNode>(&*dest)) {
|
||||
if (gpr->GetIndex() == Register::ZeroIndex) {
|
||||
// Writing to Register::ZeroIndex is a no op
|
||||
return {};
|
||||
}
|
||||
target = registers.at(gpr->GetIndex());
|
||||
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(dest)) {
|
||||
} else if (const auto abuf = std::get_if<AbufNode>(&*dest)) {
|
||||
target = [&]() -> Id {
|
||||
switch (const auto attribute = abuf->GetIndex(); attribute) {
|
||||
case Attribute::Index::Position:
|
||||
@@ -725,7 +731,7 @@ private:
|
||||
}
|
||||
}();
|
||||
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(dest)) {
|
||||
} else if (const auto lmem = std::get_if<LmemNode>(&*dest)) {
|
||||
Id address = BitcastTo<Type::Uint>(Visit(lmem->GetAddress()));
|
||||
address = Emit(OpUDiv(t_uint, address, Constant(t_uint, 4)));
|
||||
target = Emit(OpAccessChain(t_prv_float, local_memory, {address}));
|
||||
@@ -771,11 +777,11 @@ private:
|
||||
}
|
||||
|
||||
Id LogicalAssign(Operation operation) {
|
||||
const Node dest = operation[0];
|
||||
const Node src = operation[1];
|
||||
const Node& dest = operation[0];
|
||||
const Node& src = operation[1];
|
||||
|
||||
Id target{};
|
||||
if (const auto pred = std::get_if<PredicateNode>(dest)) {
|
||||
if (const auto pred = std::get_if<PredicateNode>(&*dest)) {
|
||||
ASSERT_MSG(!pred->IsNegated(), "Negating logical assignment");
|
||||
|
||||
const auto index = pred->GetIndex();
|
||||
@@ -787,7 +793,7 @@ private:
|
||||
}
|
||||
target = predicates.at(index);
|
||||
|
||||
} else if (const auto flag = std::get_if<InternalFlagNode>(dest)) {
|
||||
} else if (const auto flag = std::get_if<InternalFlagNode>(&*dest)) {
|
||||
target = internal_flags.at(static_cast<u32>(flag->GetFlag()));
|
||||
}
|
||||
|
||||
@@ -873,7 +879,7 @@ private:
|
||||
} else {
|
||||
u32 component_value = 0;
|
||||
if (meta->component) {
|
||||
const auto component = std::get_if<ImmediateNode>(meta->component);
|
||||
const auto component = std::get_if<ImmediateNode>(&*meta->component);
|
||||
ASSERT_MSG(component, "Component is not an immediate value");
|
||||
component_value = component->GetValue();
|
||||
}
|
||||
@@ -930,7 +936,7 @@ private:
|
||||
}
|
||||
|
||||
Id Branch(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
UNIMPLEMENTED_IF(!target);
|
||||
|
||||
Emit(OpStore(jmp_to, Constant(t_uint, target->GetValue())));
|
||||
@@ -939,9 +945,10 @@ private:
|
||||
}
|
||||
|
||||
Id PushFlowStack(Operation operation) {
|
||||
const auto target = std::get_if<ImmediateNode>(operation[0]);
|
||||
const auto target = std::get_if<ImmediateNode>(&*operation[0]);
|
||||
ASSERT(target);
|
||||
|
||||
const auto [flow_stack, flow_stack_top] = GetFlowStack(operation);
|
||||
const Id current = Emit(OpLoad(t_uint, flow_stack_top));
|
||||
const Id next = Emit(OpIAdd(t_uint, current, Constant(t_uint, 1)));
|
||||
const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, current));
|
||||
@@ -952,6 +959,7 @@ private:
|
||||
}
|
||||
|
||||
Id PopFlowStack(Operation operation) {
|
||||
const auto [flow_stack, flow_stack_top] = GetFlowStack(operation);
|
||||
const Id current = Emit(OpLoad(t_uint, flow_stack_top));
|
||||
const Id previous = Emit(OpISub(t_uint, current, Constant(t_uint, 1)));
|
||||
const Id access = Emit(OpAccessChain(t_func_uint, flow_stack, previous));
|
||||
@@ -968,11 +976,11 @@ private:
|
||||
case ShaderStage::Vertex: {
|
||||
// TODO(Rodrigo): We should use VK_EXT_depth_range_unrestricted instead, but it doesn't
|
||||
// seem to be working on Nvidia's drivers and Intel (mesa and blob) doesn't support it.
|
||||
const Id position = AccessElement(t_float4, per_vertex, position_index);
|
||||
Id depth = Emit(OpLoad(t_float, AccessElement(t_out_float, position, 2)));
|
||||
const Id z_pointer = AccessElement(t_out_float, per_vertex, position_index, 2u);
|
||||
Id depth = Emit(OpLoad(t_float, z_pointer));
|
||||
depth = Emit(OpFAdd(t_float, depth, Constant(t_float, 1.0f)));
|
||||
depth = Emit(OpFMul(t_float, depth, Constant(t_float, 0.5f)));
|
||||
Emit(OpStore(AccessElement(t_out_float, position, 2), depth));
|
||||
Emit(OpStore(z_pointer, depth));
|
||||
break;
|
||||
}
|
||||
case ShaderStage::Fragment: {
|
||||
@@ -1162,6 +1170,31 @@ private:
|
||||
Emit(skip_label);
|
||||
}
|
||||
|
||||
std::tuple<Id, Id> CreateFlowStack() {
|
||||
// TODO(Rodrigo): Figure out the actual depth of the flow stack, for now it seems unlikely
|
||||
// that shaders will use 20 nested SSYs and PBKs.
|
||||
constexpr u32 FLOW_STACK_SIZE = 20;
|
||||
constexpr auto storage_class = spv::StorageClass::Function;
|
||||
|
||||
const Id flow_stack_type = TypeArray(t_uint, Constant(t_uint, FLOW_STACK_SIZE));
|
||||
const Id stack = Emit(OpVariable(TypePointer(storage_class, flow_stack_type), storage_class,
|
||||
ConstantNull(flow_stack_type)));
|
||||
const Id top = Emit(OpVariable(t_func_uint, storage_class, Constant(t_uint, 0)));
|
||||
return std::tie(stack, top);
|
||||
}
|
||||
|
||||
std::pair<Id, Id> GetFlowStack(Operation operation) {
|
||||
const auto stack_class = std::get<MetaStackClass>(operation.GetMeta());
|
||||
switch (stack_class) {
|
||||
case MetaStackClass::Ssy:
|
||||
return {ssy_flow_stack, ssy_flow_stack_top};
|
||||
case MetaStackClass::Pbk:
|
||||
return {pbk_flow_stack, pbk_flow_stack_top};
|
||||
}
|
||||
UNREACHABLE();
|
||||
return {};
|
||||
}
|
||||
|
||||
static constexpr OperationDecompilersArray operation_decompilers = {
|
||||
&SPIRVDecompiler::Assign,
|
||||
|
||||
@@ -1311,6 +1344,7 @@ private:
|
||||
&SPIRVDecompiler::WorkGroupId<2>,
|
||||
};
|
||||
|
||||
const VKDevice& device;
|
||||
const ShaderIR& ir;
|
||||
const ShaderStage stage;
|
||||
const Tegra::Shader::Header header;
|
||||
@@ -1349,12 +1383,18 @@ private:
|
||||
const Id t_out_float4 = Name(TypePointer(spv::StorageClass::Output, t_float4), "out_float4");
|
||||
|
||||
const Id t_cbuf_float = TypePointer(spv::StorageClass::Uniform, t_float);
|
||||
const Id t_cbuf_array =
|
||||
Decorate(Name(TypeArray(t_float4, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS)), "CbufArray"),
|
||||
spv::Decoration::ArrayStride, CBUF_STRIDE);
|
||||
const Id t_cbuf_struct = MemberDecorate(
|
||||
Decorate(TypeStruct(t_cbuf_array), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
||||
const Id t_cbuf_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_struct);
|
||||
const Id t_cbuf_std140 = Decorate(
|
||||
Name(TypeArray(t_float4, Constant(t_uint, MAX_CONSTBUFFER_ELEMENTS)), "CbufStd140Array"),
|
||||
spv::Decoration::ArrayStride, 16u);
|
||||
const Id t_cbuf_scalar = Decorate(
|
||||
Name(TypeArray(t_float, Constant(t_uint, MAX_CONSTBUFFER_FLOATS)), "CbufScalarArray"),
|
||||
spv::Decoration::ArrayStride, 4u);
|
||||
const Id t_cbuf_std140_struct = MemberDecorate(
|
||||
Decorate(TypeStruct(t_cbuf_std140), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
||||
const Id t_cbuf_scalar_struct = MemberDecorate(
|
||||
Decorate(TypeStruct(t_cbuf_scalar), spv::Decoration::Block), 0, spv::Decoration::Offset, 0);
|
||||
const Id t_cbuf_std140_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_std140_struct);
|
||||
const Id t_cbuf_scalar_ubo = TypePointer(spv::StorageClass::Uniform, t_cbuf_scalar_struct);
|
||||
|
||||
const Id t_gmem_float = TypePointer(spv::StorageClass::StorageBuffer, t_float);
|
||||
const Id t_gmem_array =
|
||||
@@ -1397,14 +1437,17 @@ private:
|
||||
|
||||
Id execute_function{};
|
||||
Id jmp_to{};
|
||||
Id flow_stack_top{};
|
||||
Id flow_stack{};
|
||||
Id ssy_flow_stack_top{};
|
||||
Id pbk_flow_stack_top{};
|
||||
Id ssy_flow_stack{};
|
||||
Id pbk_flow_stack{};
|
||||
Id continue_label{};
|
||||
std::map<u32, Id> labels;
|
||||
};
|
||||
|
||||
DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage) {
|
||||
auto decompiler = std::make_unique<SPIRVDecompiler>(ir, stage);
|
||||
DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
|
||||
Maxwell::ShaderStage stage) {
|
||||
auto decompiler = std::make_unique<SPIRVDecompiler>(device, ir, stage);
|
||||
decompiler->Decompile();
|
||||
return {std::move(decompiler), decompiler->GetShaderEntries()};
|
||||
}
|
||||
|
||||
@@ -20,10 +20,13 @@ namespace VideoCommon::Shader {
|
||||
class ShaderIR;
|
||||
}
|
||||
|
||||
namespace Vulkan {
|
||||
class VKDevice;
|
||||
}
|
||||
|
||||
namespace Vulkan::VKShader {
|
||||
|
||||
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
||||
|
||||
using SamplerEntry = VideoCommon::Shader::Sampler;
|
||||
|
||||
constexpr u32 DESCRIPTOR_SET = 0;
|
||||
@@ -75,6 +78,7 @@ struct ShaderEntries {
|
||||
|
||||
using DecompilerResult = std::pair<std::unique_ptr<Sirit::Module>, ShaderEntries>;
|
||||
|
||||
DecompilerResult Decompile(const VideoCommon::Shader::ShaderIR& ir, Maxwell::ShaderStage stage);
|
||||
DecompilerResult Decompile(const VKDevice& device, const VideoCommon::Shader::ShaderIR& ir,
|
||||
Maxwell::ShaderStage stage);
|
||||
|
||||
} // namespace Vulkan::VKShader
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/engines/shader_header.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
@@ -169,7 +170,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
|
||||
|
||||
SetTemporal(bb, i, gmem);
|
||||
}
|
||||
@@ -262,7 +263,7 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
|
||||
const Node it_offset = Immediate(i * 4);
|
||||
const Node real_address =
|
||||
Operation(OperationCode::UAdd, NO_PRECISE, real_address_base, it_offset);
|
||||
const Node gmem = StoreNode(GmemNode(real_address, base_address, descriptor));
|
||||
const Node gmem = MakeNode<GmemNode>(real_address, base_address, descriptor);
|
||||
|
||||
bb.push_back(Operation(OperationCode::Assign, gmem, GetTemporal(i + 1)));
|
||||
}
|
||||
@@ -298,9 +299,9 @@ std::tuple<Node, Node, GlobalMemoryBase> ShaderIR::TrackAndGetGlobalMemory(NodeB
|
||||
|
||||
const Node base_address{
|
||||
TrackCbuf(addr_register, global_code, static_cast<s64>(global_code.size()))};
|
||||
const auto cbuf = std::get_if<CbufNode>(base_address);
|
||||
const auto cbuf = std::get_if<CbufNode>(&*base_address);
|
||||
ASSERT(cbuf != nullptr);
|
||||
const auto cbuf_offset_imm = std::get_if<ImmediateNode>(cbuf->GetOffset());
|
||||
const auto cbuf_offset_imm = std::get_if<ImmediateNode>(&*cbuf->GetOffset());
|
||||
ASSERT(cbuf_offset_imm != nullptr);
|
||||
const auto cbuf_offset = cbuf_offset_imm->GetValue();
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
@@ -108,22 +109,20 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
||||
"Constant buffer flow is not supported");
|
||||
|
||||
// The SSY opcode tells the GPU where to re-converge divergent execution paths, it sets the
|
||||
// target of the jump that the SYNC instruction will make. The SSY opcode has a similar
|
||||
// structure to the BRA opcode.
|
||||
// The SSY opcode tells the GPU where to re-converge divergent execution paths with SYNC.
|
||||
const u32 target = pc + instr.bra.GetBranchTarget();
|
||||
bb.push_back(Operation(OperationCode::PushFlowStack, Immediate(target)));
|
||||
bb.push_back(
|
||||
Operation(OperationCode::PushFlowStack, MetaStackClass::Ssy, Immediate(target)));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::PBK: {
|
||||
UNIMPLEMENTED_IF_MSG(instr.bra.constant_buffer != 0,
|
||||
"Constant buffer PBK is not supported");
|
||||
|
||||
// PBK pushes to a stack the address where BRK will jump to. This shares stack with SSY but
|
||||
// using SYNC on a PBK address will kill the shader execution. We don't emulate this because
|
||||
// it's very unlikely a driver will emit such invalid shader.
|
||||
// PBK pushes to a stack the address where BRK will jump to.
|
||||
const u32 target = pc + instr.bra.GetBranchTarget();
|
||||
bb.push_back(Operation(OperationCode::PushFlowStack, Immediate(target)));
|
||||
bb.push_back(
|
||||
Operation(OperationCode::PushFlowStack, MetaStackClass::Pbk, Immediate(target)));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::SYNC: {
|
||||
@@ -132,7 +131,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
static_cast<u32>(cc));
|
||||
|
||||
// The SYNC opcode jumps to the address previously set by the SSY opcode
|
||||
bb.push_back(Operation(OperationCode::PopFlowStack));
|
||||
bb.push_back(Operation(OperationCode::PopFlowStack, MetaStackClass::Ssy));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::BRK: {
|
||||
@@ -141,7 +140,7 @@ u32 ShaderIR::DecodeOther(NodeBlock& bb, u32 pc) {
|
||||
static_cast<u32>(cc));
|
||||
|
||||
// The BRK opcode jumps to the address previously set by the PBK opcode
|
||||
bb.push_back(Operation(OperationCode::PopFlowStack));
|
||||
bb.push_back(Operation(OperationCode::PopFlowStack, MetaStackClass::Pbk));
|
||||
break;
|
||||
}
|
||||
case OpCode::Id::IPA: {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/shader/node_helper.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
|
||||
namespace VideoCommon::Shader {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user