Compare commits

...

84 Commits

Author SHA1 Message Date
german 4057af1871 Address Comments 2020-10-03 22:15:53 -05:00
german 9336dec9dc Enable GC vibration 2020-10-03 15:24:00 -05:00
David 4d0ae1a17a Merge pull request #4291 from german77/ImplementControllerRumble
input_common: First implementation of controller rumble
2020-09-30 21:24:11 +10:00
David f7808f5658 Merge pull request #4726 from lioncash/applet
frontend/controller: Eliminate dependency on the global system instance
2020-09-30 21:22:45 +10:00
LC 1ba0b077fc Merge pull request #4733 from ReinUsesLisp/game-list-leak
qt/game_list: Give GameListSearchField::KeyReleaseEater a parent
2020-09-30 05:54:18 -04:00
LC cb56eaee41 Merge pull request #4732 from ReinUsesLisp/wall-clock-destr
common/wall_clock: Add virtual destructors
2020-09-30 05:53:12 -04:00
LC 3665a05488 Merge pull request #4735 from goldenx86/patch-1
Remove ext_extended_dynamic_state blacklist
2020-09-30 05:52:47 -04:00
bunnei 392c1b96bc Merge pull request #4705 from german77/SplitMotionPoller
HID: Use different timing for motion
2020-09-30 01:04:22 -07:00
Matías Locatti d7843b8ef2 Remove ext_extended_dynamic_state blacklist
Latest AMD 20.9.2 driver fixed this, there's no reason to keep it blocked, as the previous stable signed driver release doesn't include the extension.
2020-09-30 03:13:38 -03:00
ReinUsesLisp 771a9c21cc common/wall_clock: Add virtual destructors
From -fsanitize=address, this code wasn't calling the proper destructor.
Adding virtual destructors for each inherited class and the base class
fixes this bug.

While we are at it, mark the functions as final.
2020-09-30 02:53:34 -03:00
bunnei 385b5602e4 Update yuzu-patreon-step2.yml to set build timeout to 120 min
This is currently necessary to build ffmpeg
2020-09-29 22:17:50 -07:00
bunnei a1e3f6e27b Merge pull request #4728 from Morph1984/applets-on-top
main: Allow applets to display on top while fullscreen
2020-09-29 15:46:44 -07:00
bunnei 09609dd50e Merge pull request #4721 from lioncash/genfn
codec: Make lookup table static constexpr
2020-09-29 15:16:24 -07:00
bunnei 2a82f1b08b Merge pull request #4722 from lioncash/casting
cubeb_sink: Use static_cast instead of reinterpret_cast in DataCallback()
2020-09-29 15:09:12 -07:00
bunnei 02ea62568f Merge pull request #1703 from DarkLordZach/nvdec-ioctl
nvdrv: Stub nvdec/vic ioctls to bypass nvdec movies
2020-09-29 15:00:48 -07:00
ReinUsesLisp ae6df703f5 qt/game_list: Give GameListSearchField::KeyReleaseEater a parent
This fixes a memory leak as KeyReleaseEater's destructor was never
called.
2020-09-29 16:23:16 -03:00
german ab88c2f611 First implementation of controller rumble 2020-09-29 10:38:25 -04:00
bunnei a8be822e8e Merge pull request #4719 from lioncash/audio-warn
audio_core: Resolve sign conversion warnings
2020-09-27 01:52:59 -07:00
Rodrigo Locatti e5a1e0a76d Merge pull request #4724 from lat9nq/fix-vulkan-nvidia-allocate-2
vk_stream_buffer: Fix initializing Vulkan with NVIDIA on Linux
2020-09-26 23:52:49 +00:00
bunnei 442096298e Merge pull request #4703 from lioncash/desig7
shader/registry: Make use of designated initializers where applicable
2020-09-26 15:23:15 -07:00
Morph 86e4aa81e9 main: Allow applets to display on top while fullscreen
Using the Qt::WindowStaysOnTopHint flag allows these dialogs to show up on top while running in fullscreen. However, if yuzu goes out of focus (by alt-tabbing or otherwise), this flag does not seem to have an effect.
2020-09-26 06:55:47 -04:00
bunnei fbb5ca2633 Merge pull request #4718 from lioncash/vk
vk_command_pool: Add missing header guard
2020-09-26 00:13:48 -07:00
bunnei 891090799c Merge pull request #4720 from lioncash/header
audio_core: Remove unnecessary inclusions
2020-09-25 22:04:26 -07:00
bunnei ad76b00f1e Merge pull request #4723 from lioncash/typo
behavior_info: Fix typo Renerer -> Renderer
2020-09-25 22:03:59 -07:00
Lioncash 5c4e237902 core: Mark GetInstance() as deprecated
This way it's obvious that this function shouldn't be used in any future
code.
2020-09-25 19:23:23 -04:00
Lioncash 3e4a0a13cb frontend/controller: Eliminate dependency on the global system instance 2020-09-25 19:23:20 -04:00
Lioncash 90c6141164 command_generator: Make lookup table static constexpr
Allows compilers to elide needing to push these values on the stack
every time the function is called.
2020-09-25 18:33:04 -04:00
lat9nq ca26fd0f42 vk_stream_buffer: Fix initializing Vulkan with NVIDIA on Linux
The previous fix only partially solved the issue, as only certain GPUs that needed 9 or less MiB subtracted would work (i.e. GTX 980 Ti, GT 730). This takes from DXVK's example to divide `heap_size` by 2 to determine `allocable_size`. Additionally tested on my Quadro K4200, which previously required setting it to 12 to boot.
2020-09-25 17:42:59 -04:00
Lioncash dc83ca8914 behavior_info: Fix typo Renerer -> Renderer 2020-09-25 17:14:02 -04:00
Lioncash 4073931305 cubeb_sink: Use static_cast instead of reinterpret_cast in DataCallback()
Conversions from void* to the proper data type are well-defined and
supported by static_cast. We don't need to use reinterpret_cast here.
2020-09-25 17:10:02 -04:00
Lioncash 7c0908f301 codec: Make lookup table static constexpr
Allows compilers to elide needing to push these values on the stack
every time the function is called.
2020-09-25 14:24:25 -04:00
Lioncash 966966dc02 audio_core: Remove unnecessary inclusions
Same behavior, but removes header dependencies where they don't need to
be.
2020-09-25 13:19:42 -04:00
Lioncash 8b4ecf22d4 audio_core: Resolve sign conversion warnings
While were at it, we can also enable sign conversion warnings and other
common warnings as errors to prevent these from creeping back into the
codebase.
2020-09-25 01:22:47 -04:00
Lioncash 111852a983 effect_context: Make use of explicit where applicable
While we're at it we can make the destructor of the base class virtual
to ensure that any polymorphism issues never occur.
2020-09-25 00:27:11 -04:00
Lioncash 940d85241b vk_command_pool: Move definition of Pool into the cpp file
Allows the implementation details to be changed without recompiling any
files that include this header.
2020-09-25 00:15:52 -04:00
Lioncash 4ed4bba305 vk_command_pool: Make use of override on destructor 2020-09-25 00:14:10 -04:00
Lioncash e0f2db4376 vk_command_pool: Add missing header guard 2020-09-25 00:12:45 -04:00
LC 4d4afc1502 Merge pull request #4717 from lioncash/debug
service: Restore "unused" function
2020-09-25 00:07:19 -04:00
Lioncash f3a1bf53f9 service: Restore "unused" function
Turns out this function is actually used, but within a trace log.
2020-09-25 00:06:40 -04:00
bunnei 2634e3c6eb Merge pull request #4711 from lioncash/move5
arithmetic_integer_immediate: Make use of std::move where applicable
2020-09-24 21:02:42 -07:00
bunnei fa4294cc6f Merge pull request #4678 from Morph1984/LoadOpenContext-partial-impl
acc: Partially implement LoadOpenContext
2020-09-24 11:21:25 -07:00
Lioncash e3a615a616 arithmetic_integer_immediate: Make use of std::move where applicable
Same behavior, minus any redundant atomic reference count increments and
decrements.
2020-09-24 13:28:45 -04:00
bunnei d66b897a6d Merge pull request #4674 from ReinUsesLisp/timeline-semaphores
renderer_vulkan: Make unconditional use of VK_KHR_timeline_semaphore
2020-09-23 18:24:27 -07:00
german ddff03cff5 Use different timing for motion 2020-09-23 19:09:33 -05:00
bunnei 10e8acc451 Merge pull request #4618 from german77/GcAdapterAutoMap
Add automap feature for GC adapter
2020-09-23 15:55:26 -07:00
Lioncash 77532ebde3 shader/registry: Silence a -Wshadow warning 2020-09-23 15:10:25 -04:00
Lioncash cd6f4f7eed shader/registry: Remove unnecessary namespace qualifiers
Using statements already make these unnecessary.
2020-09-23 15:08:34 -04:00
Rodrigo Locatti 8b0f334e0c Merge pull request #4702 from lioncash/doc-warn
memory: Resolve a -Wdocumentation warning
2020-09-23 19:07:47 +00:00
Rodrigo Locatti c307ae2402 Merge pull request #4701 from lioncash/unused-proto
install_dialog: Remove unused function prototype
2020-09-23 19:07:27 +00:00
Rodrigo Locatti 6d9661939f Merge pull request #4700 from lioncash/copies
game_list: Eliminate redundant argument copies
2020-09-23 19:06:47 +00:00
Lioncash ffeb4ef83e shader/registry: Make use of designated initializers where applicable
Same behavior, less repetition.
2020-09-23 15:06:25 -04:00
Lioncash b14d344dfc memory: Resolve a -Wdocumentation warning
memory doesn't exist as a parameter any more.
2020-09-23 13:39:27 -04:00
Lioncash aa35e51fcd install_dialog: Make use of [[nodiscard]] where applicable
Allows the compiler to warn against cases where the return value isn't
used (which would be a bug).
2020-09-23 13:22:04 -04:00
Lioncash e107870bc8 install_dialog: Remove unused function prototype
This function doesn't have an implementation, so it can be removed to
prevent others from unintentionally using it.
2020-09-23 13:20:12 -04:00
Lioncash f43a1da808 game_list: Make game list function naming consistent
Makes the naming consistent with the rest of the functions that are
present.
2020-09-23 11:28:11 -04:00
Lioncash d264b7375c game_list: Eliminate redundant argument copies
Several functions can be taken by const reference to avoid copies
2020-09-23 11:20:12 -04:00
Rodrigo Locatti b8219ec838 Merge pull request #4699 from lioncash/move3
control_flow: Make use of std::move in InsertBranch()
2020-09-23 06:20:59 +00:00
Lioncash 0dc6967ff1 control_flow: emplace elements in place within TryQuery()
Places data structures where they'll eventually be moved to to avoid
needing to even move them in the first place.
2020-09-22 22:54:36 -04:00
Lioncash fcd0145eb5 control_flow: Make use of std::move in InsertBranch()
Avoids unnecessary atomic increments and decrements.
2020-09-22 22:48:09 -04:00
Rodrigo Locatti 2b863c9aa3 Merge pull request #4698 from lioncash/optional-null
General: Make use of std::nullopt where applicable
2020-09-22 23:37:51 +00:00
Lioncash ff45c39578 General: Make use of std::nullopt where applicable
Allows some implementations to avoid completely zeroing out the internal
buffer of the optional, and instead only set the validity byte within
the structure.

This also makes it consistent how we return empty optionals.
2020-09-22 17:32:33 -04:00
bunnei c07fd2898b Merge pull request #4697 from lioncash/copy5
ips_layer: Eliminate a redundant copy in Parse()
2020-09-22 13:42:47 -07:00
Lioncash a881efbf26 ips_layer: Eliminate a redundant copy in Parse()
Prevents unnecessary copying of the line being parsed.
2020-09-22 16:38:45 -04:00
bunnei 53829d4cbd Merge pull request #4675 from Morph1984/fix-boot-multicontent
submission_package: Account for multi-content NSPs
2020-09-21 16:36:46 -07:00
Morph 7a504a9365 acc: Stub LoadOpenContext
This is used in multiple games such as:
- Clubhouse Games: 51 Worldwide Classics
- Grandia HD Collection
- XCOM 2 Collection
- Baldur's Gate 1/2
- Dr Kawashima's Brain Training
- Super Mario 3D All-Stars
2020-09-21 01:01:02 -04:00
Rodrigo Locatti a2eb44db82 Merge pull request #4692 from ReinUsesLisp/remove-vsync
renderer_opengl: Remove emulated mailbox presentation
2020-09-21 00:21:32 +00:00
bunnei 754109fd54 Merge pull request #4683 from Morph1984/NpadHandheldActivationMode-impl
hid: Implement Get/SetNpadHandheldActivationMode
2020-09-20 12:39:20 -07:00
ReinUsesLisp 7003090187 renderer_opengl: Remove emulated mailbox presentation
Emulated mailbox presentation was causing performance issues on
Nvidia's OpenGL driver. Remove it.
2020-09-20 16:29:41 -03:00
bunnei 8a85a562ed Merge pull request #4643 from FearlessTobi/decrease-pad-update-interval
Test: Decrease pad_update_ns
2020-09-19 00:39:50 -07:00
ReinUsesLisp 4f5bbe56ba vk_query_cache: Hack counter destructor to avoid reserving queries
This is a hack to destroy all HostCounter instances before the base
class destructor is called. The query cache should be redesigned to have
a proper ownership model instead of using shared pointers.

For now, destroy the host counter hierarchy from the derived class
destructor.
2020-09-19 01:47:29 -03:00
ReinUsesLisp 58b0ae84b5 renderer_vulkan: Make unconditional use of VK_KHR_timeline_semaphore
This reworks how host<->device synchronization works on the Vulkan
backend. Instead of "protecting" resources with a fence and signalling
these as free when the fence is known to be signalled by the host GPU,
use timeline semaphores.

Vulkan timeline semaphores allow use to work on a subset of D3D12
fences. As far as we are concerned, timeline semaphores are a value set
by the host or the device that can be waited by either of them.

Taking advantange of this, we can have a monolithically increasing
atomic value for each submission to the graphics queue. Instead of
protecting resources with a fence, we simply store the current logical
tick (the atomic value stored in CPU memory). When we want to know if a
resource is free, it can be compared to the current GPU tick.

This greatly simplifies resource management code and the free status of
resources should have less false negatives.

