Compare commits

...

88 Commits

Author SHA1 Message Date
Lioncash 82cd87f6c2 service/acc: Silence truncation warnings
The sanitizing function ensures that the returned type is always the
correct type. This eliminates warnings without extra casts.
2019-06-21 10:04:31 -04:00
bunnei 80a8456af8 Merge pull request #2596 from FernandoS27/revert-2590
Revert PR 2590.
2019-06-20 13:04:06 -04:00
Hexagon12 908a5a00c5 Merge pull request #2595 from jonsn0w/patch-1
Update content_archive.cpp
2019-06-20 10:03:24 +03:00
Fernando Sahmkow 26fcdf087d Revert PR 2590.
Even though it has been proven that IAudioRenderer:SystemEvent is 
actually an automatic event. The current implementation of such event is 
all thought to be manual. Thus it's implementation needs to be corrected 
when doing such change. As it is right now this PR introduced a series 
of regressions on softlocks on multiple games. Therefore, this pr 
reverts such change until a correct implementation is made.
2019-06-19 23:19:19 -04:00
bunnei c28694d907 Merge pull request #2591 from lioncash/record
core: Remove unused CiTrace source files
2019-06-19 22:28:26 -04:00
bunnei ca470890a3 Merge pull request #2590 from lioncash/event
service/audio/audren_u: Correct event reset type for the system event
2019-06-19 22:27:52 -04:00
Frederic L 5cef446f42 CMake: Get Git submodule dependencies via CMake (#2474)
* CMake: Get Git submodule dependencies via CMake

* CMakeLists: Fixed unintentional line break

* travis: Bring parity between linux-mingw and linux build script

* CMakeLists: Fixed typo in error message
2019-06-19 22:26:12 -04:00
jonsn0w e78d069a81 Update content_archive.cpp
log clutter in debug logs when theres really no need
2019-06-19 22:01:41 -04:00
Zach Hilman 8893d63612 Merge pull request #2594 from FearlessTobi/very-important-change
yuzu/configure_input: Add missing space in window title
2019-06-19 19:15:37 -04:00
Tobias 0c64a6f0f2 Change to a more descriptive name 2019-06-19 23:55:13 +02:00
Tobias c5b20a108d yuzu/configure_input: Add missing space in window name 2019-06-19 23:32:34 +02:00
Mat M 908ca1fc72 Merge pull request #2593 from deadmeu/patch-1
Added missing space between two words
2019-06-19 14:11:10 -04:00
Alex Subaric f375e10411 Added missing space between two words
Added missing whitespace character between two words in the "Warning Missing Derivation Components" warning message box.
2019-06-20 02:42:56 +10:00
Zach Hilman 5c665fcc5b Merge pull request #2584 from ogniK5377/cadence
Impl'd IsUserAccountSwitchLocked, SetAudioOutVolume, GetAudioOutVolume & Partial impl of GetAccumulatedSuspendedTickChangedEvent
2019-06-19 10:29:42 -04:00
Lioncash 61d2498f00 core: Remove unused CiTrace source files
These source files have been unused for the entire lifecycle of the
project. They're a hold-over from Citra and only add to the build time
of the project, so they can be removed.

There's also likely no way this would ever work in yuzu in its current
form without revamping quite a bit of it, given how different the GPU on
the Switch is compared to the 3DS.
2019-06-18 16:57:59 -04:00
Lioncash 5799404b78 service/audio/audren_u: Correct event reset type for the system event
This is actually an auto-reset event in the audio service itself, not a
manual one.
2019-06-18 09:23:20 -04:00
bunnei c7b5c245e1 Merge pull request #2562 from ReinUsesLisp/split-cbuf-upload
video_core/engines: Move ConstBufferInfo out of Maxwell3D
2019-06-17 22:35:04 -04:00
David Marcec 6ca20ad7ba Addressed issues 2019-06-17 08:17:26 +10:00
David Marcec 50e3269f3b Signalled accumulated_suspended_tick_changed_event on creation based on RE 2019-06-16 22:18:54 +10:00
David Marcec 5fb6781c61 Cleanup 2019-06-16 20:18:35 +10:00
David Marcec 335127af69 Impl'd IsUserAccountSwitchLocked, SetAudioOutVolume, GetAudioOutVolume & Partial impl of GetAccumulatedSuspendedTickChangedEvent
IPC-100 was changed to InitializeApplicationInfoOld instead of InitializeApplicationInfo. IPC-150 makes an indentical call to IPC-100 however does extra processing. They should not have the same name as it's quite confusing to debug.
2019-06-16 19:06:33 +10:00
Zach Hilman c0e7b91145 Merge pull request #2538 from ReinUsesLisp/ssy-pbk
shader: Split SSY and PBK stack
2019-06-15 20:30:13 -04:00
Zach Hilman c140b6ae2c Merge pull request #2581 from lioncash/hex
common/hex_util: Combine HexVectorToString() and HexArrayToString()
2019-06-15 16:47:13 -04:00
bunnei 0360c40e90 Merge pull request #2582 from lioncash/reserved
file_sys/ips_layer: Remove unnecessary reserve() call
2019-06-14 11:24:18 -04:00
Zach Hilman a9521c983b Merge pull request #2580 from lioncash/redundant
kernel/vm_manager: Remove redundant Reset call in destructor
2019-06-12 19:26:07 -04:00
Zach Hilman 0f08f2d562 Merge pull request #2577 from lioncash/fs
file_sys/card_image: Minor cleanup
2019-06-12 19:23:33 -04:00
Lioncash 18c1d91920 file_sys/ips_layer: Remove unnecessary reserve() call
Given 'replace' is assigned to on the following line, this isn't
necessary, given the underlying data is going to be overwritten
entirely.
2019-06-12 18:12:45 -04:00
Lioncash 969cd6dc1d common/hex_util: Reserve std::string memory ahead of time
Avoids potentially performing multiple reallocations (depending on the
size of the input data) by reserving the necessary amount of memory
ahead of time.

This is trivially doable, so there's no harm in it.
2019-06-12 17:54:11 -04:00
Lioncash a62088539e common/hex_util: Combine HexVectorToString() and HexArrayToString()
These can be generified together by using a concept type to designate
them. This also has the benefit of not making copies of potentially very
large arrays.
2019-06-12 17:54:05 -04:00
Lioncash c7daddb715 file_sys/card_image: Remove obsolete TODO
We already support Rev 1+.
2019-06-12 16:52:19 -04:00
Lioncash 0af3b4d9f4 kernel/vm_manager: Remove redundant Reset call in destructor
This is performing more work than would otherwise be necessary during
VMManager's destruction. All we actually want to occur in this scenario
is for any allocated memory to be freed, which will happen automatically
as the VMManager instance goes out of scope.

Anything else being done is simply unnecessary work.
2019-06-12 16:10:00 -04:00
bunnei 7e2bcf04b4 Merge pull request #2578 from lioncash/cnmt
file_sys/nca_metadata: Update CNMT structures
2019-06-11 21:13:05 -04:00
bunnei f981efdf8d Merge pull request #2572 from FernandoS27/gpu-mem
GPUVM: Correct GPU VM virtual address space
2019-06-11 21:09:57 -04:00
Lioncash a602bcaaf8 file_sys/nca_metadata: Update CNMT structures
Names a few more entries in relevant structures. Information based off
SwitchBrew and my own RE.
2019-06-10 23:51:06 -04:00
Lioncash 7bdef6106e file_sys/card_image: Deduplicate casts within AddNCAFromPartition()
Makes for nicer reading.
2019-06-10 23:27:14 -04:00
Lioncash 81d361d9f8 file_sys/card_image: Make bracing consistent
Makes for more consistent reading.
2019-06-10 23:27:13 -04:00
Lioncash e34368249f file_sys/card_image: Assign collapsed NCA contents directly to ncas member
Same thing, significantly less noisy.
2019-06-10 23:27:13 -04:00
Lioncash 288d027e89 file_sys/card_image: Deduplicate type cast
Same thing, less duplication. We can also std::move raw into the
PartitionFilesystem constructor.
2019-06-10 23:27:05 -04:00
Lioncash 825ffd7b1f file_sys/card_image: Get rid of a magic number
We can just use the size of the array to dehardcode it.
2019-06-10 22:58:08 -04:00
Lioncash bf35138d1d file_sys/card_image: Use std::array deduction guides
Same thing, less code.
2019-06-10 22:57:53 -04:00
Zach Hilman 364932df3a Merge pull request #2571 from lioncash/ref
kernel/process: Make Create()'s name parameter be taken by value
2019-06-09 20:43:57 -04:00
Zach Hilman 4486103e1d Merge pull request #2570 from lioncash/svc
kernel/svc: Handle TotalPhysicalMemoryAvailableWithoutMmHeap and TotalPhysicalMemoryUsedWithoutMmHeap
2019-06-09 20:43:03 -04:00
Lioncash fea6568955 kernel/process: Make Create()'s name parameter be taken by value
Makes the interface more flexible in terms of how Create() may be
called, while still allowing the parameter itself to be moved into.
2019-06-09 18:47:37 -04:00
Lioncash 3f87664d8f kernel/svc: Implement TotalMemoryUsedWithoutMmHeap/TotalMemoryAvailableWithoutMmHeap
Given we don't currently implement the personal heap yet, the existing
memory querying functions are essentially doing what the memory querying
types introduced in 6.0.0 do.

So, we can build the necessary machinery over the top of those and just
use them as part of info types.
2019-06-09 18:22:30 -04:00
Lioncash c1a8f684df kernel/svc: Amend naming for TotalMemoryUsage in svcGetInfo()
Disambiguates and makes the name a little more consistent with
TotalPhysicalMemoryUsed.
2019-06-09 18:12:05 -04:00
Lioncash 81b1102090 kernel/svc: Remove duplicate enum entry in svcGetInfo() 2019-06-09 18:08:37 -04:00
Fernando Sahmkow f79823fda7 GPUVM: Correct GPU VM virtual address space 2019-06-09 17:47:15 -04:00
Zach Hilman 834e07d639 Merge pull request #2564 from ReinUsesLisp/block-dim-x-fix
kepler_compute: Minor changes
2019-06-08 14:09:02 -04:00
Zach Hilman 9a84f428e1 Merge pull request #2567 from FearlessTobi/patch-1
.github: Create FUNDING.yml
2019-06-08 13:56:53 -04:00
Tobias 9738dc3058 .github: Create FUNDING.yml 2019-06-08 17:00:32 +02:00
Zach Hilman ac54f1a967 Merge pull request #2553 from lioncash/language
yuzu/configuration: Make all widgets and dialogs aware of language changes
2019-06-07 21:46:08 -04:00
ReinUsesLisp 528c15051c kepler_compute: Use std::array for cbuf info 2019-06-07 20:36:22 -03:00
ReinUsesLisp 17d5fb6d06 kepler_compute: Fix block_dim_x encoding 2019-06-07 20:35:46 -03:00
ReinUsesLisp 5669ff3cbd gl_rasterizer: Remove unused parameters in descriptor uploads 2019-06-07 19:52:16 -03:00
ReinUsesLisp 2f2a61887a video_core/engines: Move ConstBufferInfo out of Maxwell3D 2019-06-07 19:47:15 -03:00
Zach Hilman 357ea15a39 Merge pull request #2293 from DarkLordZach/system-constants
core: Remove duplicated account JPEG data structure
2019-06-07 18:39:37 -04:00
Zach Hilman 11f2f0f45c constants: Extract backup JPEG used by account services 2019-06-07 17:46:57 -04:00
Zach Hilman de33ad25f5 Merge pull request #2514 from ReinUsesLisp/opengl-compat
video_core: Drop OpenGL core in favor of OpenGL compatibility
2019-06-07 17:23:25 -04:00
ReinUsesLisp fe8e6618f2 shader: Split SSY and PBK stack
Hardware testing revealed that SSY and PBK push to a different stack,
allowing code like this:

        SSY label1;
        PBK label2;
        SYNC;
label1: PBK;
label2: EXIT;
2019-06-07 02:18:27 -03:00
bunnei cd2d9628c9 Merge pull request #2558 from ReinUsesLisp/shader-nodes
shader: Move Node declarations out of the shader IR header
2019-06-06 22:31:46 -04:00
ReinUsesLisp dec1cbaf7f cmake: Add missing shader hash file entries 2019-06-06 20:11:48 -03:00
ReinUsesLisp 769a50661a shader/node: Minor changes
Reflect std::shared_ptr nature of Node on initializers and remove
constant members in nodes.

Add some commentaries.
2019-06-06 20:03:33 -03:00
ReinUsesLisp e1b3be7ced shader: Move Node declarations out of the shader IR header
Analysis passes do not have a good reason to depend on shader_ir.h to
work on top of nodes. This splits node-related declarations to their own
file and leaves the IR in shader_ir.h
2019-06-06 20:02:37 -03:00
Zach Hilman 04ac7a637a Merge pull request #2552 from ReinUsesLisp/shader-shared-ptr
shader: Use shared_ptr to store nodes and move initialization to file
2019-06-06 18:25:24 -04:00
Zach Hilman adb8a9152b Merge pull request #2549 from lioncash/header
kernel/process: Remove unused boost header include
2019-06-06 14:31:46 -04:00
Zach Hilman 7322c8bd7c Merge pull request #2550 from lioncash/frontend
yuzu/CMakeLists: Pass compilation flags that make it more difficult to cause bugs in Qt code
2019-06-06 14:31:22 -04:00
bunnei 03d9bbaa90 Merge pull request #2551 from lioncash/dtor
service/ns: Add missing override specifiers
2019-06-06 10:37:28 -04:00
ReinUsesLisp bf4dfb3ad4 shader: Use shared_ptr to store nodes and move initialization to file
Instead of having a vector of unique_ptr stored in a vector and
returning star pointers to this, use shared_ptr. While changing
initialization code, move it to a separate file when possible.

This is a first step to allow code analysis and node generation beyond
the ShaderIR class.
2019-06-05 20:41:52 -03:00
bunnei a20ba09bfd Merge pull request #2520 from ReinUsesLisp/vulkan-refresh
vk_device,vk_shader_decompiler: Miscellaneous changes
2019-06-05 18:10:00 -04:00
bunnei 55c5029171 Merge pull request #2540 from ReinUsesLisp/remove-guest-position
gl_shader_decompiler: Remove guest "position" varying
2019-06-05 18:07:23 -04:00
bunnei e4fea833d4 Merge pull request #2419 from DarkLordZach/srv-lr-iface
lr: Add command handler skeletons for Open*LocationResolver
2019-06-05 18:05:50 -04:00
Lioncash 8304aaf282 service/ns: Add missing override specifiers 2019-06-05 16:20:24 -04:00
Lioncash d7d5bffa18 yuzu/CMakeLists: Disable implicit QString->QUrl conversions
Enforces the use of the proper URL resolution functions. e.g.

url = some_local_path_string;

should actually be:

url = QUrl::fromLocalPath(some_local_path_string);

etc.

This makes it harder to cause bugs when operating with both strings and
URLs at the same time.
2019-06-05 16:05:40 -04:00
Lioncash 5b93290183 yuzu/CMakeLists: Disable unsafe overloads of QProcess' start() function
Other overloads of start() are considerably much safer to use if we ever
need this in the future and need to pass arguments to the program, given
it contains separate parameters for the program path and the arguments
themselves, whereas this unsafe overload contains both as a single
string.

Given the alternatives are much safer, we can disable this.
2019-06-05 15:49:23 -04:00
Lioncash b5e1e87922 yuzu/CMakeLists: Disable implicit type narrowing in connect() calls
Prevents hard-to-diagnose bugs from potentially occurring and requires
any type narrowing to be explicitly performed by our code.
2019-06-05 15:47:35 -04:00
Lioncash 19dcb50692 kernel/process: Remove unused boost header include
Boost headers typically include a lot of other headers, so removing this
can prevent a bit of unnecessary compiler churn when building.
2019-06-05 14:03:29 -04:00
ReinUsesLisp 0935c2d97b gl_shader_decompiler: Remove guest "position" varying
"position" was being written but not read anywhere besides geometry
shaders, where it had the same value as gl_Position.

This commit replaces "position" with gl_Position, reducing the
complexity of our code and the emitted GLSL code.
2019-06-03 01:01:34 -03:00
ReinUsesLisp 6ac4490751 gl_buffer_cache: Remove unused ReserveMemory method 2019-05-30 13:21:01 -03:00
ReinUsesLisp a89cc0bafc maxwell_to_gl: Use GL_CLAMP to emulate Clamp wrap mode 2019-05-30 13:21:01 -03:00
ReinUsesLisp b76df62c00 gl_rasterizer: Move alpha testing to the OpenGL pipeline
Removes the alpha testing code from each fragment shader invocation.
2019-05-30 13:21:01 -03:00
ReinUsesLisp df509486c4 gl_rasterizer: Use GL_QUADS to emulate quads rendering 2019-05-30 13:21:01 -03:00
ReinUsesLisp 7259f7a733 rasterizer_opengl: Remove OpenGL core profile 2019-05-30 13:21:00 -03:00
Zach Hilman 52b80d231c ncm: Implement LR OpenAddOnContentLocationResolver (2)
Returns an object of type IAddOnContentLocationResolver for the provided StorageId.
2019-05-26 20:37:13 -04:00
Zach Hilman e0920ef4ba ncm: Implement LR OpenRegisteredLocationResolver (1)
Returns an object of type IRegisteredLocationResolver for the StorageId.
2019-05-26 18:24:48 -04:00
Zach Hilman 33ac193bf6 ncm: Implement LR OpenLocationResolver (0)
Returns an object of type ILocationResolver with the provided StorageId.
2019-05-26 18:24:48 -04:00
ReinUsesLisp f424b46036 vk_device: Let formats array type be deduced 2019-05-26 03:09:06 -03:00
ReinUsesLisp a4c5e3e339 vk_shader_decompiler: Misc fixes
Fix missing OpSelectionMerge instruction. This caused devices loses on
most hardware, Intel didn't care.

Fix [-1;1] -> [0;1] depth conversions.

Conditionally use VK_EXT_scalar_block_layout. This allows us to use
non-std140 layouts on UBOs.

Update external Vulkan headers.
2019-05-26 01:48:04 -03:00
ReinUsesLisp dec3c981d0 vk_device: Enable features when available and misc changes
Keeps track of native ASTC support, VK_EXT_scalar_block_layout
availability and SSBO range.

Check for independentBlend and vertexPipelineStorageAndAtomics as a
required feature. Always enable it.

Use vk::to_string format to log Vulkan enums.

Style changes.
2019-05-26 01:41:34 -03:00
122 changed files with 1666 additions and 1756 deletions
+3
View File
@@ -0,0 +1,3 @@
# These are supported funding model platforms
patreon: yuzuteam
+3 -2
View File
@@ -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
View File
@@ -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)
+3
View File
@@ -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"
+7 -3
View File
@@ -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());
+7
View File
@@ -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
+3
View File
@@ -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"
-7
View File
@@ -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
View File
@@ -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;
}
+2 -3
View File
@@ -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)
+17
View File
@@ -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,
}};
}
+17
View File
@@ -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
+2 -2
View File
@@ -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;
+29 -17
View File
@@ -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());
}
}
+2 -2
View File
@@ -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) {
+4
View File
@@ -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;
}
+3 -1
View File
@@ -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{};
+1 -2
View File
@@ -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));
+7 -2
View File
@@ -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.");
+4 -4
View File
@@ -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));
+8 -6
View File
@@ -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,
+8 -5
View File
@@ -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)) {
+1 -1
View File
@@ -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;
+17 -1
View File
@@ -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);
}
+12 -2
View File
@@ -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;
+19 -7
View File
@@ -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;
}
+2 -4
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+48 -25
View File
@@ -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
+6 -3
View File
@@ -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
+3 -2
View File
@@ -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"},
+2 -2
View File
@@ -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;
};
+3 -2
View File
@@ -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"},
+2 -2
View File
@@ -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;
};
+5 -4
View File
@@ -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
+2 -2
View File
@@ -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;
};
+3 -2
View File
@@ -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"},
+2 -2
View File
@@ -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;
};
+17 -1
View File
@@ -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,
+3
View File
@@ -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));
}
}
+21 -2
View File
@@ -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;
+1 -1
View File
@@ -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;
+77 -3
View File
@@ -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);
}
+9 -9
View File
@@ -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;
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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());
}
}
-1
View File
@@ -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",
-1
View File
@@ -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;
-100
View File
@@ -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
-208
View File
@@ -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
-87
View File
@@ -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
+4 -2
View File
@@ -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
+4 -3
View File
@@ -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;
+1 -3
View File
@@ -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;
}
+1 -7
View File
@@ -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;
};
+2 -2
View File
@@ -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
+12 -9
View File
@@ -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.
+108 -28
View File
@@ -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;
}
+43 -15
View File
@@ -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
+1
View File
@@ -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 {
+1
View File
@@ -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 {
+1
View File
@@ -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 {
+1
View File
@@ -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 {
+1
View File
@@ -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 {
+5 -4
View File
@@ -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();
+9 -10
View File
@@ -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