To workaround bugs in validation layers, when these are attached there's
a thread waiting for timeline semaphores.
2020-09-19 01:46:37 -03:00
german c5e257017f Add automap feature for GC adapter 2020-09-18 16:51:16 -05:00
Rodrigo Locatti 059dd724d6 Merge pull request #4684 from lioncash/desig4
fermi_2d: Make use of designated initializers
2020-09-18 19:14:56 +00:00
Lioncash 91bca9eb0b fermi_2d: Make use of designated initializers
Same behavior, less repetition. We can also ensure all members of Config
are initialized.
2020-09-18 13:55:21 -04:00
Morph ab961e0701 hid: Implement Get/SetNpadHandheldActivationMode
- Used in Clubhouse Games: 51 Worldwide Classics
2020-09-18 10:10:30 -04:00
David 050a4a401b Merge pull request #4680 from Morph1984/fix-motion-mapping
configure_input_player: Fixes motion mapping using ConfigureButtonClick
2020-09-18 21:03:50 +10:00
Morph 70499b8cbd configure_input_player: Fixes motion mapping using ConfigureButtonClick 2020-09-18 03:56:31 -04:00
bunnei 8568f44ffa Merge pull request #4647 from Morph1984/readd-context-menu
configure_input_player: Re-add "Clear" context menu option
2020-09-17 22:59:13 -07:00
bunnei 669005b75e Merge pull request #4676 from Morph1984/GetPreviousProgramIndex-impl
am: Stub GetPreviousProgramIndex
2020-09-17 22:12:57 -07:00
Morph 65d9def873 configure_input_player: Re-add "Clear" context menu option
The context menu was removed in Mjölnir Part 1 as part of the input rewrite as we were unaware of it's usage statistics.
However, as this was the only way to clear the inputs of individual buttons, this PR will re-add it back in.
2020-09-17 21:57:06 -04:00
Morph 41c2f5200c submission_package: Account for multi-content NSPs
Previously we assumed a submission package can only contain one Program NCA with a single TitleID.
However, Super Mario 3D All-Stars contains four Program NCAs, each with their unique TitleIDs.
This accounts for the existence of multi-content games such as this one.
- Fixes booting Super Mario 3D All-Stars from the games list.
2020-09-17 20:44:51 -04:00
FearlessTobi 57162e1df3 Test: Decrease pad_update_ns
There have been reports of quite heavy input lag in the past.
Compared to Citra for example, our pad_update_ns value is very high.
So let's decrease it and see if it helps with this problem.
2020-09-10 16:38:53 +02:00
bunnei 1adf640d37 service: nvhost_vic: Ignore Submit commands. 2020-06-04 22:32:28 -04:00
Zach Hilman 34635a42c0 nvdrv: Stub nvdec/vic ioctls to bypass nvdec movies 2020-06-04 22:32:28 -04:00
131 changed files with 1759 additions and 1587 deletions
+1
View File
@@ -9,6 +9,7 @@ stages:
displayName: 'build'
jobs:
- job: build
timeoutInMinutes: 120
displayName: 'windows-msvc'
pool:
vmImage: windows-2019
+10
View File
@@ -44,6 +44,16 @@ add_library(audio_core STATIC
create_target_directory_groups(audio_core)
if (NOT MSVC)
target_compile_options(audio_core PRIVATE
-Werror=ignored-qualifiers
-Werror=implicit-fallthrough
-Werror=reorder
-Werror=sign-compare
-Werror=unused-variable
)
endif()
target_link_libraries(audio_core PUBLIC common core)
target_link_libraries(audio_core PRIVATE SoundTouch)
+1 -4
View File
@@ -3,16 +3,13 @@
// Refer to the license.txt file included.
#include <vector>
#include "audio_core/algorithm/interpolate.h"
#include "audio_core/audio_out.h"
#include "audio_core/audio_renderer.h"
#include "audio_core/codec.h"
#include "audio_core/common.h"
#include "audio_core/info_updater.h"
#include "audio_core/voice_context.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
#include "core/settings.h"
-1
View File
@@ -21,7 +21,6 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Core::Timing {
+3 -3
View File
@@ -57,15 +57,15 @@ bool BehaviorInfo::IsLongSizePreDelaySupported() const {
return AudioCommon::IsRevisionSupported(3, user_revision);
}
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const {
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit80PercentSupported() const {
return AudioCommon::IsRevisionSupported(5, user_revision);
}
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const {
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit75PercentSupported() const {
return AudioCommon::IsRevisionSupported(4, user_revision);
}
bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const {
bool BehaviorInfo::IsAudioRendererProcessingTimeLimit70PercentSupported() const {
return AudioCommon::IsRevisionSupported(1, user_revision);
}
+3 -3
View File
@@ -49,9 +49,9 @@ public:
bool IsAdpcmLoopContextBugFixed() const;
bool IsSplitterSupported() const;
bool IsLongSizePreDelaySupported() const;
bool IsAudioRenererProcessingTimeLimit80PercentSupported() const;
bool IsAudioRenererProcessingTimeLimit75PercentSupported() const;
bool IsAudioRenererProcessingTimeLimit70PercentSupported() const;
bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
bool IsElapsedFrameCountSupported() const;
bool IsMemoryPoolForceMappingEnabled() const;
bool IsFlushVoiceWaveBuffersSupported() const;
+3 -2
View File
@@ -16,8 +16,9 @@ std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM
constexpr std::size_t FRAME_LEN = 8;
constexpr std::size_t SAMPLES_PER_FRAME = 14;
constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
static constexpr std::array<int, 16> SIGNED_NIBBLES{
0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1,
};
const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
const std::size_t ret_size =
+1 -1
View File
@@ -38,7 +38,7 @@ using ADPCM_Coeff = std::array<s16, 16>;
* @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length
*/
std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
std::vector<s16> DecodeADPCM(const u8* data, std::size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state);
}; // namespace AudioCore::Codec
+9 -8
View File
@@ -152,7 +152,7 @@ void CommandGenerator::GenerateVoiceCommand(ServerVoiceInfo& voice_info) {
if (!destination_data->IsConfigured()) {
continue;
}
if (destination_data->GetMixId() >= mix_context.GetCount()) {
if (destination_data->GetMixId() >= static_cast<int>(mix_context.GetCount())) {
continue;
}
@@ -435,7 +435,7 @@ void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* inf
GetMixBuffer(output_index), worker_params.sample_count, offset, write_count);
memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
if (samples_read != worker_params.sample_count &&
if (samples_read != static_cast<int>(worker_params.sample_count) &&
samples_read <= params.sample_count) {
std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read);
}
@@ -611,7 +611,8 @@ void CommandGenerator::GenerateMixCommands(ServerMixInfo& mix_info) {
const auto& dest_mix = mix_context.GetInfo(destination_data->GetMixId());
const auto& dest_in_params = dest_mix.GetInParams();
const auto mix_index = (base - 1) % in_params.buffer_count + in_params.buffer_offset;
for (std::size_t i = 0; i < dest_in_params.buffer_count; i++) {
for (std::size_t i = 0; i < static_cast<std::size_t>(dest_in_params.buffer_count);
i++) {
const auto mixed_volume = in_params.volume * destination_data->GetMixVolume(i);
if (mixed_volume != 0.0f) {
GenerateMixCommand(dest_in_params.buffer_offset + i, mix_index, mixed_volume,
@@ -704,7 +705,7 @@ s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_s
std::vector<s16> buffer(samples_processed * channel_count);
memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
for (std::size_t i = 0; i < samples_processed; i++) {
for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) {
sample_buffer[mix_offset + i] = buffer[i * channel_count + channel];
}
}
@@ -726,8 +727,9 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
return 0;
}
constexpr std::array<int, 16> SIGNED_NIBBLES = {
{0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
static constexpr std::array<int, 16> SIGNED_NIBBLES{
0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1,
};
constexpr std::size_t FRAME_LEN = 8;
constexpr std::size_t NIBBLES_PER_SAMPLE = 16;
@@ -789,7 +791,7 @@ s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_s
position_in_frame += 2;
// Decode entire frame
if (remaining_samples >= SAMPLES_PER_FRAME) {
if (remaining_samples >= static_cast<int>(SAMPLES_PER_FRAME)) {
for (std::size_t i = 0; i < SAMPLES_PER_FRAME / 2; i++) {
// Sample 1
@@ -866,7 +868,6 @@ void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* o
const auto resample_rate = static_cast<s32>(
static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
auto* output_base = output;
if (dsp_state.fraction + sample_count * resample_rate >
static_cast<s32>(SCALED_MIX_BUFFER_SIZE - 4ULL)) {
return;
-1
View File
@@ -7,7 +7,6 @@
#include <array>
#include "audio_core/common.h"
#include "audio_core/voice_context.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
namespace Core::Memory {
+1
View File
@@ -3,6 +3,7 @@
// Refer to the license.txt file included.
#pragma once
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+2 -2
View File
@@ -192,8 +192,8 @@ SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames) {
CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data);
u8* buffer = reinterpret_cast<u8*>(output_buffer);
auto* impl = static_cast<CubebSinkStream*>(user_data);
auto* buffer = static_cast<u8*>(output_buffer);
if (!impl) {
return {};
+4 -4
View File
@@ -184,19 +184,19 @@ void EffectAuxInfo::UpdateForCommandGeneration() {
}
}
const VAddr EffectAuxInfo::GetSendInfo() const {
VAddr EffectAuxInfo::GetSendInfo() const {
return send_info;
}
const VAddr EffectAuxInfo::GetSendBuffer() const {
VAddr EffectAuxInfo::GetSendBuffer() const {
return send_buffer;
}
const VAddr EffectAuxInfo::GetRecvInfo() const {
VAddr EffectAuxInfo::GetRecvInfo() const {
return recv_info;
}
const VAddr EffectAuxInfo::GetRecvBuffer() const {
VAddr EffectAuxInfo::GetRecvBuffer() const {
return recv_buffer;
}
+16 -17
View File
@@ -166,13 +166,13 @@ public:
std::array<u8, 0xa0> raw;
};
};
static_assert(sizeof(EffectInfo::InParams) == 0xc0, "InParams is an invalid size");
static_assert(sizeof(InParams) == 0xc0, "InParams is an invalid size");
struct OutParams {
UsageStatus status{};
INSERT_PADDING_BYTES(15);
};
static_assert(sizeof(EffectInfo::OutParams) == 0x10, "OutParams is an invalid size");
static_assert(sizeof(OutParams) == 0x10, "OutParams is an invalid size");
};
struct AuxAddress {
@@ -184,8 +184,8 @@ struct AuxAddress {
class EffectBase {
public:
EffectBase(EffectType effect_type);
~EffectBase();
explicit EffectBase(EffectType effect_type);
virtual ~EffectBase();
virtual void Update(EffectInfo::InParams& in_params) = 0;
virtual void UpdateForCommandGeneration() = 0;
@@ -206,8 +206,7 @@ protected:
template <typename T>
class EffectGeneric : public EffectBase {
public:
EffectGeneric(EffectType effect_type) : EffectBase::EffectBase(effect_type) {}
~EffectGeneric() = default;
explicit EffectGeneric(EffectType effect_type) : EffectBase(effect_type) {}
T& GetParams() {
return internal_params;
@@ -224,7 +223,7 @@ private:
class EffectStubbed : public EffectBase {
public:
explicit EffectStubbed();
~EffectStubbed();
~EffectStubbed() override;
void Update(EffectInfo::InParams& in_params) override;
void UpdateForCommandGeneration() override;
@@ -233,7 +232,7 @@ public:
class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
public:
explicit EffectI3dl2Reverb();
~EffectI3dl2Reverb();
~EffectI3dl2Reverb() override;
void Update(EffectInfo::InParams& in_params) override;
void UpdateForCommandGeneration() override;
@@ -245,7 +244,7 @@ private:
class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
public:
explicit EffectBiquadFilter();
~EffectBiquadFilter();
~EffectBiquadFilter() override;
void Update(EffectInfo::InParams& in_params) override;
void UpdateForCommandGeneration() override;
@@ -254,14 +253,14 @@ public:
class EffectAuxInfo : public EffectGeneric<AuxInfo> {
public:
explicit EffectAuxInfo();
~EffectAuxInfo();
~EffectAuxInfo() override;
void Update(EffectInfo::InParams& in_params) override;
void UpdateForCommandGeneration() override;
const VAddr GetSendInfo() const;
const VAddr GetSendBuffer() const;
const VAddr GetRecvInfo() const;
const VAddr GetRecvBuffer() const;
VAddr GetSendInfo() const;
VAddr GetSendBuffer() const;
VAddr GetRecvInfo() const;
VAddr GetRecvBuffer() const;
private:
VAddr send_info{};
@@ -275,7 +274,7 @@ private:
class EffectDelay : public EffectGeneric<DelayParams> {
public:
explicit EffectDelay();
~EffectDelay();
~EffectDelay() override;
void Update(EffectInfo::InParams& in_params) override;
void UpdateForCommandGeneration() override;
@@ -287,7 +286,7 @@ private:
class EffectBufferMixer : public EffectGeneric<BufferMixerParams> {
public:
explicit EffectBufferMixer();
~EffectBufferMixer();
~EffectBufferMixer() override;
void Update(EffectInfo::InParams& in_params) override;
void UpdateForCommandGeneration() override;
@@ -296,7 +295,7 @@ public:
class EffectReverb : public EffectGeneric<ReverbParams> {
public:
explicit EffectReverb();
~EffectReverb();
~EffectReverb() override;
void Update(EffectInfo::InParams& in_params) override;
void UpdateForCommandGeneration() override;
+3 -4
View File
@@ -64,7 +64,6 @@ bool InfoUpdater::UpdateBehaviorInfo(BehaviorInfo& in_behavior_info) {
}
bool InfoUpdater::UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info) {
const auto force_mapping = behavior_info.IsMemoryPoolForceMappingEnabled();
const auto memory_pool_count = memory_pool_info.size();
const auto total_memory_pool_in = sizeof(ServerMemoryPoolInfo::InParams) * memory_pool_count;
const auto total_memory_pool_out = sizeof(ServerMemoryPoolInfo::OutParams) * memory_pool_count;
@@ -174,7 +173,7 @@ bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
}
// Voice states for each channel
std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> voice_states{};
ASSERT(in_params.id < voice_count);
ASSERT(static_cast<std::size_t>(in_params.id) < voice_count);
// Grab our current voice info
auto& voice_info = voice_context.GetInfo(static_cast<std::size_t>(in_params.id));
@@ -352,8 +351,8 @@ ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buf
for (std::size_t i = 0; i < mix_count; i++) {
const auto& in = mix_in_params[i];
total_buffer_count += in.buffer_count;
if (in.dest_mix_id > mix_count && in.dest_mix_id != AudioCommon::NO_MIX &&
in.mix_id != AudioCommon::FINAL_MIX) {
if (static_cast<std::size_t>(in.dest_mix_id) > mix_count &&
in.dest_mix_id != AudioCommon::NO_MIX && in.mix_id != AudioCommon::FINAL_MIX) {
LOG_ERROR(
Audio,
"Invalid mix destination, mix_id={:X}, dest_mix_id={:X}, mix_buffer_count={:X}",
+2 -2
View File
@@ -53,7 +53,7 @@ void MixContext::UpdateDistancesFromFinalMix() {
auto mix_id = in_params.mix_id;
// Needs to be referenced out of scope
s32 distance_to_final_mix{AudioCommon::FINAL_MIX};
for (; distance_to_final_mix < info_count; distance_to_final_mix++) {
for (; distance_to_final_mix < static_cast<s32>(info_count); distance_to_final_mix++) {
if (mix_id == AudioCommon::FINAL_MIX) {
// If we're at the final mix, we're done
break;
@@ -77,7 +77,7 @@ void MixContext::UpdateDistancesFromFinalMix() {
}
// If we're out of range for our distance, mark it as no final mix
if (distance_to_final_mix >= info_count) {
if (distance_to_final_mix >= static_cast<s32>(info_count)) {
distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
}
+3 -3
View File
@@ -306,7 +306,7 @@ bool SplitterContext::UpdateInfo(const std::vector<u8>& input, std::size_t& inpu
break;
}
if (header.send_id < 0 || header.send_id > info_count) {
if (header.send_id < 0 || static_cast<std::size_t>(header.send_id) > info_count) {
LOG_ERROR(Audio, "Bad splitter data id");
break;
}
@@ -348,7 +348,7 @@ bool SplitterContext::UpdateData(const std::vector<u8>& input, std::size_t& inpu
break;
}
if (header.splitter_id < 0 || header.splitter_id > data_count) {
if (header.splitter_id < 0 || static_cast<std::size_t>(header.splitter_id) > data_count) {
LOG_ERROR(Audio, "Bad splitter data id");
break;
}
@@ -434,7 +434,7 @@ const std::vector<s32>& NodeStates::GetIndexList() const {
}
void NodeStates::PushTsortResult(s32 index) {
ASSERT(index < node_count);
ASSERT(index < static_cast<s32>(node_count));
index_list[index_pos++] = index;
}
-1
View File
@@ -12,7 +12,6 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
#include "core/settings.h"
namespace AudioCore {
+2 -2
View File
@@ -488,11 +488,11 @@ s32 VoiceContext::DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer,
// Fast path
if (channel_count == 1) {
for (std::size_t i = 0; i < samples_processed; i++) {
for (std::ptrdiff_t i = 0; i < samples_processed; i++) {
output_buffer[i] = buffer_data[i];
}
} else {
for (std::size_t i = 0; i < samples_processed; i++) {
for (std::ptrdiff_t i = 0; i < samples_processed; i++) {
output_buffer[i] = buffer_data[i * channel_count + channel];
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ namespace Common {
using base_timer = std::chrono::steady_clock;
using base_time_point = std::chrono::time_point<base_timer>;
class StandardWallClock : public WallClock {
class StandardWallClock final : public WallClock {
public:
StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency)
: WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) {
+2
View File
@@ -13,6 +13,8 @@ namespace Common {
class WallClock {
public:
virtual ~WallClock() = default;
/// Returns current wall time in nanoseconds
[[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
+1 -1
View File
@@ -12,7 +12,7 @@
namespace Common {
namespace X64 {
class NativeClock : public WallClock {
class NativeClock final : public WallClock {
public:
NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency);
+3 -3
View File
@@ -34,7 +34,7 @@ std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigne
CoprocReg CRm, unsigned opc2) {
LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn,
CRm, opc2);
return {};
return std::nullopt;
}
CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
@@ -115,7 +115,7 @@ std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_trans
LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
long_transfer ? "l" : "", CRd);
}
return {};
return std::nullopt;
}
std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
@@ -127,7 +127,7 @@ std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_tran
LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
long_transfer ? "l" : "", CRd);
}
return {};
return std::nullopt;
}
} // namespace Core
+1 -1
View File
@@ -120,7 +120,7 @@ public:
* Gets the instance of the System singleton class.
* @returns Reference to the instance of the System singleton class.
*/
static System& GetInstance() {
[[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() {
return s_instance;
}
+5 -5
View File
@@ -323,7 +323,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
subsection_buckets.back().entries.push_back({size, {0}, 0});
std::optional<Core::Crypto::Key128> key = {};
std::optional<Core::Crypto::Key128> key;
if (encrypted) {
if (has_rights_id) {
status = Loader::ResultStatus::Success;
@@ -442,18 +442,18 @@ std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
memcpy(rights_id.data(), header.rights_id.data(), 16);
if (rights_id == u128{}) {
status = Loader::ResultStatus::ErrorInvalidRightsID;
return {};
return std::nullopt;
}
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
if (titlekey == Core::Crypto::Key128{}) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
return {};
return std::nullopt;
}
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
status = Loader::ResultStatus::ErrorMissingTitlekek;
return {};
return std::nullopt;
}
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -477,7 +477,7 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
case NCASectionCryptoType::BKTR:
LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
std::optional<Core::Crypto::Key128> key = {};
std::optional<Core::Crypto::Key128> key;
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
+4 -2
View File
@@ -245,9 +245,11 @@ void IPSwitchCompiler::Parse() {
// Read rest of patch
while (true) {
if (i + 1 >= lines.size())
if (i + 1 >= lines.size()) {
break;
const auto patch_line = lines[++i];
}
const auto& patch_line = lines[++i];
// Start of new patch
if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
+5 -5
View File
@@ -267,9 +267,9 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
}
const CNMT cnmt(inner_file);
auto& ncas_title = ncas[cnmt.GetTitleID()];
ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
ncas[cnmt.GetTitleID()][{cnmt.GetType(), ContentRecordType::Meta}] = nca;
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexToString(rec.nca_id, false);
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
@@ -287,12 +287,12 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0);
if (next_nca->GetType() == NCAContentType::Program) {
program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
}
if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
(next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
(cnmt.GetTitleID() & 0x800) != 0)) {
ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca);
(next_nca->GetTitleId() & 0x800) != 0)) {
ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca);
}
}
+4 -3
View File
@@ -169,11 +169,12 @@ VfsDirectory::~VfsDirectory() = default;
std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
u8 out{};
std::size_t size = Read(&out, 1, offset);
if (size == 1)
const std::size_t size = Read(&out, sizeof(u8), offset);
if (size == 1) {
return out;
}
return {};
return std::nullopt;
}
std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
+4 -3
View File
@@ -58,10 +58,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
}
std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
if (r_offset < size)
return file->ReadByte(offset + r_offset);
if (r_offset >= size) {
return std::nullopt;
}
return {};
return file->ReadByte(offset + r_offset);
}
std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
+5 -3
View File
@@ -54,9 +54,11 @@ public:
}
std::optional<u8> ReadByte(std::size_t offset) const override {
if (offset < size)
return value;
return {};
if (offset >= size) {
return std::nullopt;
}
return value;
}
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
+4 -4
View File
@@ -4,7 +4,6 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/frontend/applets/controller.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/hid/hid.h"
@@ -14,6 +13,9 @@ namespace Core::Frontend {
ControllerApplet::~ControllerApplet() = default;
DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_)
: service_manager{service_manager_} {}
DefaultControllerApplet::~DefaultControllerApplet() = default;
void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
@@ -21,9 +23,7 @@ void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callb
LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
auto& npad =
Core::System::GetInstance()
.ServiceManager()
.GetService<Service::HID::Hid>("hid")
service_manager.GetService<Service::HID::Hid>("hid")
->GetAppletResource()
->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
+8
View File
@@ -8,6 +8,10 @@
#include "common/common_types.h"
namespace Service::SM {
class ServiceManager;
}
namespace Core::Frontend {
using BorderColor = std::array<u8, 4>;
@@ -39,10 +43,14 @@ public:
class DefaultControllerApplet final : public ControllerApplet {
public:
explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_);
~DefaultControllerApplet() override;
void ReconfigureControllers(std::function<void()> callback,
ControllerParameters parameters) const override;
private:
Service::SM::ServiceManager& service_manager;
};
} // namespace Core::Frontend
+3
View File
@@ -33,6 +33,9 @@ public:
virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
return {};
}
virtual bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const {
return {};
}
};
/// An abstract class template for a factory that can create input devices.
+11
View File
@@ -774,6 +774,17 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
// This is similar to GetBaasAccountManagerForApplication
// This command is used concurrently with ListOpenContextStoredUsers
// TODO: Find the differences between this and GetBaasAccountManagerForApplication
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IManagerForApplication>(profile_manager->GetLastOpenedUser());
}
void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
+1
View File
@@ -34,6 +34,7 @@ public:
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
void GetProfileEditor(Kernel::HLERequestContext& ctx);
void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
void LoadOpenContext(Kernel::HLERequestContext& ctx);
void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
private:
+1 -1
View File
@@ -29,7 +29,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{110, nullptr, "StoreSaveDataThumbnail"},
{111, nullptr, "ClearSaveDataThumbnail"},
{120, nullptr, "CreateGuestLoginRequest"},
{130, nullptr, "LoadOpenContext"}, // 5.0.0+
{130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+
{131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
{140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
{141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
+2 -1
View File
@@ -206,7 +206,8 @@ void AppletManager::SetDefaultAppletFrontendSet() {
void AppletManager::SetDefaultAppletsIfMissing() {
if (frontend.controller == nullptr) {
frontend.controller = std::make_unique<Core::Frontend::DefaultControllerApplet>();
frontend.controller =
std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
}
if (frontend.e_commerce == nullptr) {
@@ -31,6 +31,10 @@ public:
virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) = 0;
// When the controller is requesting a motion update for the shared memory
virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) {}
// Called when input devices should be loaded
virtual void OnLoadInputDevices() = 0;
+163 -88
View File
@@ -365,44 +365,6 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
}
const u32 npad_index = static_cast<u32>(i);
const std::array<SixAxisGeneric*, 6> controller_sixaxes{
&npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
&npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
};
for (auto* sixaxis_sensor : controller_sixaxes) {
sixaxis_sensor->common.entry_count = 16;
sixaxis_sensor->common.total_entry_count = 17;
const auto& last_entry =
sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
sixaxis_sensor->common.last_entry_index =
(sixaxis_sensor->common.last_entry_index + 1) % 17;
auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
cur_entry.timestamp = last_entry.timestamp + 1;
cur_entry.timestamp2 = cur_entry.timestamp;
}
// Try to read sixaxis sensor states
std::array<MotionDevice, 2> motion_devices;
if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_devices.size(); ++e) {
const auto& device = motions[i][e];
if (device) {
std::tie(motion_devices[e].accel, motion_devices[e].gyro,
motion_devices[e].rotation, motion_devices[e].orientation) =
device->GetStatus();
sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
}
}
}
RequestPadStateUpdate(npad_index);
auto& pad_state = npad_pad_states[npad_index];
@@ -446,13 +408,6 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
main_controller.pad.r_stick = pad_state.r_stick;
libnx_entry.connection_status.IsWired.Assign(1);
if (sixaxis_sensors_enabled && motions[i][0]) {
full_sixaxis_entry.accel = motion_devices[0].accel;
full_sixaxis_entry.gyro = motion_devices[0].gyro;
full_sixaxis_entry.rotation = motion_devices[0].rotation;
full_sixaxis_entry.orientation = motion_devices[0].orientation;
}
break;
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
@@ -471,13 +426,6 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
libnx_entry.connection_status.IsRightJoyWired.Assign(1);
if (sixaxis_sensors_enabled && motions[i][0]) {
handheld_sixaxis_entry.accel = motion_devices[0].accel;
handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
}
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
@@ -490,21 +438,6 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
if (sixaxis_sensors_enabled && motions[i][0]) {
// Set motion for the left joycon
dual_left_sixaxis_entry.accel = motion_devices[0].accel;
dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
}
if (sixaxis_sensors_enabled && motions[i][1]) {
// Set motion for the right joycon
dual_right_sixaxis_entry.accel = motion_devices[1].accel;
dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
}
break;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
@@ -515,13 +448,6 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
left_entry.pad.r_stick = pad_state.r_stick;
libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
if (sixaxis_sensors_enabled && motions[i][0]) {
left_sixaxis_entry.accel = motion_devices[0].accel;
left_sixaxis_entry.gyro = motion_devices[0].gyro;
left_sixaxis_entry.rotation = motion_devices[0].rotation;
left_sixaxis_entry.orientation = motion_devices[0].orientation;
}
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
@@ -532,13 +458,6 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
right_entry.pad.r_stick = pad_state.r_stick;
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
if (sixaxis_sensors_enabled && motions[i][1]) {
right_sixaxis_entry.accel = motion_devices[1].accel;
right_sixaxis_entry.gyro = motion_devices[1].gyro;
right_sixaxis_entry.rotation = motion_devices[1].rotation;
right_sixaxis_entry.orientation = motion_devices[1].orientation;
}
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
@@ -561,6 +480,143 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
shared_memory_entries.size() * sizeof(NPadEntry));
}
void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t data_len) {
if (!IsControllerActivated()) {
return;
}
for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
auto& npad = shared_memory_entries[i];
const auto& controller_type = connected_controllers[i].type;
if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
continue;
}
const std::array<SixAxisGeneric*, 6> controller_sixaxes{
&npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
&npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
};
for (auto* sixaxis_sensor : controller_sixaxes) {
sixaxis_sensor->common.entry_count = 16;
sixaxis_sensor->common.total_entry_count = 17;
const auto& last_entry =
sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
sixaxis_sensor->common.last_entry_index =
(sixaxis_sensor->common.last_entry_index + 1) % 17;
auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
cur_entry.timestamp = last_entry.timestamp + 1;
cur_entry.timestamp2 = cur_entry.timestamp;
}
// Try to read sixaxis sensor states
std::array<MotionDevice, 2> motion_devices;
if (sixaxis_sensors_enabled && Settings::values.motion_enabled) {
sixaxis_at_rest = true;
for (std::size_t e = 0; e < motion_devices.size(); ++e) {
const auto& device = motions[i][e];
if (device) {
std::tie(motion_devices[e].accel, motion_devices[e].gyro,
motion_devices[e].rotation, motion_devices[e].orientation) =
device->GetStatus();
sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
}
}
}
auto& main_controller =
npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
auto& handheld_entry =
npad.handheld_states.npad[npad.handheld_states.common.last_entry_index];
auto& dual_entry = npad.dual_states.npad[npad.dual_states.common.last_entry_index];
auto& left_entry = npad.left_joy_states.npad[npad.left_joy_states.common.last_entry_index];
auto& right_entry =
npad.right_joy_states.npad[npad.right_joy_states.common.last_entry_index];
auto& pokeball_entry =
npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
auto& full_sixaxis_entry =
npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
auto& handheld_sixaxis_entry =
npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
auto& dual_left_sixaxis_entry =
npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
auto& dual_right_sixaxis_entry =
npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
auto& left_sixaxis_entry =
npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
auto& right_sixaxis_entry =
npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
break;
case NPadControllerType::ProController:
if (sixaxis_sensors_enabled && motions[i][0]) {
full_sixaxis_entry.accel = motion_devices[0].accel;
full_sixaxis_entry.gyro = motion_devices[0].gyro;
full_sixaxis_entry.rotation = motion_devices[0].rotation;
full_sixaxis_entry.orientation = motion_devices[0].orientation;
}
break;
case NPadControllerType::Handheld:
if (sixaxis_sensors_enabled && motions[i][0]) {
handheld_sixaxis_entry.accel = motion_devices[0].accel;
handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
}
break;
case NPadControllerType::JoyDual:
if (sixaxis_sensors_enabled && motions[i][0]) {
// Set motion for the left joycon
dual_left_sixaxis_entry.accel = motion_devices[0].accel;
dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
}
if (sixaxis_sensors_enabled && motions[i][1]) {
// Set motion for the right joycon
dual_right_sixaxis_entry.accel = motion_devices[1].accel;
dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
}
break;
case NPadControllerType::JoyLeft:
if (sixaxis_sensors_enabled && motions[i][0]) {
left_sixaxis_entry.accel = motion_devices[0].accel;
left_sixaxis_entry.gyro = motion_devices[0].gyro;
left_sixaxis_entry.rotation = motion_devices[0].rotation;
left_sixaxis_entry.orientation = motion_devices[0].orientation;
}
break;
case NPadControllerType::JoyRight:
if (sixaxis_sensors_enabled && motions[i][1]) {
right_sixaxis_entry.accel = motion_devices[1].accel;
right_sixaxis_entry.gyro = motion_devices[1].gyro;
right_sixaxis_entry.rotation = motion_devices[1].rotation;
right_sixaxis_entry.orientation = motion_devices[1].orientation;
}
break;
case NPadControllerType::Pokeball:
break;
}
}
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
shared_memory_entries.size() * sizeof(NPadEntry));
}
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
style.raw = style_set.raw;
}
@@ -593,6 +649,14 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
return hold_type;
}
void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
handheld_activation_mode = activation_mode;
}
Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const {
return handheld_activation_mode;
}
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
@@ -601,20 +665,31 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
}
}
void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
void Controller_NPad::VibrateController(const std::vector<u32>& controllers,
const std::vector<Vibration>& vibrations) {
LOG_DEBUG(Service_HID, "(STUBBED) called");
LOG_TRACE(Service_HID, "called");
if (!Settings::values.vibration_enabled || !can_controllers_vibrate) {
return;
}
for (std::size_t i = 0; i < controller_ids.size(); i++) {
std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
if (connected_controllers[controller_pos].is_connected) {
// TODO(ogniK): Vibrate the physical controller
bool success = true;
for (std::size_t i = 0; i < controllers.size(); ++i) {
if (!connected_controllers[i].is_connected) {
continue;
}
using namespace Settings::NativeButton;
const auto& button_state = buttons[i];
if (button_state[A - BUTTON_HID_BEGIN]) {
if (button_state[A - BUTTON_HID_BEGIN]->SetRumblePlay(
vibrations[0].amp_high, vibrations[0].amp_low, vibrations[0].freq_high,
vibrations[0].freq_low)) {
success = false;
}
}
}
last_processed_vibration = vibrations.back();
if (success) {
last_processed_vibration = vibrations.back();
}
}
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
+15 -1
View File
@@ -32,6 +32,10 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
// When the controller is requesting a motion update for the shared memory
void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) override;
// Called when input devices should be loaded
void OnLoadInputDevices() override;
@@ -74,6 +78,12 @@ public:
Single = 1,
};
enum class NpadHandheldActivationMode : u64 {
Dual = 0,
Single = 1,
None = 2,
};
enum class NPadControllerType {
None,
ProController,
@@ -110,9 +120,12 @@ public:
void SetHoldType(NpadHoldType joy_hold_type);
NpadHoldType GetHoldType() const;
void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
void VibrateController(const std::vector<u32>& controller_ids,
void VibrateController(const std::vector<u32>& controllers,
const std::vector<Vibration>& vibrations);
Vibration GetLastVibration() const;
@@ -369,6 +382,7 @@ private:
MotionArray motions;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
// Each controller should have their own styleset changed event
std::array<Kernel::EventPair, 10> styleset_changed_events;
Vibration last_processed_vibration{};
+31 -13
View File
@@ -40,7 +40,8 @@ namespace Service::HID {
// Updating period for each HID device.
// HID is polled every 15ms, this value was derived from
// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
constexpr auto pad_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.6Hz)
constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz)
constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
@@ -79,10 +80,14 @@ IAppletResource::IAppletResource(Core::System& system)
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
UpdateControllers(user_data, ns_late);
});
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
motion_update_event = Core::Timing::CreateEvent(
"HID::MotionPadCallback",
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
UpdateMotion(user_data, ns_late);
});
system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
ReloadInputDevices();
}
@@ -122,6 +127,16 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
}
void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
auto& core_timing = system.CoreTiming();
for (const auto& controller : controllers) {
controller->OnMotionUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
}
core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
}
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
public:
IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") {
@@ -739,8 +754,11 @@ void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
const auto applet_resource_user_id{rp.Pop<u64>()};
const auto mode{rp.Pop<u64>()};
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
applet_resource_user_id, mode);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, mode={}", applet_resource_user_id,
mode);
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetNpadHandheldActivationMode(Controller_NPad::NpadHandheldActivationMode{mode});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -750,11 +768,13 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
applet_resource_user_id);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(
static_cast<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetNpadHandheldActivationMode()));
}
void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
@@ -797,18 +817,18 @@ void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto controller_id{rp.Pop<u32>()};
const auto controller{rp.Pop<u32>()};
const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id,
LOG_DEBUG(Service_HID, "called, controller={}, applet_resource_user_id={}", controller,
applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateController({controller_id}, {vibration_values});
.VibrateController({controller}, {vibration_values});
}
void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
@@ -826,8 +846,6 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
std::memcpy(controller_list.data(), controllers.data(), controllers.size());
std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
[](u32 controller_id) { return controller_id - 3; });
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateController(controller_list, vibration_list);
+2
View File
@@ -65,10 +65,12 @@ private:
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
std::shared_ptr<Kernel::SharedMemory> shared_mem;
std::shared_ptr<Core::Timing::EventType> pad_update_event;
std::shared_ptr<Core::Timing::EventType> motion_update_event;
Core::System& system;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -265,7 +265,7 @@ std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gp
}
}
return {};
return std::nullopt;
}
void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
@@ -286,7 +286,7 @@ std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
return size;
}
return {};
return std::nullopt;
}
} // namespace Service::Nvidia::Devices
@@ -22,6 +22,18 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input, output);
case IoctlCommand::IocSubmit:
return Submit(input, output);
case IoctlCommand::IocGetSyncpoint:
return GetSyncpoint(input, output);
case IoctlCommand::IocGetWaitbase:
return GetWaitbase(input, output);
case IoctlCommand::IocMapBuffer:
return MapBuffer(input, output);
case IoctlCommand::IocMapBufferEx:
return MapBufferEx(input, output);
case IoctlCommand::IocUnmapBufferEx:
return UnmapBufferEx(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
@@ -30,11 +42,67 @@ u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::
u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
}
u32 nvhost_nvdec::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
return 0;
}
u32 nvhost_nvdec::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetSyncpoint params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return 0;
}
u32 nvhost_nvdec::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
return 0;
}
u32 nvhost_nvdec::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
params.address_1);
params.address_1 = 0;
params.address_2 = 0;
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
return 0;
}
u32 nvhost_nvdec::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBufferEx));
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
params.address_1);
params.address_1 = 0;
params.address_2 = 0;
std::memcpy(output.data(), &params, sizeof(IoctlMapBufferEx));
return 0;
}
u32 nvhost_nvdec::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlUnmapBufferEx params{};
std::memcpy(&params, input.data(), sizeof(IoctlUnmapBufferEx));
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
std::memcpy(output.data(), &params, sizeof(IoctlUnmapBufferEx));
return 0;
}
} // namespace Service::Nvidia::Devices
@@ -23,16 +23,66 @@ public:
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
IocSubmit = 0xC0400001,
IocGetSyncpoint = 0xC0080002,
IocGetWaitbase = 0xC0080003,
IocMapBuffer = 0xC01C0009,
IocMapBufferEx = 0xC0A40009,
IocUnmapBufferEx = 0xC0A4000A,
};
struct IoctlSetNvmapFD {
u32_le nvmap_fd;
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
static_assert(sizeof(IoctlSetNvmapFD) == 0x4, "IoctlSetNvmapFD is incorrect size");
struct IoctlSubmit {
INSERT_PADDING_BYTES(0x40); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit has incorrect size");
struct IoctlGetSyncpoint {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
};
static_assert(sizeof(IoctlGetSyncpoint) == 0x08, "IoctlGetSyncpoint has incorrect size");
struct IoctlGetWaitbase {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
};
static_assert(sizeof(IoctlGetWaitbase) == 0x08, "IoctlGetWaitbase has incorrect size");
struct IoctlMapBuffer {
u32 unknown;
u32 address_1;
u32 address_2;
INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
struct IoctlMapBufferEx {
u32 unknown;
u32 address_1;
u32 address_2;
INSERT_PADDING_BYTES(0x98); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlMapBufferEx) == 0xA4, "IoctlMapBufferEx has incorrect size");
struct IoctlUnmapBufferEx {
INSERT_PADDING_BYTES(0xA4); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlUnmapBufferEx) == 0xA4, "IoctlUnmapBufferEx has incorrect size");
u32_le nvmap_fd{};
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices
@@ -22,6 +22,18 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
switch (static_cast<IoctlCommand>(command.raw)) {
case IoctlCommand::IocSetNVMAPfdCommand:
return SetNVMAPfd(input, output);
case IoctlCommand::IocSubmit:
return Submit(input, output);
case IoctlCommand::IocGetSyncpoint:
return GetSyncpoint(input, output);
case IoctlCommand::IocGetWaitbase:
return GetWaitbase(input, output);
case IoctlCommand::IocMapBuffer:
return MapBuffer(input, output);
case IoctlCommand::IocMapBufferEx:
return MapBuffer(input, output);
case IoctlCommand::IocUnmapBufferEx:
return UnmapBufferEx(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
@@ -30,11 +42,71 @@ u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::ve
u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
return 0;
}
u32 nvhost_vic::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
// Workaround for Luigi's Mansion 3, as nvhost_vic is not implemented for asynch GPU
params.command_buffer = {};
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
return 0;
}
u32 nvhost_vic::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetSyncpoint params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
return 0;
}
u32 nvhost_vic::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
return 0;
}
u32 nvhost_vic::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBuffer params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
params.address_1);
params.address_1 = 0;
params.address_2 = 0;
std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
return 0;
}
u32 nvhost_vic::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), sizeof(IoctlMapBufferEx));
LOG_WARNING(Service_NVDRV, "(STUBBED) called with address={:08X}{:08X}", params.address_2,
params.address_1);
params.address_1 = 0;
params.address_2 = 0;
std::memcpy(output.data(), &params, sizeof(IoctlMapBufferEx));
return 0;
}
u32 nvhost_vic::UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlUnmapBufferEx params{};
std::memcpy(&params, input.data(), sizeof(IoctlUnmapBufferEx));
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
std::memcpy(output.data(), &params, sizeof(IoctlUnmapBufferEx));
return 0;
}
} // namespace Service::Nvidia::Devices
@@ -4,6 +4,7 @@
#pragma once
#include <array>
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
@@ -23,6 +24,12 @@ public:
private:
enum class IoctlCommand : u32_le {
IocSetNVMAPfdCommand = 0x40044801,
IocSubmit = 0xC0400001,
IocGetSyncpoint = 0xC0080002,
IocGetWaitbase = 0xC0080003,
IocMapBuffer = 0xC01C0009,
IocMapBufferEx = 0xC03C0009,
IocUnmapBufferEx = 0xC03C000A,
};
struct IoctlSetNvmapFD {
@@ -30,9 +37,65 @@ private:
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
struct IoctlSubmitCommandBuffer {
u32 id;
u32 offset;
u32 count;
};
static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
"IoctlSubmitCommandBuffer is incorrect size");
struct IoctlSubmit {
u32 command_buffer_count;
u32 relocations_count;
u32 syncpt_count;
u32 wait_count;
std::array<IoctlSubmitCommandBuffer, 4> command_buffer;
};
static_assert(sizeof(IoctlSubmit) == 0x40, "IoctlSubmit is incorrect size");
struct IoctlGetSyncpoint {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
};
static_assert(sizeof(IoctlGetSyncpoint) == 0x8, "IoctlGetSyncpoint is incorrect size");
struct IoctlGetWaitbase {
u32 unknown; // seems to be ignored? Nintendo added this
u32 value;
};
static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
struct IoctlMapBuffer {
u32 unknown;
u32 address_1;
u32 address_2;
INSERT_PADDING_BYTES(0x10); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlMapBuffer) == 0x1C, "IoctlMapBuffer is incorrect size");
struct IoctlMapBufferEx {
u32 unknown;
u32 address_1;
u32 address_2;
INSERT_PADDING_BYTES(0x30); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlMapBufferEx) == 0x3C, "IoctlMapBufferEx is incorrect size");
struct IoctlUnmapBufferEx {
INSERT_PADDING_BYTES(0x3C); // TODO(DarkLordZach): RE this structure
};
static_assert(sizeof(IoctlUnmapBufferEx) == 0x3C, "IoctlUnmapBufferEx is incorrect size");
u32_le nvmap_fd{};
u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
u32 Submit(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
u32 UnmapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices
+1 -1
View File
@@ -54,7 +54,7 @@ struct EventInterface {
}
mask = mask >> 1;
}
return {};
return std::nullopt;
}
void SetEventStatus(const u32 event_id, EventState new_status) {
EventState old_status = status[event_id];
+3 -3
View File
@@ -114,7 +114,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
[&](const VI::Display& display) { return display.GetName() == name; });
if (itr == displays.end()) {
return {};
return std::nullopt;
}
return itr->GetID();
@@ -124,7 +124,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
return {};
return std::nullopt;
}
const u64 layer_id = next_layer_id++;
@@ -144,7 +144,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
const auto* const layer = FindLayer(display_id, layer_id);
if (layer == nullptr) {
return {};
return std::nullopt;
}
return layer->GetBufferQueue().GetId();
+17
View File
@@ -72,6 +72,23 @@
namespace Service {
/**
* Creates a function string for logging, complete with the name (or header code, depending
* on what's passed in) the port name, and all the cmd_buff arguments.
*/
[[maybe_unused]] static std::string MakeFunctionString(std::string_view name,
std::string_view port_name,
const u32* cmd_buff) {
// Number of params == bits 0-5 + bits 6-11
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
std::string function_string = fmt::format("function '{}': port={}", name, port_name);
for (int i = 1; i <= num_params; ++i) {
function_string += fmt::format(", cmd_buff[{}]=0x{:X}", i, cmd_buff[i]);
}
return function_string;
}
ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions,
InvokerFn* handler_invoker)
: service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {}
+1 -1
View File
@@ -497,7 +497,7 @@ std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u
return {0, Errno::SUCCESS};
}
std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
if (!descriptor) {
LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd);
pollfd.revents = POLL_NVAL;
+3 -3
View File
@@ -76,16 +76,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::S
bool should_pass_arguments, bool load_into_process,
std::optional<FileSys::PatchManager> pm) {
if (file.GetSize() < sizeof(NSOHeader)) {
return {};
return std::nullopt;
}
NSOHeader nso_header{};
if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
return {};
return std::nullopt;
}
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
return {};
return std::nullopt;
}
// Build program image
+1 -1
View File
@@ -567,7 +567,7 @@ struct Memory::Impl {
* @param page_table The page table to use to perform the mapping.
* @param base The base address to begin mapping at.
* @param size The total size of the range in bytes.
* @param memory The memory to map.
* @param target The target address to begin mapping from.
* @param type The page type to map the memory as.
*/
void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
+125
View File
@@ -15,7 +15,10 @@
#endif
#include "common/logging/log.h"
#include "common/param_package.h"
#include "core/settings.h"
#include "input_common/gcadapter/gc_adapter.h"
#include "input_common/settings.h"
namespace GCAdapter {
@@ -127,6 +130,23 @@ void Adapter::Read() {
adapter_thread_running = false; // error reading from adapter, stop reading.
break;
}
if (Settings::values.vibration_enabled) {
// Modulate vibrations to 8 states
vibration_counter = (vibration_counter + 1) % 8;
const bool p1 = rumble[0] > vibration_counter;
const bool p2 = rumble[1] > vibration_counter;
const bool p3 = rumble[2] > vibration_counter;
const bool p4 = rumble[3] > vibration_counter;
Vibrate(p1, p2, p3, p4);
} else {
// stop all onging vibrations
if (vibration_counter != 8) {
vibration_counter = 8;
Vibrate(false, false, false, false);
}
}
for (std::size_t port = 0; port < pads.size(); ++port) {
pads[port] = GetPadStatus(port, adapter_payload);
if (DeviceConnected(port) && configuring) {
@@ -158,6 +178,8 @@ void Adapter::Setup() {
adapter_controllers_status.fill(ControllerTypes::None);
// Initialize all ports to store axis origin values
get_origin.fill(true);
// Initialize all rumble to no vibrate
rumble.fill(0);
// pointer to list of connected usb devices
libusb_device** devices{};
@@ -292,6 +314,109 @@ void Adapter::Reset() {
}
}
std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
std::vector<Common::ParamPackage> devices;
for (std::size_t port = 0; port < state.size(); ++port) {
if (!DeviceConnected(port)) {
continue;
}
std::string name = fmt::format("Gamecube Controller {}", port);
devices.emplace_back(Common::ParamPackage{
{"class", "gcpad"},
{"display", std::move(name)},
{"port", std::to_string(port)},
});
}
return devices;
}
InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
const Common::ParamPackage& params) const {
// This list is missing ZL/ZR since those are not considered buttons.
// We will add those afterwards
// This list also excludes any button that can't be really mapped
static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
switch_to_gcadapter_button = {
std::pair{Settings::NativeButton::A, PadButton::PAD_BUTTON_A},
{Settings::NativeButton::B, PadButton::PAD_BUTTON_B},
{Settings::NativeButton::X, PadButton::PAD_BUTTON_X},
{Settings::NativeButton::Y, PadButton::PAD_BUTTON_Y},
{Settings::NativeButton::Plus, PadButton::PAD_BUTTON_START},
{Settings::NativeButton::DLeft, PadButton::PAD_BUTTON_LEFT},
{Settings::NativeButton::DUp, PadButton::PAD_BUTTON_UP},
{Settings::NativeButton::DRight, PadButton::PAD_BUTTON_RIGHT},
{Settings::NativeButton::DDown, PadButton::PAD_BUTTON_DOWN},
{Settings::NativeButton::SL, PadButton::PAD_TRIGGER_L},
{Settings::NativeButton::SR, PadButton::PAD_TRIGGER_R},
{Settings::NativeButton::R, PadButton::PAD_TRIGGER_Z},
};
if (!params.Has("port")) {
return {};
}
InputCommon::ButtonMapping mapping{};
for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) {
Common::ParamPackage button_params({{"engine", "gcpad"}});
button_params.Set("port", params.Get("port", 0));
button_params.Set("button", static_cast<int>(gcadapter_button));
mapping.insert_or_assign(switch_button, std::move(button_params));
}
// Add the missing bindings for ZL/ZR
static constexpr std::array<std::pair<Settings::NativeButton::Values, PadAxes>, 2>
switch_to_gcadapter_axis = {
std::pair{Settings::NativeButton::ZL, PadAxes::TriggerLeft},
{Settings::NativeButton::ZR, PadAxes::TriggerRight},
};
for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
Common::ParamPackage button_params({{"engine", "gcpad"}});
button_params.Set("port", params.Get("port", 0));
button_params.Set("button", static_cast<int>(PadButton::PAD_STICK));
button_params.Set("axis", static_cast<int>(gcadapter_axis));
mapping.insert_or_assign(switch_button, std::move(button_params));
}
return mapping;
}
InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
const Common::ParamPackage& params) const {
if (!params.Has("port")) {
return {};
}
InputCommon::AnalogMapping mapping = {};
Common::ParamPackage left_analog_params;
left_analog_params.Set("engine", "gcpad");
left_analog_params.Set("port", params.Get("port", 0));
left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX));
left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY));
mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
Common::ParamPackage right_analog_params;
right_analog_params.Set("engine", "gcpad");
right_analog_params.Set("port", params.Get("port", 0));
right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX));
right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY));
mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
return mapping;
}
void Adapter::Vibrate(bool p1, bool p2, bool p3, bool p4) {
int size = 0;
std::array<u8, 5> payload = {0x11, p1, p2, p3, p4};
const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
sizeof(payload), &size, 16);
if (err) {
LOG_ERROR(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
}
}
bool Adapter::RumblePlay(int port, f32 amplitude) {
const u8 raw_amp = static_cast<u8>(amplitude * 0x8);
rumble[port] = raw_amp;
return false;
}
bool Adapter::DeviceConnected(std::size_t port) const {
return adapter_controllers_status[port] != ControllerTypes::None;
}
+12
View File
@@ -10,6 +10,7 @@
#include <unordered_map>
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
#include "input_common/main.h"
struct libusb_context;
struct libusb_device;
@@ -75,6 +76,10 @@ public:
void BeginConfiguration();
void EndConfiguration();
std::vector<Common::ParamPackage> GetInputDevices() const;
InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
/// Returns true if there is a device connected to port
bool DeviceConnected(std::size_t port) const;
@@ -86,6 +91,8 @@ public:
int GetOriginValue(int port, int axis) const;
bool RumblePlay(int port, f32 amplitude);
private:
GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
@@ -108,6 +115,9 @@ private:
/// For use in initialization, querying devices to find the adapter
void Setup();
// Updates vibration state of all controllers
void Vibrate(bool p1, bool p2, bool p3, bool p4);
libusb_device_handle* usb_adapter_handle = nullptr;
std::thread adapter_input_thread;
@@ -117,10 +127,12 @@ private:
u8 input_endpoint = 0;
u8 output_endpoint = 0;
u8 vibration_counter = 0;
bool configuring = false;
std::array<GCState, 4> state;
std::array<u8, 4> rumble;
std::array<bool, 4> get_origin;
std::array<GCPadStatus, 4> origin_status;
std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
+9 -2
View File
@@ -15,7 +15,7 @@ namespace InputCommon {
class GCButton final : public Input::ButtonDevice {
public:
explicit GCButton(int port_, int button_, const GCAdapter::Adapter* adapter)
explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter)
: port(port_), button(button_), gcadapter(adapter) {}
~GCButton() override;
@@ -27,10 +27,17 @@ public:
return false;
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const f32 amplitude = amp_high + amp_low > 2 ? 1.0f : (amp_high + amp_low) * 0.5f;
const f32 new_amp = pow(amplitude, 0.5f) * (3.0f - 2.0f * pow(amplitude, 0.15f));
return gcadapter->RumblePlay(port, new_amp);
}
private:
const int port;
const int button;
const GCAdapter::Adapter* gcadapter;
GCAdapter::Adapter* gcadapter;
};
class GCAxisButton final : public Input::ButtonDevice {
+10 -1
View File
@@ -22,7 +22,7 @@ namespace InputCommon {
struct InputSubsystem::Impl {
void Initialize() {
auto gcadapter = std::make_shared<GCAdapter::Adapter>();
gcadapter = std::make_shared<GCAdapter::Adapter>();
gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
@@ -82,6 +82,8 @@ struct InputSubsystem::Impl {
#endif
auto udp_devices = udp->GetInputDevices();
devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
auto gcpad_devices = gcadapter->GetInputDevices();
devices.insert(devices.end(), gcpad_devices.begin(), gcpad_devices.end());
return devices;
}
@@ -94,6 +96,9 @@ struct InputSubsystem::Impl {
// TODO consider returning the SDL key codes for the default keybindings
return {};
}
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetAnalogMappingForDevice(params);
}
#ifdef HAVE_SDL2
if (params.Get("class", "") == "sdl") {
return sdl->GetAnalogMappingForDevice(params);
@@ -111,6 +116,9 @@ struct InputSubsystem::Impl {
// TODO consider returning the SDL key codes for the default keybindings
return {};
}
if (params.Get("class", "") == "gcpad") {
return gcadapter->GetButtonMappingForDevice(params);
}
#ifdef HAVE_SDL2
if (params.Get("class", "") == "sdl") {
return sdl->GetButtonMappingForDevice(params);
@@ -141,6 +149,7 @@ struct InputSubsystem::Impl {
std::shared_ptr<UDPMotionFactory> udpmotion;
std::shared_ptr<UDPTouchFactory> udptouch;
std::shared_ptr<CemuhookUDP::Client> udp;
std::shared_ptr<GCAdapter::Adapter> gcadapter;
};
InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
+38 -1
View File
@@ -5,6 +5,7 @@
#include <algorithm>
#include <array>
#include <atomic>
#include <chrono>
#include <cmath>
#include <functional>
#include <mutex>
@@ -78,6 +79,33 @@ public:
return state.axes.at(axis) / (32767.0f * range);
}
bool RumblePlay(f32 amp_low, f32 amp_high, int time) {
const u16 raw_amp_low = static_cast<u16>(amp_low * 0xFFFF);
const u16 raw_amp_high = static_cast<u16>(amp_high * 0xFFFF);
// Lower drastically the number of state changes
if (raw_amp_low >> 11 == last_state_rumble_low >> 11 &&
raw_amp_high >> 11 == last_state_rumble_high >> 11) {
if (raw_amp_low + raw_amp_high != 0 ||
last_state_rumble_low + last_state_rumble_high == 0) {
return false;
}
}
// Don't change state if last vibration was < 20ms
const auto now = std::chrono::system_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(now - last_vibration) <
std::chrono::milliseconds(20)) {
return raw_amp_low + raw_amp_high == 0;
}
last_vibration = now;
last_state_rumble_low = raw_amp_low;
last_state_rumble_high = raw_amp_high;
if (sdl_joystick) {
SDL_JoystickRumble(sdl_joystick.get(), raw_amp_low, raw_amp_high, time);
}
return false;
}
std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
float x = GetAxis(axis_x, range);
float y = GetAxis(axis_y, range);
@@ -139,6 +167,9 @@ private:
} state;
std::string guid;
int port;
u16 last_state_rumble_high;
u16 last_state_rumble_low;
std::chrono::time_point<std::chrono::system_clock> last_vibration;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
@@ -207,7 +238,7 @@ void SDLState::InitJoystick(int joystick_index) {
sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
}
if (!sdl_joystick) {
LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
return;
}
const std::string guid = GetGUID(sdl_joystick);
@@ -303,6 +334,12 @@ public:
return joystick->GetButton(button);
}
bool SetRumblePlay(f32 amp_high, f32 amp_low, f32 freq_high, f32 freq_low) const override {
const f32 new_amp_low = pow(amp_low, 0.5f) * (3.0f - 2.0f * pow(amp_low, 0.15f));
const f32 new_amp_high = pow(amp_high, 0.5f) * (3.0f - 2.0f * pow(amp_high, 0.15f));
return joystick->RumblePlay(new_amp_low, new_amp_high, 250);
}
private:
std::shared_ptr<SDLJoystick> joystick;
int button;
+6 -2
View File
@@ -190,6 +190,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_blit_screen.h
renderer_vulkan/vk_buffer_cache.cpp
renderer_vulkan/vk_buffer_cache.h
renderer_vulkan/vk_command_pool.cpp
renderer_vulkan/vk_command_pool.h
renderer_vulkan/vk_compute_pass.cpp
renderer_vulkan/vk_compute_pass.h
renderer_vulkan/vk_compute_pipeline.cpp
@@ -204,6 +206,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_graphics_pipeline.h
renderer_vulkan/vk_image.cpp
renderer_vulkan/vk_image.h
renderer_vulkan/vk_master_semaphore.cpp
renderer_vulkan/vk_master_semaphore.h
renderer_vulkan/vk_memory_manager.cpp
renderer_vulkan/vk_memory_manager.h
renderer_vulkan/vk_pipeline_cache.cpp
@@ -214,8 +218,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_rasterizer.h
renderer_vulkan/vk_renderpass_cache.cpp
renderer_vulkan/vk_renderpass_cache.h
renderer_vulkan/vk_resource_manager.cpp
renderer_vulkan/vk_resource_manager.h
renderer_vulkan/vk_resource_pool.cpp
renderer_vulkan/vk_resource_pool.h
renderer_vulkan/vk_sampler_cache.cpp
renderer_vulkan/vk_sampler_cache.h
renderer_vulkan/vk_scheduler.cpp
+6 -6
View File
@@ -87,12 +87,12 @@ void Fermi2D::HandleSurfaceCopy() {
const Common::Rectangle<u32> src_rect{src_blit_x1, src_blit_y1, src_blit_x2, src_blit_y2};
const Common::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y, dst_blit_x2,
dst_blit_y2};
Config copy_config;
copy_config.operation = regs.operation;
copy_config.filter = regs.blit_control.filter;
copy_config.src_rect = src_rect;
copy_config.dst_rect = dst_rect;
const Config copy_config{
.operation = regs.operation,
.filter = regs.blit_control.filter,
.src_rect = src_rect,
.dst_rect = dst_rect,
};
if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) {
UNIMPLEMENTED();
}
+2 -2
View File
@@ -145,8 +145,8 @@ public:
} regs{};
struct Config {
Operation operation;
Filter filter;
Operation operation{};
Filter filter{};
Common::Rectangle<u32> src_rect;
Common::Rectangle<u32> dst_rect;
};
+1 -1
View File
@@ -597,7 +597,7 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
// Deferred.
rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
system.GPU().GetTicks());
return {};
return std::nullopt;
default:
LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
static_cast<u32>(regs.query.query_get.select.Value()));
+1 -1
View File
@@ -36,7 +36,7 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method,
}
} else {
// Macro not compiled, check if it's uploaded and if so, compile it
std::optional<u32> mid_method = std::nullopt;
std::optional<u32> mid_method;
const auto macro_code = uploaded_macro_code.find(method);
if (macro_code == uploaded_macro_code.end()) {
for (const auto& [method_base, code] : uploaded_macro_code) {
+3 -3
View File
@@ -58,7 +58,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
for (u64 offset{}; offset < size; offset += page_size) {
if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
return {};
return std::nullopt;
}
}
@@ -135,13 +135,13 @@ std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size
}
}
return {};
return std::nullopt;
}
std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
const auto page_entry{GetPageEntry(gpu_addr)};
if (!page_entry.IsValid()) {
return {};
return std::nullopt;
}
return page_entry.ToAddress() + (gpu_addr & page_mask);
+1 -5
View File
@@ -91,8 +91,7 @@ private:
std::shared_ptr<HostCounter> last;
};
template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter,
class QueryPool>
template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
class QueryCacheBase {
public:
explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
@@ -206,9 +205,6 @@ public:
committed_flushes.pop_front();
}
protected:
std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
private:
/// Flushes a memory range to guest memory and removes it from the cache.
void FlushAndRemoveRegion(VAddr addr, std::size_t size) {
-5
View File
@@ -46,11 +46,6 @@ public:
/// Finalize rendering the guest frame and draw into the presentation texture
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
/// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
/// specific implementation)
/// Returns true if a frame was drawn
virtual bool TryPresent(int timeout_ms) = 0;
// Getter/setter functions:
// ------------------------
@@ -32,10 +32,8 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
QueryCache::QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
Tegra::MemoryManager& gpu_memory)
: VideoCommon::QueryCacheBase<
QueryCache, CachedQuery, CounterStream, HostCounter,
std::vector<OGLQuery>>{static_cast<VideoCore::RasterizerInterface&>(rasterizer),
maxwell3d, gpu_memory},
: VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter>(
rasterizer, maxwell3d, gpu_memory),
gl_rasterizer{rasterizer} {}
QueryCache::~QueryCache() = default;
@@ -91,6 +89,8 @@ u64 HostCounter::BlockingQuery() const {
CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr)
: VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {}
CachedQuery::~CachedQuery() = default;
CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept
: VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {}
@@ -26,8 +26,8 @@ class RasterizerOpenGL;
using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream,
HostCounter, std::vector<OGLQuery>> {
class QueryCache final
: public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
public:
explicit QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
Tegra::MemoryManager& gpu_memory);
@@ -41,6 +41,7 @@ public:
private:
RasterizerOpenGL& gl_rasterizer;
std::array<std::vector<OGLQuery>, VideoCore::NumQueryTypes> query_pools;
};
class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> {
@@ -63,10 +64,12 @@ class CachedQuery final : public VideoCommon::CachedQueryBase<HostCounter> {
public:
explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr,
u8* host_ptr);
CachedQuery(CachedQuery&& rhs) noexcept;
CachedQuery(const CachedQuery&) = delete;
~CachedQuery() override;
CachedQuery(CachedQuery&& rhs) noexcept;
CachedQuery& operator=(CachedQuery&& rhs) noexcept;
CachedQuery(const CachedQuery&) = delete;
CachedQuery& operator=(const CachedQuery&) = delete;
void Flush() override;
@@ -813,7 +813,7 @@ private:
const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
const auto it = transform_feedback.find(location);
if (it == transform_feedback.end()) {
return {};
return std::nullopt;
}
return it->second.components;
}
@@ -1295,21 +1295,21 @@ private:
switch (element) {
case 0:
UNIMPLEMENTED();
return {};
return std::nullopt;
case 1:
if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
return {};
return std::nullopt;
}
return {{"gl_Layer", Type::Int}};
case 2:
if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
return {};
return std::nullopt;
}
return {{"gl_ViewportIndex", Type::Int}};
case 3:
return {{"gl_PointSize", Type::Float}};
}
return {};
return std::nullopt;
case Attribute::Index::FrontColor:
return {{"gl_FrontColor"s + GetSwizzle(element), Type::Float}};
case Attribute::Index::FrontSecondaryColor:
@@ -1332,7 +1332,7 @@ private:
Type::Float}};
}
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
return {};
return std::nullopt;
}
}
@@ -32,20 +32,6 @@ namespace OpenGL {
namespace {
constexpr std::size_t SWAP_CHAIN_SIZE = 3;
struct Frame {
u32 width{}; /// Width of the frame (to detect resize)
u32 height{}; /// Height of the frame
bool color_reloaded{}; /// Texture attachment was recreated (ie: resized)
OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
GLsync render_fence{}; /// Fence created on the render thread
GLsync present_fence{}; /// Fence created on the presentation thread
bool is_srgb{}; /// Framebuffer is sRGB or RGB
};
constexpr GLint PositionLocation = 0;
constexpr GLint TexCoordLocation = 1;
constexpr GLint ModelViewMatrixLocation = 0;
@@ -58,24 +44,6 @@ struct ScreenRectVertex {
std::array<GLfloat, 2> tex_coord;
};
/// Returns true if any debug tool is attached
bool HasDebugTool() {
const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
if (nsight) {
return true;
}
GLint num_extensions;
glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) {
const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
if (!std::strcmp(name, "GL_EXT_debug_tool")) {
return true;
}
}
return false;
}
/**
* Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
* corner and (width, height) on the lower-bottom.
@@ -159,135 +127,15 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
} // Anonymous namespace
/**
* For smooth Vsync rendering, we want to always present the latest frame that the core generates,
* but also make sure that rendering happens at the pace that the frontend dictates. This is a
* helper class that the renderer uses to sync frames between the render thread and the presentation
* thread
*/
class FrameMailbox {
public:
std::mutex swap_chain_lock;
std::condition_variable present_cv;
std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
std::queue<Frame*> free_queue;
std::deque<Frame*> present_queue;
Frame* previous_frame{};
FrameMailbox() {
for (auto& frame : swap_chain) {
free_queue.push(&frame);
}
}
~FrameMailbox() {
// lock the mutex and clear out the present and free_queues and notify any people who are
// blocked to prevent deadlock on shutdown
std::scoped_lock lock{swap_chain_lock};
std::queue<Frame*>().swap(free_queue);
present_queue.clear();
present_cv.notify_all();
}
void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
frame->present.Release();
frame->present.Create();
GLint previous_draw_fbo{};
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
frame->color.handle);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
frame->color_reloaded = false;
}
void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
// Recreate the color texture attachment
frame->color.Release();
frame->color.Create();
const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
// Recreate the FBO for the render target
frame->render.Release();
frame->render.Create();
glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
frame->color.handle);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
}
frame->width = width;
frame->height = height;
frame->color_reloaded = true;
}
Frame* GetRenderFrame() {
std::unique_lock lock{swap_chain_lock};
// If theres no free frames, we will reuse the oldest render frame
if (free_queue.empty()) {
auto frame = present_queue.back();
present_queue.pop_back();
return frame;
}
Frame* frame = free_queue.front();
free_queue.pop();
return frame;
}
void ReleaseRenderFrame(Frame* frame) {
std::unique_lock lock{swap_chain_lock};
present_queue.push_front(frame);
present_cv.notify_one();
}
Frame* TryGetPresentFrame(int timeout_ms) {
std::unique_lock lock{swap_chain_lock};
// wait for new entries in the present_queue
present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
[&] { return !present_queue.empty(); });
if (present_queue.empty()) {
// timed out waiting for a frame to draw so return the previous frame
return previous_frame;
}
// free the previous frame and add it back to the free queue
if (previous_frame) {
free_queue.push(previous_frame);
}
// the newest entries are pushed to the front of the queue
Frame* frame = present_queue.front();
present_queue.pop_front();
// remove all old entries from the present queue and move them back to the free_queue
for (auto f : present_queue) {
free_queue.push(f);
}
present_queue.clear();
previous_frame = frame;
return frame;
}
};
RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
Core::Frontend::EmuWindow& emu_window_,
Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
std::unique_ptr<Core::Frontend::GraphicsContext> context)
: RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_},
emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device},
has_debug_tool{HasDebugTool()} {}
emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device} {}
RendererOpenGL::~RendererOpenGL() = default;
MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
if (!framebuffer) {
return;
@@ -296,79 +144,34 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
PrepareRendertarget(framebuffer);
RenderScreenshot();
Frame* frame;
{
MICROPROFILE_SCOPE(OpenGL_WaitPresent);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
DrawScreen(emu_window.GetFramebufferLayout());
frame = frame_mailbox->GetRenderFrame();
++m_current_frame;
// Clean up sync objects before drawing
// INTEL driver workaround. We can't delete the previous render sync object until we are
// sure that the presentation is done
if (frame->present_fence) {
glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
}
// delete the draw fence if the frame wasn't presented
if (frame->render_fence) {
glDeleteSync(frame->render_fence);
frame->render_fence = 0;
}
// wait for the presentation to be done
if (frame->present_fence) {
glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
glDeleteSync(frame->present_fence);
frame->present_fence = 0;
}
}
{
MICROPROFILE_SCOPE(OpenGL_RenderFrame);
const auto& layout = render_window.GetFramebufferLayout();
// Recreate the frame if the size of the window has changed
if (layout.width != frame->width || layout.height != frame->height ||
screen_info.display_srgb != frame->is_srgb) {
LOG_DEBUG(Render_OpenGL, "Reloading render frame");
frame->is_srgb = screen_info.display_srgb;
frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle);
DrawScreen(layout);
// Create a fence for the frontend to wait on and swap this frame to OffTex
frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
frame_mailbox->ReleaseRenderFrame(frame);
m_current_frame++;
rasterizer->TickFrame();
}
rasterizer->TickFrame();
render_window.PollEvents();
if (has_debug_tool) {
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
Present(0);
context->SwapBuffers();
}
context->SwapBuffers();
}
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
if (framebuffer) {
// If framebuffer is provided, reload it from memory to a texture
if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
screen_info.texture.pixel_format != framebuffer->pixel_format ||
gl_framebuffer_data.empty()) {
// Reallocate texture if the framebuffer size has changed.
// This is expected to not happen very often and hence should not be a
// performance problem.
ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
}
// Load the framebuffer from memory, draw it to the screen, and swap buffers
LoadFBToScreenInfo(*framebuffer);
if (!framebuffer) {
return;
}
// If framebuffer is provided, reload it from memory to a texture
if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
screen_info.texture.pixel_format != framebuffer->pixel_format ||
gl_framebuffer_data.empty()) {
// Reallocate texture if the framebuffer size has changed.
// This is expected to not happen very often and hence should not be a
// performance problem.
ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
}
// Load the framebuffer from memory, draw it to the screen, and swap buffers
LoadFBToScreenInfo(*framebuffer);
}
void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -418,8 +221,6 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
}
void RendererOpenGL::InitOpenGLObjects() {
frame_mailbox = std::make_unique<FrameMailbox>();
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue(), 0.0f);
@@ -647,51 +448,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
program_manager.RestoreGuestPipeline();
}
bool RendererOpenGL::TryPresent(int timeout_ms) {
if (has_debug_tool) {
LOG_DEBUG(Render_OpenGL,
"Skipping presentation because we are presenting on the main context");
return false;
}
return Present(timeout_ms);
}
bool RendererOpenGL::Present(int timeout_ms) {
const auto& layout = render_window.GetFramebufferLayout();
auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
if (!frame) {
LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
return false;
}
// Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
// readback since we won't be doing any blending
glClear(GL_COLOR_BUFFER_BIT);
// Recreate the presentation FBO if the color attachment was changed
if (frame->color_reloaded) {
LOG_DEBUG(Render_OpenGL, "Reloading present frame");
frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
}
glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
// INTEL workaround.
// Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
// it on the emulation thread without too much penalty
// glDeleteSync(frame.render_sync);
// frame.render_sync = 0;
glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
// Insert fence for the main thread to block on
frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
glFlush();
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
return true;
}
void RendererOpenGL::RenderScreenshot() {
if (!renderer_settings.screenshot_requested) {
return;
@@ -706,7 +462,7 @@ void RendererOpenGL::RenderScreenshot() {
screenshot_framebuffer.Create();
glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
@@ -55,14 +55,6 @@ struct ScreenInfo {
TextureInfo texture;
};
struct PresentationTexture {
u32 width = 0;
u32 height = 0;
OGLTexture texture;
};
class FrameMailbox;
class RendererOpenGL final : public VideoCore::RendererBase {
public:
explicit RendererOpenGL(Core::TelemetrySession& telemetry_session,
@@ -74,7 +66,6 @@ public:
bool Init() override;
void ShutDown() override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
bool TryPresent(int timeout_ms) override;
private:
/// Initializes the OpenGL state and creates persistent objects.
@@ -102,8 +93,6 @@ private:
void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
bool Present(int timeout_ms);
Core::TelemetrySession& telemetry_session;
Core::Frontend::EmuWindow& emu_window;
Core::Memory::Memory& cpu_memory;
@@ -134,11 +123,6 @@ private:
/// Used for transforming the framebuffer orientation
Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
Common::Rectangle<int> framebuffer_crop_rect;
/// Frame presentation mailbox
std::unique_ptr<FrameMailbox> frame_mailbox;
bool has_debug_tool = false;
};
} // namespace OpenGL
@@ -25,9 +25,9 @@
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -56,7 +56,7 @@ VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT type,
const VkDebugUtilsMessengerCallbackDataEXT* data,
[[maybe_unused]] void* user_data) {
const char* message{data->pMessage};
const char* const message{data->pMessage};
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
LOG_CRITICAL(Render_Vulkan, "{}", message);
@@ -269,11 +269,11 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
scheduler->WaitWorker();
swapchain->AcquireNextImage();
const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated);
const VkSemaphore render_semaphore = blit_screen->Draw(*framebuffer, use_accelerated);
scheduler->Flush(false, render_semaphore);
scheduler->Flush(render_semaphore);
if (swapchain->Present(render_semaphore, fence)) {
if (swapchain->Present(render_semaphore)) {
blit_screen->Recreate();
}
@@ -283,11 +283,6 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
render_window.PollEvents();
}
bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
// TODO (bunnei): ImplementMe
return true;
}
bool RendererVulkan::Init() {
library = OpenVulkanLibrary();
instance = CreateInstance(library, dld, render_window.GetWindowInfo().type,
@@ -300,23 +295,21 @@ bool RendererVulkan::Init() {
memory_manager = std::make_unique<VKMemoryManager>(*device);
resource_manager = std::make_unique<VKResourceManager>(*device);
const auto& framebuffer = render_window.GetFramebufferLayout();
swapchain = std::make_unique<VKSwapchain>(*surface, *device);
swapchain->Create(framebuffer.width, framebuffer.height, false);
state_tracker = std::make_unique<StateTracker>(gpu);
scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker);
scheduler = std::make_unique<VKScheduler>(*device, *state_tracker);
rasterizer = std::make_unique<RasterizerVulkan>(
render_window, gpu, gpu.MemoryManager(), cpu_memory, screen_info, *device,
*resource_manager, *memory_manager, *state_tracker, *scheduler);
const auto& framebuffer = render_window.GetFramebufferLayout();
swapchain = std::make_unique<VKSwapchain>(*surface, *device, *scheduler);
swapchain->Create(framebuffer.width, framebuffer.height, false);
blit_screen = std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
*resource_manager, *memory_manager, *swapchain,
*scheduler, screen_info);
rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(),
cpu_memory, screen_info, *device,
*memory_manager, *state_tracker, *scheduler);
blit_screen =
std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
*memory_manager, *swapchain, *scheduler, screen_info);
return true;
}
@@ -334,7 +327,6 @@ void RendererVulkan::ShutDown() {
scheduler.reset();
swapchain.reset();
memory_manager.reset();
resource_manager.reset();
device.reset();
}
@@ -30,9 +30,7 @@ namespace Vulkan {
class StateTracker;
class VKBlitScreen;
class VKDevice;
class VKFence;
class VKMemoryManager;
class VKResourceManager;
class VKSwapchain;
class VKScheduler;
class VKImage;
@@ -55,7 +53,6 @@ public:
bool Init() override;
void ShutDown() override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
bool TryPresent(int timeout_ms) override;
static std::vector<std::string> EnumerateDevices();
@@ -82,11 +79,10 @@ private:
vk::DebugCallback debug_callback;
std::unique_ptr<VKDevice> device;
std::unique_ptr<VKSwapchain> swapchain;
std::unique_ptr<VKMemoryManager> memory_manager;
std::unique_ptr<VKResourceManager> resource_manager;
std::unique_ptr<StateTracker> state_tracker;
std::unique_ptr<VKScheduler> scheduler;
std::unique_ptr<VKSwapchain> swapchain;
std::unique_ptr<VKBlitScreen> blit_screen;
};
@@ -12,11 +12,9 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/math_util.h"
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/morton.h"
#include "video_core/rasterizer_interface.h"
@@ -24,8 +22,8 @@
#include "video_core/renderer_vulkan/vk_blit_screen.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_image.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_shader_util.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -213,16 +211,12 @@ struct VKBlitScreen::BufferData {
VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
Core::Frontend::EmuWindow& render_window_,
VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_,
VKResourceManager& resource_manager_, VKMemoryManager& memory_manager_,
VKSwapchain& swapchain_, VKScheduler& scheduler_,
const VKScreenInfo& screen_info_)
: cpu_memory{cpu_memory_}, render_window{render_window_},
rasterizer{rasterizer_}, device{device_}, resource_manager{resource_manager_},
memory_manager{memory_manager_}, swapchain{swapchain_}, scheduler{scheduler_},
image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
watches.resize(image_count);
std::generate(watches.begin(), watches.end(),
[]() { return std::make_unique<VKFenceWatch>(); });
VKMemoryManager& memory_manager_, VKSwapchain& swapchain_,
VKScheduler& scheduler_, const VKScreenInfo& screen_info_)
: cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_},
device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_},
scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
resource_ticks.resize(image_count);
CreateStaticResources();
CreateDynamicResources();
@@ -234,15 +228,16 @@ void VKBlitScreen::Recreate() {
CreateDynamicResources();
}
std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
bool use_accelerated) {
VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated) {
RefreshResources(framebuffer);
// Finish any pending renderpass
scheduler.RequestOutsideRenderPassOperationContext();
const std::size_t image_index = swapchain.GetImageIndex();
watches[image_index]->Watch(scheduler.GetFence());
scheduler.Wait(resource_ticks[image_index]);
resource_ticks[image_index] = scheduler.CurrentTick();
VKImage* blit_image = use_accelerated ? screen_info.image : raw_images[image_index].get();
@@ -345,7 +340,7 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
cmdbuf.EndRenderPass();
});
return {scheduler.GetFence(), *semaphores[image_index]};
return *semaphores[image_index];
}
void VKBlitScreen::CreateStaticResources() {
@@ -713,7 +708,7 @@ void VKBlitScreen::CreateFramebuffers() {
void VKBlitScreen::ReleaseRawImages() {
for (std::size_t i = 0; i < raw_images.size(); ++i) {
watches[i]->Wait();
scheduler.Wait(resource_ticks.at(i));
}
raw_images.clear();
raw_buffer_commits.clear();
@@ -5,10 +5,8 @@
#pragma once
#include <memory>
#include <tuple>
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Core {
@@ -34,9 +32,9 @@ class RasterizerInterface;
namespace Vulkan {
struct ScreenInfo;
class RasterizerVulkan;
class VKDevice;
class VKFence;
class VKImage;
class VKScheduler;
class VKSwapchain;
@@ -46,15 +44,14 @@ public:
explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
Core::Frontend::EmuWindow& render_window,
VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
VKSwapchain& swapchain, VKScheduler& scheduler,
const VKScreenInfo& screen_info);
VKMemoryManager& memory_manager, VKSwapchain& swapchain,
VKScheduler& scheduler, const VKScreenInfo& screen_info);
~VKBlitScreen();
void Recreate();
std::tuple<VKFence&, VkSemaphore> Draw(const Tegra::FramebufferConfig& framebuffer,
bool use_accelerated);
[[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer,
bool use_accelerated);
private:
struct BufferData;
@@ -90,7 +87,6 @@ private:
Core::Frontend::EmuWindow& render_window;
VideoCore::RasterizerInterface& rasterizer;
const VKDevice& device;
VKResourceManager& resource_manager;
VKMemoryManager& memory_manager;
VKSwapchain& swapchain;
VKScheduler& scheduler;
@@ -111,7 +107,7 @@ private:
vk::Buffer buffer;
VKMemoryCommit buffer_commit;
std::vector<std::unique_ptr<VKFenceWatch>> watches;
std::vector<u64> resource_ticks;
std::vector<vk::Semaphore> semaphores;
std::vector<std::unique_ptr<VKImage>> raw_images;
@@ -0,0 +1,46 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstddef>
#include "video_core/renderer_vulkan/vk_command_pool.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
constexpr size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
struct CommandPool::Pool {
vk::CommandPool handle;
vk::CommandBuffers cmdbufs;
};
CommandPool::CommandPool(MasterSemaphore& master_semaphore, const VKDevice& device)
: ResourcePool(master_semaphore, COMMAND_BUFFER_POOL_SIZE), device{device} {}
CommandPool::~CommandPool() = default;
void CommandPool::Allocate(size_t begin, size_t end) {
// Command buffers are going to be commited, recorded, executed every single usage cycle.
// They are also going to be reseted when commited.
Pool& pool = pools.emplace_back();
pool.handle = device.GetLogical().CreateCommandPool({
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags =
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = device.GetGraphicsFamily(),
});
pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
}
VkCommandBuffer CommandPool::Commit() {
const size_t index = CommitResource();
const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
return pools[pool_index].cmdbufs[sub_index];
}
} // namespace Vulkan
@@ -0,0 +1,34 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <vector>
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
class MasterSemaphore;
class VKDevice;
class CommandPool final : public ResourcePool {
public:
explicit CommandPool(MasterSemaphore& master_semaphore, const VKDevice& device);
~CommandPool() override;
void Allocate(size_t begin, size_t end) override;
VkCommandBuffer Commit();
private:
struct Pool;
const VKDevice& device;
std::vector<Pool> pools;
};
} // namespace Vulkan
@@ -112,7 +112,8 @@ constexpr u8 quad_array[] = {
0xf9, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4e, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00,
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
VkDescriptorSetLayoutBinding BuildQuadArrayPassDescriptorSetLayoutBinding() {
return {
@@ -218,7 +219,8 @@ constexpr u8 uint8_pass[] = {
0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00,
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
// Quad indexed SPIR-V module. Generated from the "shaders/" directory.
constexpr u8 QUAD_INDEXED_SPV[] = {
@@ -341,7 +343,8 @@ constexpr u8 QUAD_INDEXED_SPV[] = {
0xf9, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00,
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
};
std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBindings() {
return {{
@@ -448,12 +451,12 @@ VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descripto
VKComputePass::~VKComputePass() = default;
VkDescriptorSet VKComputePass::CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue,
VKFence& fence) {
VkDescriptorSet VKComputePass::CommitDescriptorSet(
VKUpdateDescriptorQueue& update_descriptor_queue) {
if (!descriptor_template) {
return nullptr;
}
const auto set = descriptor_allocator->Commit(fence);
const VkDescriptorSet set = descriptor_allocator->Commit();
update_descriptor_queue.Send(*descriptor_template, set);
return set;
}
@@ -477,7 +480,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence());
const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
@@ -520,13 +523,13 @@ Uint8Pass::~Uint8Pass() = default;
std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
u64 src_offset) {
const auto staging_size = static_cast<u32>(num_vertices * sizeof(u16));
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence());
const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
@@ -589,7 +592,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence());
const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
@@ -15,7 +15,6 @@
namespace Vulkan {
class VKDevice;
class VKFence;
class VKScheduler;
class VKStagingBufferPool;
class VKUpdateDescriptorQueue;
@@ -30,8 +29,7 @@ public:
~VKComputePass();
protected:
VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue,
VKFence& fence);
VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue);
vk::DescriptorUpdateTemplateKHR descriptor_template;
vk::PipelineLayout layout;
@@ -32,7 +32,7 @@ VkDescriptorSet VKComputePipeline::CommitDescriptorSet() {
if (!descriptor_template) {
return {};
}
const auto set = descriptor_allocator.Commit(scheduler.GetFence());
const VkDescriptorSet set = descriptor_allocator.Commit();
update_descriptor_queue.Send(*descriptor_template, set);
return set;
}
@@ -7,7 +7,8 @@
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
@@ -15,14 +16,15 @@ namespace Vulkan {
// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines.
constexpr std::size_t SETS_GROW_RATE = 0x20;
DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool,
VkDescriptorSetLayout layout)
: VKFencedPool{SETS_GROW_RATE}, descriptor_pool{descriptor_pool}, layout{layout} {}
DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool_,
VkDescriptorSetLayout layout_)
: ResourcePool(descriptor_pool_.master_semaphore, SETS_GROW_RATE),
descriptor_pool{descriptor_pool_}, layout{layout_} {}
DescriptorAllocator::~DescriptorAllocator() = default;
VkDescriptorSet DescriptorAllocator::Commit(VKFence& fence) {
const std::size_t index = CommitResource(fence);
VkDescriptorSet DescriptorAllocator::Commit() {
const std::size_t index = CommitResource();
return descriptors_allocations[index / SETS_GROW_RATE][index % SETS_GROW_RATE];
}
@@ -30,8 +32,9 @@ void DescriptorAllocator::Allocate(std::size_t begin, std::size_t end) {
descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin));
}
VKDescriptorPool::VKDescriptorPool(const VKDevice& device)
: device{device}, active_pool{AllocateNewPool()} {}
VKDescriptorPool::VKDescriptorPool(const VKDevice& device_, VKScheduler& scheduler)
: device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{
AllocateNewPool()} {}
VKDescriptorPool::~VKDescriptorPool() = default;
@@ -6,21 +6,24 @@
#include <vector>
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
class VKDevice;
class VKDescriptorPool;
class VKScheduler;
class DescriptorAllocator final : public VKFencedPool {
class DescriptorAllocator final : public ResourcePool {
public:
explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, VkDescriptorSetLayout layout);
~DescriptorAllocator() override;
DescriptorAllocator& operator=(const DescriptorAllocator&) = delete;
DescriptorAllocator(const DescriptorAllocator&) = delete;
VkDescriptorSet Commit(VKFence& fence);
VkDescriptorSet Commit();
protected:
void Allocate(std::size_t begin, std::size_t end) override;
@@ -36,15 +39,19 @@ class VKDescriptorPool final {
friend DescriptorAllocator;
public:
explicit VKDescriptorPool(const VKDevice& device);
explicit VKDescriptorPool(const VKDevice& device, VKScheduler& scheduler);
~VKDescriptorPool();
VKDescriptorPool(const VKDescriptorPool&) = delete;
VKDescriptorPool& operator=(const VKDescriptorPool&) = delete;
private:
vk::DescriptorPool* AllocateNewPool();
vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count);
const VKDevice& device;
MasterSemaphore& master_semaphore;
std::vector<vk::DescriptorPool> pools;
vk::DescriptorPool* active_pool;
+8 -8
View File
@@ -42,6 +42,7 @@ constexpr std::array REQUIRED_EXTENSIONS{
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
@@ -250,6 +251,13 @@ bool VKDevice::Create() {
.inheritedQueries = false,
};
VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR,
.pNext = nullptr,
.timelineSemaphore = true,
};
SetNext(next, timeline_semaphore);
VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
.pNext = nullptr,
@@ -380,14 +388,6 @@ bool VKDevice::Create() {
CollectTelemetryParameters();
if (ext_extended_dynamic_state && driver_id == VK_DRIVER_ID_AMD_PROPRIETARY_KHR) {
// AMD's proprietary driver supports VK_EXT_extended_dynamic_state but the <stride> field
// seems to be bugged. Blacklisting it for now.
LOG_WARNING(Render_Vulkan,
"Blacklisting AMD proprietary from VK_EXT_extended_dynamic_state");
ext_extended_dynamic_state = false;
}
graphics_queue = logical.GetQueue(graphics_family);
present_queue = logical.GetQueue(present_family);
@@ -29,8 +29,8 @@ void InnerFence::Queue() {
}
ASSERT(!event);
event = device.GetLogical().CreateNewEvent();
ticks = scheduler.Ticks();
event = device.GetLogical().CreateEvent();
ticks = scheduler.CurrentTick();
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
@@ -52,7 +52,7 @@ void InnerFence::Wait() {
}
ASSERT(event);
if (ticks >= scheduler.Ticks()) {
if (ticks >= scheduler.CurrentTick()) {
scheduler.Flush();
}
while (!IsEventSignalled()) {
@@ -93,7 +93,7 @@ VkDescriptorSet VKGraphicsPipeline::CommitDescriptorSet() {
if (!descriptor_template) {
return {};
}
const auto set = descriptor_allocator.Commit(scheduler.GetFence());
const VkDescriptorSet set = descriptor_allocator.Commit();
update_descriptor_queue.Send(*descriptor_template, set);
return set;
}
@@ -0,0 +1,56 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <atomic>
#include <chrono>
#include "core/settings.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
using namespace std::chrono_literals;
MasterSemaphore::MasterSemaphore(const VKDevice& device) {
static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR,
.pNext = nullptr,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR,
.initialValue = 0,
};
static constexpr VkSemaphoreCreateInfo semaphore_ci{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &semaphore_type_ci,
.flags = 0,
};
semaphore = device.GetLogical().CreateSemaphore(semaphore_ci);
if (!Settings::values.renderer_debug) {
return;
}
// Validation layers have a bug where they fail to track resource usage when using timeline
// semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have
// a separate thread waiting for each timeline semaphore value.
debug_thread = std::thread([this] {
u64 counter = 0;
while (!shutdown) {
if (semaphore.Wait(counter, 10'000'000)) {
++counter;
}
}
});
}
MasterSemaphore::~MasterSemaphore() {
shutdown = true;
// This thread might not be started
if (debug_thread.joinable()) {
debug_thread.join();
}
}
} // namespace Vulkan
@@ -0,0 +1,70 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <atomic>
#include <thread>
#include "common/common_types.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
class VKDevice;
class MasterSemaphore {
public:
explicit MasterSemaphore(const VKDevice& device);
~MasterSemaphore();
/// Returns the current logical tick.
[[nodiscard]] u64 CurrentTick() const noexcept {
return current_tick;
}
/// Returns the timeline semaphore handle.
[[nodiscard]] VkSemaphore Handle() const noexcept {
return *semaphore;
}
/// Returns true when a tick has been hit by the GPU.
[[nodiscard]] bool IsFree(u64 tick) {
return gpu_tick >= tick;
}
/// Advance to the logical tick.
void NextTick() noexcept {
++current_tick;
}
/// Refresh the known GPU tick
void Refresh() {
gpu_tick = semaphore.GetCounter();
}
/// Waits for a tick to be hit on the GPU
void Wait(u64 tick) {
// No need to wait if the GPU is ahead of the tick
if (IsFree(tick)) {
return;
}
// Update the GPU tick and try again
Refresh();
if (IsFree(tick)) {
return;
}
// If none of the above is hit, fallback to a regular wait
semaphore.Wait(tick);
}
private:
vk::Semaphore semaphore; ///< Timeline semaphore.
std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
std::atomic<u64> current_tick{1}; ///< Current logical tick.
std::atomic<bool> shutdown{false}; ///< True when the object is being destroyed.
std::thread debug_thread; ///< Debug thread to workaround validation layer bugs.
};
} // namespace Vulkan
@@ -38,7 +38,6 @@ class RasterizerVulkan;
class VKComputePipeline;
class VKDescriptorPool;
class VKDevice;
class VKFence;
class VKScheduler;
class VKUpdateDescriptorQueue;
@@ -9,35 +9,33 @@
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
using VideoCore::QueryType;
namespace {
constexpr std::array QUERY_TARGETS = {VK_QUERY_TYPE_OCCLUSION};
constexpr VkQueryType GetTarget(VideoCore::QueryType type) {
constexpr VkQueryType GetTarget(QueryType type) {
return QUERY_TARGETS[static_cast<std::size_t>(type)];
}
} // Anonymous namespace
QueryPool::QueryPool() : VKFencedPool{GROW_STEP} {}
QueryPool::QueryPool(const VKDevice& device_, VKScheduler& scheduler, QueryType type_)
: ResourcePool{scheduler.GetMasterSemaphore(), GROW_STEP}, device{device_}, type{type_} {}
QueryPool::~QueryPool() = default;
void QueryPool::Initialize(const VKDevice& device_, VideoCore::QueryType type_) {
device = &device_;
type = type_;
}
std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) {
std::pair<VkQueryPool, u32> QueryPool::Commit() {
std::size_t index;
do {
index = CommitResource(fence);
index = CommitResource();
} while (usage[index]);
usage[index] = true;
@@ -47,7 +45,7 @@ std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) {
void QueryPool::Allocate(std::size_t begin, std::size_t end) {
usage.resize(end);
pools.push_back(device->GetLogical().CreateQueryPool({
pools.push_back(device.GetLogical().CreateQueryPool({
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
@@ -71,28 +69,36 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
const VKDevice& device, VKScheduler& scheduler)
: VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
QueryPool>{rasterizer, maxwell3d, gpu_memory},
device{device}, scheduler{scheduler} {
for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) {
query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i));
: VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream,
HostCounter>{rasterizer, maxwell3d, gpu_memory},
device{device}, scheduler{scheduler}, query_pools{
QueryPool{device, scheduler,
QueryType::SamplesPassed},
} {}
VKQueryCache::~VKQueryCache() {
// TODO(Rodrigo): This is a hack to destroy all HostCounter instances before the base class
// destructor is called. The query cache should be redesigned to have a proper ownership model
// instead of using shared pointers.
for (size_t query_type = 0; query_type < VideoCore::NumQueryTypes; ++query_type) {
auto& stream = Stream(static_cast<QueryType>(query_type));
stream.Update(false);
stream.Reset();
}
}
VKQueryCache::~VKQueryCache() = default;
std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(VideoCore::QueryType type) {
return query_pools[static_cast<std::size_t>(type)].Commit(scheduler.GetFence());
std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(QueryType type) {
return query_pools[static_cast<std::size_t>(type)].Commit();
}
void VKQueryCache::Reserve(VideoCore::QueryType type, std::pair<VkQueryPool, u32> query) {
void VKQueryCache::Reserve(QueryType type, std::pair<VkQueryPool, u32> query) {
query_pools[static_cast<std::size_t>(type)].Reserve(query);
}
HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency,
VideoCore::QueryType type)
QueryType type)
: VideoCommon::HostCounterBase<VKQueryCache, HostCounter>{std::move(dependency)}, cache{cache},
type{type}, query{cache.AllocateQuery(type)}, ticks{cache.Scheduler().Ticks()} {
type{type}, query{cache.AllocateQuery(type)}, tick{cache.Scheduler().CurrentTick()} {
const vk::Device* logical = &cache.Device().GetLogical();
cache.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
logical->ResetQueryPoolEXT(query.first, query.second, 1);
@@ -110,7 +116,7 @@ void HostCounter::EndQuery() {
}
u64 HostCounter::BlockingQuery() const {
if (ticks >= cache.Scheduler().Ticks()) {
if (tick >= cache.Scheduler().CurrentTick()) {
cache.Scheduler().Flush();
}
u64 data;
@@ -11,7 +11,7 @@
#include "common/common_types.h"
#include "video_core/query_cache.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace VideoCore {
@@ -28,14 +28,12 @@ class VKScheduler;
using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>;
class QueryPool final : public VKFencedPool {
class QueryPool final : public ResourcePool {
public:
explicit QueryPool();
explicit QueryPool(const VKDevice& device, VKScheduler& scheduler, VideoCore::QueryType type);
~QueryPool() override;
void Initialize(const VKDevice& device, VideoCore::QueryType type);
std::pair<VkQueryPool, u32> Commit(VKFence& fence);
std::pair<VkQueryPool, u32> Commit();
void Reserve(std::pair<VkQueryPool, u32> query);
@@ -45,16 +43,15 @@ protected:
private:
static constexpr std::size_t GROW_STEP = 512;
const VKDevice* device = nullptr;
VideoCore::QueryType type = {};
const VKDevice& device;
const VideoCore::QueryType type;
std::vector<vk::QueryPool> pools;
std::vector<bool> usage;
};
class VKQueryCache final
: public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
QueryPool> {
: public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter> {
public:
explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
@@ -76,6 +73,7 @@ public:
private:
const VKDevice& device;
VKScheduler& scheduler;
std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
};
class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> {
@@ -92,7 +90,7 @@ private:
VKQueryCache& cache;
const VideoCore::QueryType type;
const std::pair<VkQueryPool, u32> query;
const u64 ticks;
const u64 tick;
};
class CachedQuery : public VideoCommon::CachedQueryBase<HostCounter> {
@@ -31,7 +31,6 @@
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_sampler_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -384,27 +383,25 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const {
RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_,
Tegra::MemoryManager& gpu_memory_,
Core::Memory::Memory& cpu_memory, VKScreenInfo& screen_info_,
const VKDevice& device_, VKResourceManager& resource_manager_,
VKMemoryManager& memory_manager_, StateTracker& state_tracker_,
VKScheduler& scheduler_)
const VKDevice& device_, VKMemoryManager& memory_manager_,
StateTracker& state_tracker_, VKScheduler& scheduler_)
: RasterizerAccelerated(cpu_memory), gpu(gpu_), gpu_memory(gpu_memory_),
maxwell3d(gpu.Maxwell3D()), kepler_compute(gpu.KeplerCompute()), screen_info(screen_info_),
device(device_), resource_manager(resource_manager_), memory_manager(memory_manager_),
state_tracker(state_tracker_), scheduler(scheduler_),
staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
update_descriptor_queue(device, scheduler), renderpass_cache(device),
device(device_), memory_manager(memory_manager_), state_tracker(state_tracker_),
scheduler(scheduler_), staging_pool(device, memory_manager, scheduler),
descriptor_pool(device, scheduler_), update_descriptor_queue(device, scheduler),
renderpass_cache(device),
quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
texture_cache(*this, maxwell3d, gpu_memory, device, resource_manager, memory_manager,
scheduler, staging_pool),
texture_cache(*this, maxwell3d, gpu_memory, device, memory_manager, scheduler, staging_pool),
pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
descriptor_pool, update_descriptor_queue, renderpass_cache),
buffer_cache(*this, gpu_memory, cpu_memory, device, memory_manager, scheduler, staging_pool),
sampler_cache(device), query_cache(*this, maxwell3d, gpu_memory, device, scheduler),
fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device,
scheduler),
wfi_event(device.GetLogical().CreateNewEvent()), async_shaders(emu_window) {
wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window) {
scheduler.SetQueryCache(query_cache);
if (device.UseAsynchronousShaders()) {
async_shaders.AllocateWorkers();
@@ -25,7 +25,6 @@
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_sampler_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -109,8 +108,8 @@ public:
explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
VKScreenInfo& screen_info, const VKDevice& device,
VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
StateTracker& state_tracker, VKScheduler& scheduler);
VKMemoryManager& memory_manager, StateTracker& state_tracker,
VKScheduler& scheduler);
~RasterizerVulkan() override;
void Draw(bool is_indexed, bool is_instanced) override;
@@ -286,7 +285,6 @@ private:
VKScreenInfo& screen_info;
const VKDevice& device;
VKResourceManager& resource_manager;
VKMemoryManager& memory_manager;
StateTracker& state_tracker;
VKScheduler& scheduler;
@@ -1,311 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <algorithm>
#include <optional>
#include "common/assert.h"
#include "common/logging/log.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
namespace {
// TODO(Rodrigo): Fine tune these numbers.
constexpr std::size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
constexpr std::size_t FENCES_GROW_STEP = 0x40;
constexpr VkFenceCreateInfo BuildFenceCreateInfo() {
return {
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
};
}
} // Anonymous namespace
class CommandBufferPool final : public VKFencedPool {
public:
explicit CommandBufferPool(const VKDevice& device)
: VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {}
void Allocate(std::size_t begin, std::size_t end) override {
// Command buffers are going to be commited, recorded, executed every single usage cycle.
// They are also going to be reseted when commited.
Pool& pool = pools.emplace_back();
pool.handle = device.GetLogical().CreateCommandPool({
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
.queueFamilyIndex = device.GetGraphicsFamily(),
});
pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
}
VkCommandBuffer Commit(VKFence& fence) {
const std::size_t index = CommitResource(fence);
const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
return pools[pool_index].cmdbufs[sub_index];
}
private:
struct Pool {
vk::CommandPool handle;
vk::CommandBuffers cmdbufs;
};
const VKDevice& device;
std::vector<Pool> pools;
};
VKResource::VKResource() = default;
VKResource::~VKResource() = default;
VKFence::VKFence(const VKDevice& device)
: device{device}, handle{device.GetLogical().CreateFence(BuildFenceCreateInfo())} {}
VKFence::~VKFence() = default;
void VKFence::Wait() {
switch (const VkResult result = handle.Wait()) {
case VK_SUCCESS:
return;
case VK_ERROR_DEVICE_LOST:
device.ReportLoss();
[[fallthrough]];
default:
throw vk::Exception(result);
}
}
void VKFence::Release() {
ASSERT(is_owned);
is_owned = false;
}
void VKFence::Commit() {
is_owned = true;
is_used = true;
}
bool VKFence::Tick(bool gpu_wait, bool owner_wait) {
if (!is_used) {
// If a fence is not used it's always free.
return true;
}
if (is_owned && !owner_wait) {
// The fence is still being owned (Release has not been called) and ownership wait has
// not been asked.
return false;
}
if (gpu_wait) {
// Wait for the fence if it has been requested.
(void)handle.Wait();
} else {
if (handle.GetStatus() != VK_SUCCESS) {
// Vulkan fence is not ready, not much it can do here
return false;
}
}
// Broadcast resources their free state.
for (auto* resource : protected_resources) {
resource->OnFenceRemoval(this);
}
protected_resources.clear();
// Prepare fence for reusage.
handle.Reset();
is_used = false;
return true;
}
void VKFence::Protect(VKResource* resource) {
protected_resources.push_back(resource);
}
void VKFence::Unprotect(VKResource* resource) {
const auto it = std::find(protected_resources.begin(), protected_resources.end(), resource);
ASSERT(it != protected_resources.end());
resource->OnFenceRemoval(this);
protected_resources.erase(it);
}
void VKFence::RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept {
std::replace(std::begin(protected_resources), std::end(protected_resources), old_resource,
new_resource);
}
VKFenceWatch::VKFenceWatch() = default;
VKFenceWatch::VKFenceWatch(VKFence& initial_fence) {
Watch(initial_fence);
}
VKFenceWatch::VKFenceWatch(VKFenceWatch&& rhs) noexcept {
fence = std::exchange(rhs.fence, nullptr);
if (fence) {
fence->RedirectProtection(&rhs, this);
}
}
VKFenceWatch& VKFenceWatch::operator=(VKFenceWatch&& rhs) noexcept {
fence = std::exchange(rhs.fence, nullptr);
if (fence) {
fence->RedirectProtection(&rhs, this);
}
return *this;
}
VKFenceWatch::~VKFenceWatch() {
if (fence) {
fence->Unprotect(this);
}
}
void VKFenceWatch::Wait() {
if (fence == nullptr) {
return;
}
fence->Wait();
fence->Unprotect(this);
}
void VKFenceWatch::Watch(VKFence& new_fence) {
Wait();
fence = &new_fence;
fence->Protect(this);
}
bool VKFenceWatch::TryWatch(VKFence& new_fence) {
if (fence) {
return false;
}
fence = &new_fence;
fence->Protect(this);
return true;
}
void VKFenceWatch::OnFenceRemoval(VKFence* signaling_fence) {
ASSERT_MSG(signaling_fence == fence, "Removing the wrong fence");
fence = nullptr;
}
VKFencedPool::VKFencedPool(std::size_t grow_step) : grow_step{grow_step} {}
VKFencedPool::~VKFencedPool() = default;
std::size_t VKFencedPool::CommitResource(VKFence& fence) {
const auto Search = [&](std::size_t begin, std::size_t end) -> std::optional<std::size_t> {
for (std::size_t iterator = begin; iterator < end; ++iterator) {
if (watches[iterator]->TryWatch(fence)) {
// The resource is now being watched, a free resource was successfully found.
return iterator;
}
}
return {};
};
// Try to find a free resource from the hinted position to the end.
auto found = Search(free_iterator, watches.size());
if (!found) {
// Search from beginning to the hinted position.
found = Search(0, free_iterator);
if (!found) {
// Both searches failed, the pool is full; handle it.
const std::size_t free_resource = ManageOverflow();
// Watch will wait for the resource to be free.
watches[free_resource]->Watch(fence);
found = free_resource;
}
}
// Free iterator is hinted to the resource after the one that's been commited.
free_iterator = (*found + 1) % watches.size();
return *found;
}
std::size_t VKFencedPool::ManageOverflow() {
const std::size_t old_capacity = watches.size();
Grow();
// The last entry is guaranted to be free, since it's the first element of the freshly
// allocated resources.
return old_capacity;
}
void VKFencedPool::Grow() {
const std::size_t old_capacity = watches.size();
watches.resize(old_capacity + grow_step);
std::generate(watches.begin() + old_capacity, watches.end(),
[]() { return std::make_unique<VKFenceWatch>(); });
Allocate(old_capacity, old_capacity + grow_step);
}
VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} {
GrowFences(FENCES_GROW_STEP);
command_buffer_pool = std::make_unique<CommandBufferPool>(device);
}
VKResourceManager::~VKResourceManager() = default;
VKFence& VKResourceManager::CommitFence() {
const auto StepFences = [&](bool gpu_wait, bool owner_wait) -> VKFence* {
const auto Tick = [=](auto& fence) { return fence->Tick(gpu_wait, owner_wait); };
const auto hinted = fences.begin() + fences_iterator;
auto it = std::find_if(hinted, fences.end(), Tick);
if (it == fences.end()) {
it = std::find_if(fences.begin(), hinted, Tick);
if (it == hinted) {
return nullptr;
}
}
fences_iterator = std::distance(fences.begin(), it) + 1;
if (fences_iterator >= fences.size())
fences_iterator = 0;
auto& fence = *it;
fence->Commit();
return fence.get();
};
VKFence* found_fence = StepFences(false, false);
if (!found_fence) {
// Try again, this time waiting.
found_fence = StepFences(true, false);
if (!found_fence) {
// Allocate new fences and try again.
LOG_INFO(Render_Vulkan, "Allocating new fences {} -> {}", fences.size(),
fences.size() + FENCES_GROW_STEP);
GrowFences(FENCES_GROW_STEP);
found_fence = StepFences(true, false);
ASSERT(found_fence != nullptr);
}
}
return *found_fence;
}
VkCommandBuffer VKResourceManager::CommitCommandBuffer(VKFence& fence) {
return command_buffer_pool->Commit(fence);
}
void VKResourceManager::GrowFences(std::size_t new_fences_count) {
const std::size_t previous_size = fences.size();
fences.resize(previous_size + new_fences_count);
std::generate(fences.begin() + previous_size, fences.end(),
[this] { return std::make_unique<VKFence>(device); });
}
} // namespace Vulkan
@@ -1,196 +0,0 @@
// Copyright 2018 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <cstddef>
#include <memory>
#include <vector>
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
class VKDevice;
class VKFence;
class VKResourceManager;
class CommandBufferPool;
/// Interface for a Vulkan resource
class VKResource {
public:
explicit VKResource();
virtual ~VKResource();
/**
* Signals the object that an owning fence has been signaled.
* @param signaling_fence Fence that signals its usage end.
*/
virtual void OnFenceRemoval(VKFence* signaling_fence) = 0;
};
/**
* Fences take ownership of objects, protecting them from GPU-side or driver-side concurrent access.
* They must be commited from the resource manager. Their usage flow is: commit the fence from the
* resource manager, protect resources with it and use them, send the fence to an execution queue
* and Wait for it if needed and then call Release. Used resources will automatically be signaled
* when they are free to be reused.
* @brief Protects resources for concurrent usage and signals its release.
*/
class VKFence {
friend class VKResourceManager;
public:
explicit VKFence(const VKDevice& device);
~VKFence();
/**
* Waits for the fence to be signaled.
* @warning You must have ownership of the fence and it has to be previously sent to a queue to
* call this function.
*/
void Wait();
/**
* Releases ownership of the fence. Pass after it has been sent to an execution queue.
* Unmanaged usage of the fence after the call will result in undefined behavior because it may
* be being used for something else.
*/
void Release();
/// Protects a resource with this fence.
void Protect(VKResource* resource);
/// Removes protection for a resource.
void Unprotect(VKResource* resource);
/// Redirects one protected resource to a new address.
void RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept;
/// Retreives the fence.
operator VkFence() const {
return *handle;
}
private:
/// Take ownership of the fence.
void Commit();
/**
* Updates the fence status.
* @warning Waiting for the owner might soft lock the execution.
* @param gpu_wait Wait for the fence to be signaled by the driver.
* @param owner_wait Wait for the owner to signal its freedom.
* @returns True if the fence is free. Waiting for gpu and owner will always return true.
*/
bool Tick(bool gpu_wait, bool owner_wait);
const VKDevice& device; ///< Device handler
vk::Fence handle; ///< Vulkan fence
std::vector<VKResource*> protected_resources; ///< List of resources protected by this fence
bool is_owned = false; ///< The fence has been commited but not released yet.
bool is_used = false; ///< The fence has been commited but it has not been checked to be free.
};
/**
* A fence watch is used to keep track of the usage of a fence and protect a resource or set of
* resources without having to inherit VKResource from their handlers.
*/
class VKFenceWatch final : public VKResource {
public:
explicit VKFenceWatch();
VKFenceWatch(VKFence& initial_fence);
VKFenceWatch(VKFenceWatch&&) noexcept;
VKFenceWatch(const VKFenceWatch&) = delete;
~VKFenceWatch() override;
VKFenceWatch& operator=(VKFenceWatch&&) noexcept;
/// Waits for the fence to be released.
void Wait();
/**
* Waits for a previous fence and watches a new one.
* @param new_fence New fence to wait to.
*/
void Watch(VKFence& new_fence);
/**
* Checks if it's currently being watched and starts watching it if it's available.
* @returns True if a watch has started, false if it's being watched.
*/
bool TryWatch(VKFence& new_fence);
void OnFenceRemoval(VKFence* signaling_fence) override;
/**
* Do not use it paired with Watch. Use TryWatch instead.
* Returns true when the watch is free.
*/
bool IsUsed() const {
return fence != nullptr;
}
private:
VKFence* fence{}; ///< Fence watching this resource. nullptr when the watch is free.
};
/**
* Handles a pool of resources protected by fences. Manages resource overflow allocating more
* resources.
*/
class VKFencedPool {
public:
explicit VKFencedPool(std::size_t grow_step);
virtual ~VKFencedPool();
protected:
/**
* Commits a free resource and protects it with a fence. It may allocate new resources.
* @param fence Fence that protects the commited resource.
* @returns Index of the resource commited.
*/
std::size_t CommitResource(VKFence& fence);
/// Called when a chunk of resources have to be allocated.
virtual void Allocate(std::size_t begin, std::size_t end) = 0;
private:
/// Manages pool overflow allocating new resources.
std::size_t ManageOverflow();
/// Allocates a new page of resources.
void Grow();
std::size_t grow_step = 0; ///< Number of new resources created after an overflow
std::size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Set of watched resources
};
/**
* The resource manager handles all resources that can be protected with a fence avoiding
* driver-side or GPU-side concurrent usage. Usage is documented in VKFence.
*/
class VKResourceManager final {
public:
explicit VKResourceManager(const VKDevice& device);
~VKResourceManager();
/// Commits a fence. It has to be sent to a queue and released.
VKFence& CommitFence();
/// Commits an unused command buffer and protects it with a fence.
VkCommandBuffer CommitCommandBuffer(VKFence& fence);
private:
/// Allocates new fences.
void GrowFences(std::size_t new_fences_count);
const VKDevice& device; ///< Device handler.
std::size_t fences_iterator = 0; ///< Index where a free fence is likely to be found.
std::vector<std::unique_ptr<VKFence>> fences; ///< Pool of fences.
std::unique_ptr<CommandBufferPool> command_buffer_pool; ///< Pool of command buffers.
};
} // namespace Vulkan
@@ -0,0 +1,63 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <optional>
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_resource_pool.h"
namespace Vulkan {
ResourcePool::ResourcePool(MasterSemaphore& master_semaphore_, size_t grow_step_)
: master_semaphore{master_semaphore_}, grow_step{grow_step_} {}
ResourcePool::~ResourcePool() = default;
size_t ResourcePool::CommitResource() {
// Refresh semaphore to query updated results
master_semaphore.Refresh();
const auto search = [this](size_t begin, size_t end) -> std::optional<size_t> {
for (size_t iterator = begin; iterator < end; ++iterator) {
if (master_semaphore.IsFree(ticks[iterator])) {
ticks[iterator] = master_semaphore.CurrentTick();
return iterator;
}
}
return {};
};
// Try to find a free resource from the hinted position to the end.
auto found = search(free_iterator, ticks.size());
if (!found) {
// Search from beginning to the hinted position.
found = search(0, free_iterator);
if (!found) {
// Both searches failed, the pool is full; handle it.
const size_t free_resource = ManageOverflow();
ticks[free_resource] = master_semaphore.CurrentTick();
found = free_resource;
}
}
// Free iterator is hinted to the resource after the one that's been commited.
free_iterator = (*found + 1) % ticks.size();
return *found;
}
size_t ResourcePool::ManageOverflow() {
const size_t old_capacity = ticks.size();
Grow();
// The last entry is guaranted to be free, since it's the first element of the freshly
// allocated resources.
return old_capacity;
}
void ResourcePool::Grow() {
const size_t old_capacity = ticks.size();
ticks.resize(old_capacity + grow_step);
Allocate(old_capacity, old_capacity + grow_step);
}
} // namespace Vulkan
@@ -0,0 +1,43 @@
// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <vector>
#include "common/common_types.h"
namespace Vulkan {
class MasterSemaphore;
/**
* Handles a pool of resources protected by fences. Manages resource overflow allocating more
* resources.
*/
class ResourcePool {
public:
explicit ResourcePool(MasterSemaphore& master_semaphore, size_t grow_step);
virtual ~ResourcePool();
protected:
size_t CommitResource();
/// Called when a chunk of resources have to be allocated.
virtual void Allocate(size_t begin, size_t end) = 0;
private:
/// Manages pool overflow allocating new resources.
size_t ManageOverflow();
/// Allocates a new page of resources.
void Grow();
MasterSemaphore& master_semaphore;
size_t grow_step = 0; ///< Number of new resources created after an overflow
size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
std::vector<u64> ticks; ///< Ticks for each resource
};
} // namespace Vulkan
+50 -27
View File
@@ -10,9 +10,10 @@
#include "common/microprofile.h"
#include "common/thread.h"
#include "video_core/renderer_vulkan/vk_command_pool.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
#include "video_core/renderer_vulkan/wrapper.h"
@@ -35,10 +36,10 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
last = nullptr;
}
VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
StateTracker& state_tracker)
: device{device}, resource_manager{resource_manager}, state_tracker{state_tracker},
next_fence{&resource_manager.CommitFence()} {
VKScheduler::VKScheduler(const VKDevice& device_, StateTracker& state_tracker_)
: device{device_}, state_tracker{state_tracker_},
master_semaphore{std::make_unique<MasterSemaphore>(device)},
command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
AcquireNewChunk();
AllocateNewContext();
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
@@ -50,20 +51,27 @@ VKScheduler::~VKScheduler() {
worker_thread.join();
}
void VKScheduler::Flush(bool release_fence, VkSemaphore semaphore) {
u64 VKScheduler::CurrentTick() const noexcept {
return master_semaphore->CurrentTick();
}
bool VKScheduler::IsFree(u64 tick) const noexcept {
return master_semaphore->IsFree(tick);
}
void VKScheduler::Wait(u64 tick) {
master_semaphore->Wait(tick);
}
void VKScheduler::Flush(VkSemaphore semaphore) {
SubmitExecution(semaphore);
if (release_fence) {
current_fence->Release();
}
AllocateNewContext();
}
void VKScheduler::Finish(bool release_fence, VkSemaphore semaphore) {
void VKScheduler::Finish(VkSemaphore semaphore) {
const u64 presubmit_tick = CurrentTick();
SubmitExecution(semaphore);
current_fence->Wait();
if (release_fence) {
current_fence->Release();
}
Wait(presubmit_tick);
AllocateNewContext();
}
@@ -160,18 +168,38 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
current_cmdbuf.End();
const VkSemaphore timeline_semaphore = master_semaphore->Handle();
const u32 num_signal_semaphores = semaphore ? 2U : 1U;
const u64 signal_value = master_semaphore->CurrentTick();
const u64 wait_value = signal_value - 1;
const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
master_semaphore->NextTick();
const std::array signal_values{signal_value, u64(0)};
const std::array signal_semaphores{timeline_semaphore, semaphore};
const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
.pNext = nullptr,
.waitSemaphoreValueCount = 1,
.pWaitSemaphoreValues = &wait_value,
.signalSemaphoreValueCount = num_signal_semaphores,
.pSignalSemaphoreValues = signal_values.data(),
};
const VkSubmitInfo submit_info{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.pNext = nullptr,
.waitSemaphoreCount = 0,
.pWaitSemaphores = nullptr,
.pWaitDstStageMask = nullptr,
.pNext = &timeline_si,
.waitSemaphoreCount = 1,
.pWaitSemaphores = &timeline_semaphore,
.pWaitDstStageMask = &wait_stage_mask,
.commandBufferCount = 1,
.pCommandBuffers = current_cmdbuf.address(),
.signalSemaphoreCount = semaphore ? 1U : 0U,
.pSignalSemaphores = &semaphore,
.signalSemaphoreCount = num_signal_semaphores,
.pSignalSemaphores = signal_semaphores.data(),
};
switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) {
switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
case VK_SUCCESS:
break;
case VK_ERROR_DEVICE_LOST:
@@ -183,14 +211,9 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
}
void VKScheduler::AllocateNewContext() {
++ticks;
std::unique_lock lock{mutex};
current_fence = next_fence;
next_fence = &resource_manager.CommitFence();
current_cmdbuf = vk::CommandBuffer(resource_manager.CommitCommandBuffer(*current_fence),
device.GetDispatchLoader());
current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
current_cmdbuf.Begin({
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
+28 -42
View File
@@ -16,42 +16,33 @@
namespace Vulkan {
class CommandPool;
class MasterSemaphore;
class StateTracker;
class VKDevice;
class VKFence;
class VKQueryCache;
class VKResourceManager;
class VKFenceView {
public:
VKFenceView() = default;
VKFenceView(VKFence* const& fence) : fence{fence} {}
VKFence* operator->() const noexcept {
return fence;
}
operator VKFence&() const noexcept {
return *fence;
}
private:
VKFence* const& fence;
};
/// The scheduler abstracts command buffer and fence management with an interface that's able to do
/// OpenGL-like operations on Vulkan command buffers.
class VKScheduler {
public:
explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
StateTracker& state_tracker);
explicit VKScheduler(const VKDevice& device, StateTracker& state_tracker);
~VKScheduler();
/// Returns the current command buffer tick.
[[nodiscard]] u64 CurrentTick() const noexcept;
/// Returns true when a tick has been triggered by the GPU.
[[nodiscard]] bool IsFree(u64 tick) const noexcept;
/// Waits for the given tick to trigger on the GPU.
void Wait(u64 tick);
/// Sends the current execution context to the GPU.
void Flush(bool release_fence = true, VkSemaphore semaphore = nullptr);
void Flush(VkSemaphore semaphore = nullptr);
/// Sends the current execution context to the GPU and waits for it to complete.
void Finish(bool release_fence = true, VkSemaphore semaphore = nullptr);
void Finish(VkSemaphore semaphore = nullptr);
/// Waits for the worker thread to finish executing everything. After this function returns it's
/// safe to touch worker resources.
@@ -86,14 +77,9 @@ public:
(void)chunk->Record(command);
}
/// Gets a reference to the current fence.
VKFenceView GetFence() const {
return current_fence;
}
/// Returns the current command buffer tick.
u64 Ticks() const {
return ticks;
/// Returns the master timeline semaphore.
[[nodiscard]] MasterSemaphore& GetMasterSemaphore() const noexcept {
return *master_semaphore;
}
private:
@@ -171,6 +157,13 @@ private:
std::array<u8, 0x8000> data{};
};
struct State {
VkRenderPass renderpass = nullptr;
VkFramebuffer framebuffer = nullptr;
VkExtent2D render_area = {0, 0};
VkPipeline graphics_pipeline = nullptr;
};
void WorkerThread();
void SubmitExecution(VkSemaphore semaphore);
@@ -186,30 +179,23 @@ private:
void AcquireNewChunk();
const VKDevice& device;
VKResourceManager& resource_manager;
StateTracker& state_tracker;
std::unique_ptr<MasterSemaphore> master_semaphore;
std::unique_ptr<CommandPool> command_pool;
VKQueryCache* query_cache = nullptr;
vk::CommandBuffer current_cmdbuf;
VKFence* current_fence = nullptr;
VKFence* next_fence = nullptr;
struct State {
VkRenderPass renderpass = nullptr;
VkFramebuffer framebuffer = nullptr;
VkExtent2D render_area = {0, 0};
VkPipeline graphics_pipeline = nullptr;
} state;
std::unique_ptr<CommandChunk> chunk;
std::thread worker_thread;
State state;
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue;
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve;
std::mutex mutex;
std::condition_variable cv;
std::atomic<u64> ticks = 0;
bool quit = false;
};
@@ -10,36 +10,18 @@
#include "common/bit_util.h"
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence,
u64 last_epoch)
: buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {}
VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_)
: buffer{std::move(buffer_)} {}
VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept {
buffer = std::move(rhs.buffer);
watch = std::move(rhs.watch);
last_epoch = rhs.last_epoch;
}
VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default;
VKStagingBufferPool::StagingBuffer& VKStagingBufferPool::StagingBuffer::operator=(
StagingBuffer&& rhs) noexcept {
buffer = std::move(rhs.buffer);
watch = std::move(rhs.watch);
last_epoch = rhs.last_epoch;
return *this;
}
VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
VKScheduler& scheduler)
: device{device}, memory_manager{memory_manager}, scheduler{scheduler} {}
VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device_, VKMemoryManager& memory_manager_,
VKScheduler& scheduler_)
: device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {}
VKStagingBufferPool::~VKStagingBufferPool() = default;
@@ -51,7 +33,6 @@ VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visib
}
void VKStagingBufferPool::TickFrame() {
++epoch;
current_delete_level = (current_delete_level + 1) % NumLevels;
ReleaseCache(true);
@@ -59,11 +40,12 @@ void VKStagingBufferPool::TickFrame() {
}
VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
if (entry.watch.TryWatch(scheduler.GetFence())) {
entry.last_epoch = epoch;
return &*entry.buffer;
for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
if (!scheduler.IsFree(entry.tick)) {
continue;
}
entry.tick = scheduler.CurrentTick();
return &*entry.buffer;
}
return nullptr;
}
@@ -86,8 +68,10 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v
});
buffer->commit = memory_manager.Commit(buffer->handle, host_visible);
auto& entries = GetCache(host_visible)[log2].entries;
return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer;
std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries;
StagingBuffer& entry = entries.emplace_back(std::move(buffer));
entry.tick = scheduler.CurrentTick();
return *entry.buffer;
}
VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
@@ -109,9 +93,8 @@ u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t lo
auto& entries = staging.entries;
const std::size_t old_size = entries.size();
const auto is_deleteable = [this](const auto& entry) {
static constexpr u64 epochs_to_destroy = 180;
return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed();
const auto is_deleteable = [this](const StagingBuffer& entry) {
return scheduler.IsFree(entry.tick);
};
const std::size_t begin_offset = staging.delete_index;
const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
@@ -10,13 +10,11 @@
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
class VKDevice;
class VKFenceWatch;
class VKScheduler;
struct VKBuffer final {
@@ -36,16 +34,10 @@ public:
private:
struct StagingBuffer final {
explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch);
StagingBuffer(StagingBuffer&& rhs) noexcept;
StagingBuffer(const StagingBuffer&) = delete;
~StagingBuffer();
StagingBuffer& operator=(StagingBuffer&& rhs) noexcept;
explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer);
std::unique_ptr<VKBuffer> buffer;
VKFenceWatch watch;
u64 last_epoch = 0;
u64 tick = 0;
};
struct StagingBuffers final {
@@ -73,8 +65,6 @@ private:
StagingBuffersCache host_staging_buffers;
StagingBuffersCache device_staging_buffers;
u64 epoch = 0;
std::size_t current_delete_level = 0;
};

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