Compare commits

..

1 Commits

Author SHA1 Message Date
lizzie 99d285b43b [vk] bump shader cache (v18)
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-06-27 16:28:47 +02:00
126 changed files with 847 additions and 2648 deletions
-5
View File
@@ -59,11 +59,6 @@ endif()
if (PLATFORM_PS4 OR PLATFORM_MANAGARM) if (PLATFORM_PS4 OR PLATFORM_MANAGARM)
# Doesn't support VA-API, don't go thru the embarrassment of trying to enable it # Doesn't support VA-API, don't go thru the embarrassment of trying to enable it
list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi) list(APPEND FFmpeg_HWACCEL_FLAGS --disable-vaapi)
elseif (ANDROID)
list(APPEND FFmpeg_HWACCEL_FLAGS
--enable-mediacodec
--enable-jni
)
elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID) elseif (UNIX AND NOT DEFINED FFmpeg_IS_CROSS_COMPILING AND NOT ANDROID)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBVA libva) pkg_check_modules(LIBVA libva)
@@ -31,7 +31,6 @@ enum class BooleanSetting(override val key: String) : AbstractBooleanSetting {
RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"), RENDERER_REACTIVE_FLUSHING("use_reactive_flushing"),
ENABLE_BUFFER_HISTORY("enable_buffer_history"), ENABLE_BUFFER_HISTORY("enable_buffer_history"),
USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"), USE_OPTIMIZED_VERTEX_BUFFERS("use_optimized_vertex_buffers"),
ENABLE_GPU_BUFFER_READBACK("enable_gpu_buffer_readback"),
SYNC_MEMORY_OPERATIONS("sync_memory_operations"), SYNC_MEMORY_OPERATIONS("sync_memory_operations"),
BUFFER_REORDER_DISABLE("disable_buffer_reorder"), BUFFER_REORDER_DISABLE("disable_buffer_reorder"),
RENDERER_DEBUG("debug"), RENDERER_DEBUG("debug"),
@@ -806,13 +806,6 @@ abstract class SettingsItem(
descriptionId = R.string.enable_buffer_history_description descriptionId = R.string.enable_buffer_history_description
) )
) )
put(
SwitchSetting(
BooleanSetting.ENABLE_GPU_BUFFER_READBACK,
titleId = R.string.enable_gpu_buffer_readback,
descriptionId = R.string.enable_gpu_buffer_readback_description
)
)
put( put(
SwitchSetting( SwitchSetting(
BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS, BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS,
@@ -292,7 +292,6 @@ class SettingsFragmentPresenter(
add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key) add(BooleanSetting.RENDERER_FORCE_MAX_CLOCK.key)
add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key) add(BooleanSetting.RENDERER_REACTIVE_FLUSHING.key)
add(BooleanSetting.ENABLE_BUFFER_HISTORY.key) add(BooleanSetting.ENABLE_BUFFER_HISTORY.key)
add(BooleanSetting.ENABLE_GPU_BUFFER_READBACK.key)
add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key) add(BooleanSetting.USE_OPTIMIZED_VERTEX_BUFFERS.key)
add(HeaderSetting(R.string.hacks)) add(HeaderSetting(R.string.hacks))
+1 -1
View File
@@ -27,7 +27,7 @@ if (ARCHITECTURE_arm64)
target_link_libraries(yuzu-android PRIVATE adrenotools) target_link_libraries(yuzu-android PRIVATE adrenotools)
endif() endif()
target_link_libraries(yuzu-android PRIVATE ${FFmpeg_LIBRARIES} OpenSSL::SSL cpp-jwt::cpp-jwt) target_link_libraries(yuzu-android PRIVATE OpenSSL::SSL cpp-jwt::cpp-jwt)
if (ENABLE_UPDATE_CHECKER) if (ENABLE_UPDATE_CHECKER)
target_compile_definitions(yuzu-android PUBLIC ENABLE_UPDATE_CHECKER) target_compile_definitions(yuzu-android PUBLIC ENABLE_UPDATE_CHECKER)
endif() endif()
-11
View File
@@ -36,10 +36,6 @@
#include <frontend_common/content_manager.h> #include <frontend_common/content_manager.h>
#include <jni.h> #include <jni.h>
extern "C" {
#include <libavcodec/jni.h>
}
#include "common/android/multiplayer/multiplayer.h" #include "common/android/multiplayer/multiplayer.h"
#include "common/android/android_common.h" #include "common/android/android_common.h"
#include "common/android/id_cache.h" #include "common/android/id_cache.h"
@@ -684,13 +680,6 @@ const char* fallback_cpu_detection() {
} // namespace } // namespace
extern "C" {
jint InitFFmpegOnLoad(JavaVM* vm) {
av_jni_set_java_vm(vm, nullptr);
return 0;
}
}
extern "C" { extern "C" {
void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance, void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceChanged(JNIEnv* env, jobject instance,
@@ -503,8 +503,6 @@
<string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string> <string name="renderer_reactive_flushing_description">Improves rendering accuracy in some games at the cost of performance.</string>
<string name="enable_buffer_history">Enable buffer history</string> <string name="enable_buffer_history">Enable buffer history</string>
<string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string> <string name="enable_buffer_history_description">Enables access to previous buffer states. This option may improve rendering quality and performance consistency in some games.</string>
<string name="enable_gpu_buffer_readback">Enable GPU Buffer Readback</string>
<string name="enable_gpu_buffer_readback_description">Preserves GPU-modified buffer data by reading it back before uploads. Some games require this to render certain effects properly. May cause issues if the hardware cannot handle the additional workload.</string>
<string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string> <string name="use_optimized_vertex_buffers">Optimized Vertex Buffers</string>
<string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string> <string name="use_optimized_vertex_buffers_description">Enables optimized vertex buffer binding for improved performance. Requires Mesa 26.0+ Turnip drivers/ QCOM drivers. Will crash on older Turnip drivers (25.3 and below).</string>
-3
View File
@@ -427,11 +427,8 @@ namespace Common::Android {
extern "C" { extern "C" {
#endif #endif
jint InitFFmpegOnLoad(JavaVM *vm);
jint JNI_OnLoad(JavaVM *vm, void *reserved) { jint JNI_OnLoad(JavaVM *vm, void *reserved) {
s_java_vm = vm; s_java_vm = vm;
InitFFmpegOnLoad(vm);
JNIEnv *env; JNIEnv *env;
if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK) if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION) != JNI_OK)
+1 -1
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#pragma once #pragma once
+1 -1
View File
@@ -350,7 +350,7 @@ struct System::Impl {
// Register with applet manager // Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear // All threads are started, begin main process execution, now that we're in the clear
applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params); applet_manager.CreateAndInsertByFrontendAppletParameters(std::make_unique<Service::Process>(*std::move(process)), params);
if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) { if (Settings::values.gamecard_current_game) {
+6 -6
View File
@@ -729,7 +729,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface(ctx, ensure_token_id); rb.PushIpcInterface(ensure_token_id);
} }
void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) { void LoadIdTokenCacheDeprecated(HLERequestContext& ctx) {
@@ -921,7 +921,7 @@ void Module::Interface::GetProfile(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IProfile>(ctx, system, user_id, *profile_manager); rb.PushIpcInterface<IProfile>(system, user_id, *profile_manager);
} }
void Module::Interface::IsUserRegistrationRequestPermitted(HLERequestContext& ctx) { void Module::Interface::IsUserRegistrationRequestPermitted(HLERequestContext& ctx) {
@@ -993,7 +993,7 @@ void Module::Interface::GetBaasAccountManagerForApplication(HLERequestContext& c
LOG_DEBUG(Service_ACC, "called"); LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IManagerForApplication>(ctx, system, profile_manager); rb.PushIpcInterface<IManagerForApplication>(system, profile_manager);
} }
void Module::Interface::IsUserAccountSwitchLocked(HLERequestContext& ctx) { void Module::Interface::IsUserAccountSwitchLocked(HLERequestContext& ctx) {
@@ -1089,7 +1089,7 @@ void Module::Interface::GetProfileEditor(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IProfileEditor>(ctx, system, user_id, *profile_manager); rb.PushIpcInterface<IProfileEditor>(system, user_id, *profile_manager);
} }
void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) { void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
@@ -1100,7 +1100,7 @@ void Module::Interface::GetBaasAccountAdministrator(HLERequestContext &ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IAdministrator>(ctx, system, uuid); rb.PushIpcInterface<IAdministrator>(system, uuid);
} }
void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) { void Module::Interface::ListQualifiedUsers(HLERequestContext& ctx) {
@@ -1143,7 +1143,7 @@ void Module::Interface::GetBaasAccountManagerForSystemService(HLERequestContext&
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IManagerForSystemService>(ctx, system, uuid); rb.PushIpcInterface<IManagerForSystemService>(system, uuid);
} }
void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) { void Module::Interface::StoreSaveDataThumbnailSystem(HLERequestContext& ctx) {
+1 -1
View File
@@ -35,7 +35,7 @@ void IAsyncContext::GetSystemEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, completion_event->GetReadableEvent()); rb.PushCopyObjects(completion_event->GetReadableEvent());
} }
void IAsyncContext::Cancel(HLERequestContext& ctx) { void IAsyncContext::Cancel(HLERequestContext& ctx) {
-4
View File
@@ -109,10 +109,6 @@ struct Applet {
std::list<std::shared_ptr<Applet>> child_applets{}; std::list<std::shared_ptr<Applet>> child_applets{};
bool is_completed{}; bool is_completed{};
std::shared_ptr<Applet> reserved_applet{};
bool unwind_after_reserved{};
bool is_winding{};
// Self state // Self state
bool exit_locked{}; bool exit_locked{};
s32 fatal_section_count{}; s32 fatal_section_count{};
@@ -25,13 +25,6 @@ void AppletStorageChannel::Push(Kernel::KernelCore& kernel, std::shared_ptr<ISto
m_event.Signal(kernel); m_event.Signal(kernel);
} }
void AppletStorageChannel::Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage) {
std::scoped_lock lk{m_lock};
m_data.emplace_front(std::move(storage));
m_event.Signal(kernel);
}
Result AppletStorageChannel::Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage) { Result AppletStorageChannel::Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage) {
std::scoped_lock lk{m_lock}; std::scoped_lock lk{m_lock};
@@ -26,7 +26,6 @@ public:
~AppletStorageChannel(); ~AppletStorageChannel();
void Push(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage); void Push(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
void Unpop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage> storage);
Result Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage); Result Pop(Kernel::KernelCore& kernel, std::shared_ptr<IStorage>* out_storage);
Kernel::KReadableEvent* GetEvent(); Kernel::KReadableEvent* GetEvent();
+1 -1
View File
@@ -267,7 +267,7 @@ void AppletManager::SetWindowSystem(WindowSystem* window_system) {
if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) { if (Settings::values.enable_overlay && m_window_system->GetOverlayDisplayApplet() == nullptr) {
if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) { if (auto overlay_process = CreateProcess(m_system, static_cast<u64>(AppletProgramId::OverlayDisplay), 0, 0)) {
auto overlay_applet = std::make_shared<Applet>(m_system, std::move(overlay_process), false); auto overlay_applet = std::make_shared<Applet>(m_system, std::make_unique<Service::Process>(*std::move(overlay_process)), false);
overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay); overlay_applet->program_id = static_cast<u64>(AppletProgramId::OverlayDisplay);
overlay_applet->applet_id = AppletId::OverlayDisplay; overlay_applet->applet_id = AppletId::OverlayDisplay;
overlay_applet->type = AppletType::OverlayApplet; overlay_applet->type = AppletType::OverlayApplet;
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -15,10 +12,7 @@ namespace Service::AM {
HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) { HidRegistration::HidRegistration(Core::System& system, Process& process) : m_process(process) {
m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid", true); m_hid_server = system.ServiceManager().GetService<HID::IHidServer>("hid", true);
this->RegisterCurrentProcess();
}
void HidRegistration::RegisterCurrentProcess() {
if (m_process.IsInitialized()) { if (m_process.IsInitialized()) {
m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(), m_hid_server->GetResourceManager()->RegisterAppletResourceUserId(m_process.GetProcessId(),
true); true);
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -27,7 +24,6 @@ public:
explicit HidRegistration(Core::System& system, Process& process); explicit HidRegistration(Core::System& system, Process& process);
~HidRegistration(); ~HidRegistration();
void RegisterCurrentProcess();
void EnableAppletToGetInput(bool enable); void EnableAppletToGetInput(bool enable);
private: private:
@@ -171,18 +171,6 @@ void LifecycleManager::SignalSystemEventIfNeeded(Kernel::KernelCore& kernel) {
} }
} }
void LifecycleManager::ResetForRelaunch() {
m_unordered_messages.clear();
m_activity_state = ActivityState::BackgroundVisible;
m_requested_focus_state = FocusState{};
m_acknowledged_focus_state = FocusState{};
m_has_focus_state_changed = true;
m_suspend_mode = SuspendMode::NoOverride;
m_forced_suspend = false;
}
bool LifecycleManager::PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message) { bool LifecycleManager::PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message) {
const auto message = this->PopMessageInOrderOfPriority(); const auto message = this->PopMessageInOrderOfPriority();
this->SignalSystemEventIfNeeded(kernel); this->SignalSystemEventIfNeeded(kernel);
@@ -138,8 +138,6 @@ public:
void PushUnorderedMessage(Kernel::KernelCore& kernel, AppletMessage message); void PushUnorderedMessage(Kernel::KernelCore& kernel, AppletMessage message);
bool PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message); bool PopMessage(Kernel::KernelCore& kernel, AppletMessage* out_message);
void ResetForRelaunch();
private: private:
FocusState GetFocusStateWhileForegroundObscured() const; FocusState GetFocusStateWhileForegroundObscured() const;
FocusState GetFocusStateWhileBackground(bool is_obscured) const; FocusState GetFocusStateWhileBackground(bool is_obscured) const;
+8 -24
View File
@@ -40,23 +40,23 @@ namespace {
} }
} }
[[nodiscard]] inline std::unique_ptr<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) { [[nodiscard]] inline std::optional<Process> CreateProcessImpl(std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
// Get the appropriate loader to parse this NCA. // Get the appropriate loader to parse this NCA.
out_loader = Loader::GetLoader(system, file, program_id, program_index); out_loader = Loader::GetLoader(system, file, program_id, program_index);
// Ensure we have a loader which can parse the NCA. // Ensure we have a loader which can parse the NCA.
if (out_loader) { if (out_loader) {
// Try to load the process. // Try to load the process.
auto process = std::make_unique<Process>(system); auto process = std::make_optional<Process>(system);
if (process->Initialize(*out_loader, out_load_result)) { if (process->Initialize(*out_loader, out_load_result)) {
return process; return process;
} }
} }
return nullptr; return std::nullopt;
} }
} // Anonymous namespace } // Anonymous namespace
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) { std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation) {
// Attempt to load program NCA. // Attempt to load program NCA.
FileSys::VirtualFile nca_raw{}; FileSys::VirtualFile nca_raw{};
@@ -66,7 +66,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8
// Ensure we retrieved a program NCA. // Ensure we retrieved a program NCA.
if (!nca_raw) { if (!nca_raw) {
return nullptr; return std::nullopt;
} }
// Ensure we have a suitable version. // Ensure we have a suitable version.
@@ -76,7 +76,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8
(nca.GetKeyGeneration() < minimum_key_generation || (nca.GetKeyGeneration() < minimum_key_generation ||
nca.GetKeyGeneration() > maximum_key_generation)) { nca.GetKeyGeneration() > maximum_key_generation)) {
LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration()); LOG_WARNING(Service_LDR, "Skipping program {:016X} with generation {}", program_id, nca.GetKeyGeneration());
return nullptr; return std::nullopt;
} }
} }
@@ -85,7 +85,7 @@ std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8
return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0); return CreateProcessImpl(loader, status, system, nca_raw, program_id, 0);
} }
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) { std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index) {
if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) { if (auto process = CreateProcessImpl(out_loader, out_load_result, system, file, program_id, program_index); process) {
FileSys::NACP nacp; FileSys::NACP nacp;
if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) { if (out_loader->ReadControlData(nacp) == Loader::ResultStatus::Success) {
@@ -110,23 +110,7 @@ std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control,
system.GetARPManager().Register(launch.title_id, launch, out_control); system.GetARPManager().Register(launch.title_id, launch, out_control);
return process; return process;
} }
return nullptr; return std::nullopt;
}
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id) {
auto& storage = system.GetContentProviderUnion();
const auto nca_raw = storage.GetEntryRaw(program_id, FileSys::ContentRecordType::Program);
if (!nca_raw) {
return false;
}
auto loader = Loader::GetLoader(system, nca_raw, program_id, 0);
if (!loader) {
return false;
}
Loader::ResultStatus status{};
return process.Initialize(*loader, status);
} }
} // namespace Service::AM } // namespace Service::AM
+2 -4
View File
@@ -27,9 +27,7 @@ class Process;
namespace Service::AM { namespace Service::AM {
std::unique_ptr<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation); std::optional<Process> CreateProcess(Core::System& system, u64 program_id, u8 minimum_key_generation, u8 maximum_key_generation);
std::unique_ptr<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index); std::optional<Process> CreateApplicationProcess(std::vector<u8>& out_control, std::unique_ptr<Loader::AppLoader>& out_loader, Loader::ResultStatus& out_load_result, Core::System& system, FileSys::VirtualFile file, u64 program_id, u64 program_index);
bool ReinitializeProcess(Core::System& system, Process& process, u64 program_id);
} // namespace Service::AM } // namespace Service::AM
@@ -35,9 +35,9 @@ Result CreateGuestApplication(SharedPointer<IApplicationAccessor>* out_applicati
std::unique_ptr<Loader::AppLoader> loader; std::unique_ptr<Loader::AppLoader> loader;
Loader::ResultStatus result; Loader::ResultStatus result;
auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0); auto process = CreateApplicationProcess(control, loader, result, system, nca_raw, program_id, 0);
R_UNLESS(process != nullptr, ResultUnknown); R_UNLESS(process != std::nullopt, ResultUnknown);
const auto applet = std::make_shared<Applet>(system, std::move(process), true); const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
applet->program_id = program_id; applet->program_id = program_id;
applet->applet_id = AppletId::Application; applet->applet_id = AppletId::Application;
applet->type = AppletType::Application; applet->type = AppletType::Application;
@@ -88,9 +88,9 @@ Result IApplicationCreator::CreateSystemApplication(
std::vector<u8> control; std::vector<u8> control;
std::unique_ptr<Loader::AppLoader> loader; std::unique_ptr<Loader::AppLoader> loader;
auto process = CreateProcess(system, application_id, 1, 22); auto process = CreateProcess(system, application_id, 1, 22);
R_UNLESS(process != nullptr, ResultUnknown); R_UNLESS(process != std::nullopt, ResultUnknown);
const auto applet = std::make_shared<Applet>(system, std::move(process), true); const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), true);
applet->program_id = application_id; applet->program_id = application_id;
applet->applet_id = AppletId::Starter; applet->applet_id = AppletId::Starter;
applet->type = AppletType::LibraryApplet; applet->type = AppletType::LibraryApplet;
@@ -75,7 +75,7 @@ ILibraryAppletAccessor::ILibraryAppletAccessor(Core::System& system_,
{105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"}, {105, D<&ILibraryAppletAccessor::GetPopOutDataEvent>, "GetPopOutDataEvent"},
{106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"}, {106, D<&ILibraryAppletAccessor::GetPopInteractiveOutDataEvent>, "GetPopInteractiveOutDataEvent"},
{110, nullptr, "NeedsToExitProcess"}, {110, nullptr, "NeedsToExitProcess"},
{120, D<&ILibraryAppletAccessor::GetLibraryAppletInfo>, "GetLibraryAppletInfo"}, {120, nullptr, "GetLibraryAppletInfo"},
{150, nullptr, "RequestForAppletToGetForeground"}, {150, nullptr, "RequestForAppletToGetForeground"},
{160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+ {160, D<&ILibraryAppletAccessor::GetIndirectLayerConsumerHandle>, "GetIndirectLayerConsumerHandle"}, //2.0.0+
{170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+ {170, D<&ILibraryAppletAccessor::Unknown170>, "Unknown170"}, //22.0.0+
@@ -218,16 +218,6 @@ Result ILibraryAppletAccessor::GetIndirectLayerConsumerHandle(Out<u64> out_handl
R_SUCCEED(); R_SUCCEED();
} }
Result ILibraryAppletAccessor::GetLibraryAppletInfo(
Out<LibraryAppletInfo> out_library_applet_info) {
LOG_INFO(Service_AM, "called");
*out_library_applet_info = {
.applet_id = m_applet->applet_id,
.library_applet_mode = m_applet->library_applet_mode,
};
R_SUCCEED();
}
Result ILibraryAppletAccessor::Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event) { Result ILibraryAppletAccessor::Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event) {
LOG_WARNING(Service_AM, "(STUBBED) called"); LOG_WARNING(Service_AM, "(STUBBED) called");
*out_event = m_applet->unknown_event.GetHandle(); *out_event = m_applet->unknown_event.GetHandle();
@@ -6,7 +6,6 @@
#pragma once #pragma once
#include "core/hle/service/am/service/library_applet_self_accessor.h"
#include "core/hle/service/cmif_types.h" #include "core/hle/service/cmif_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
@@ -22,10 +21,6 @@ public:
std::shared_ptr<Applet> applet); std::shared_ptr<Applet> applet);
~ILibraryAppletAccessor(); ~ILibraryAppletAccessor();
std::shared_ptr<Applet> GetApplet() const {
return m_applet;
}
private: private:
Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetAppletStateChangedEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result IsCompleted(Out<bool> out_is_completed); Result IsCompleted(Out<bool> out_is_completed);
@@ -42,7 +37,6 @@ private:
Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetPopOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); Result GetPopInteractiveOutDataEvent(OutCopyHandle<Kernel::KReadableEvent> out_event);
Result GetIndirectLayerConsumerHandle(Out<u64> out_handle); Result GetIndirectLayerConsumerHandle(Out<u64> out_handle);
Result GetLibraryAppletInfo(Out<LibraryAppletInfo> out_library_applet_info);
Result Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event); Result Unknown170(OutCopyHandle<Kernel::KReadableEvent> out_event);
void FrontendExecute(); void FrontendExecute();
@@ -123,7 +123,7 @@ std::shared_ptr<ILibraryAppletAccessor> CreateGuestApplet(Core::System& system,
auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200); auto process = CreateProcess(system, program_id, Firmware1400, Firmware2200);
if (process) { if (process) {
const auto applet = std::make_shared<Applet>(system, std::move(process), false); const auto applet = std::make_shared<Applet>(system, std::make_unique<Service::Process>(*std::move(process)), false);
applet->program_id = program_id; applet->program_id = program_id;
applet->applet_id = applet_id; applet->applet_id = applet_id;
applet->type = AppletType::LibraryApplet; applet->type = AppletType::LibraryApplet;
@@ -233,9 +233,8 @@ Result ILibraryAppletSelfAccessor::ReportVisibleErrorWithErrorContext(
R_SUCCEED(); R_SUCCEED();
} }
Result ILibraryAppletSelfAccessor::UnpopInData(SharedPointer<IStorage> storage) { Result ILibraryAppletSelfAccessor::UnpopInData() {
LOG_INFO(Service_AM, "called"); LOG_WARNING(Service_AM, "(STUBBED) called");
m_broker->GetInData().Unpop(system.Kernel(), storage);
R_SUCCEED(); R_SUCCEED();
} }
@@ -72,7 +72,7 @@ private:
Result ReportVisibleError(ErrorCode error_code); Result ReportVisibleError(ErrorCode error_code);
Result ReportVisibleErrorWithErrorContext( Result ReportVisibleErrorWithErrorContext(
ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context); ErrorCode error_code, InLargeData<ErrorContext, BufferAttr_HipcMapAlias> error_context);
Result UnpopInData(SharedPointer<IStorage> storage); Result UnpopInData();
Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language); Result GetMainAppletApplicationDesiredLanguage(Out<u64> out_desired_language);
Result GetCurrentApplicationId(Out<u64> out_application_id); Result GetCurrentApplicationId(Out<u64> out_application_id);
Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count, Result GetMainAppletAvailableUsers(Out<bool> out_can_select_any_user, Out<s32> out_users_count,
@@ -1,11 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include "core/core.h"
#include "core/hle/service/am/applet.h"
#include "core/hle/service/am/frontend/applets.h" #include "core/hle/service/am/frontend/applets.h"
#include "core/hle/service/am/service/library_applet_accessor.h" #include "core/hle/service/am/service/library_applet_accessor.h"
#include "core/hle/service/am/service/process_winding_controller.h" #include "core/hle/service/am/service/process_winding_controller.h"
@@ -45,18 +43,6 @@ Result IProcessWindingController::OpenCallingLibraryApplet(
Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet) { Out<SharedPointer<ILibraryAppletAccessor>> out_calling_library_applet) {
LOG_INFO(Service_AM, "called"); LOG_INFO(Service_AM, "called");
std::shared_ptr<Applet> reserved_applet;
{
std::scoped_lock lk{m_applet->lock};
reserved_applet = std::move(m_applet->reserved_applet);
}
if (reserved_applet != nullptr) {
*out_calling_library_applet = std::make_shared<ILibraryAppletAccessor>(
system, reserved_applet->caller_applet_broker, reserved_applet);
R_SUCCEED();
}
const auto caller_applet = m_applet->caller_applet.lock(); const auto caller_applet = m_applet->caller_applet.lock();
if (caller_applet == nullptr) { if (caller_applet == nullptr) {
LOG_ERROR(Service_AM, "No caller applet available"); LOG_ERROR(Service_AM, "No caller applet available");
@@ -88,82 +74,22 @@ Result IProcessWindingController::PopContext(Out<SharedPointer<IStorage>> out_co
} }
Result IProcessWindingController::CancelWindingReservation() { Result IProcessWindingController::CancelWindingReservation() {
LOG_INFO(Service_AM, "called"); LOG_WARNING(Service_AM, "STUBBED");
std::scoped_lock lk{m_applet->lock};
m_applet->reserved_applet.reset();
m_applet->unwind_after_reserved = false;
R_SUCCEED(); R_SUCCEED();
} }
Result IProcessWindingController::WindAndDoReserved() { Result IProcessWindingController::WindAndDoReserved() {
LOG_INFO(Service_AM, "called"); LOG_WARNING(Service_AM, "STUBBED");
std::shared_ptr<Applet> reserved_applet;
{
std::scoped_lock lk{m_applet->lock};
reserved_applet = m_applet->reserved_applet;
m_applet->display_layer_manager.SetWindowVisibility(false);
m_applet->exit_locked = false;
system.SetExitLocked(false);
}
if (reserved_applet) {
{
std::scoped_lock lk{m_applet->lock};
m_applet->is_winding = true;
}
{
std::scoped_lock lk{reserved_applet->lock};
reserved_applet->window_visible = true;
reserved_applet->process->Run();
}
if (reserved_applet->frontend) {
reserved_applet->frontend->Initialize();
reserved_applet->frontend->Execute();
}
} else {
LOG_WARNING(Service_AM, "called without a reserved applet to start");
}
m_applet->process->Terminate();
R_SUCCEED(); R_SUCCEED();
} }
Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis( Result IProcessWindingController::ReserveToStartAndWaitAndUnwindThis() {
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) { LOG_WARNING(Service_AM, "STUBBED");
LOG_INFO(Service_AM, "called");
if (reserved_applet_accessor == nullptr) {
LOG_ERROR(Service_AM, "No applet accessor provided");
R_THROW(ResultUnknown);
}
std::scoped_lock lk{m_applet->lock};
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
m_applet->unwind_after_reserved = true;
R_SUCCEED(); R_SUCCEED();
} }
Result IProcessWindingController::ReserveToStartAndWait( Result IProcessWindingController::ReserveToStartAndWait() {
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor) { LOG_WARNING(Service_AM, "STUBBED");
LOG_INFO(Service_AM, "called");
if (reserved_applet_accessor == nullptr) {
LOG_ERROR(Service_AM, "No applet accessor provided");
R_THROW(ResultUnknown);
}
std::scoped_lock lk{m_applet->lock};
m_applet->reserved_applet = reserved_applet_accessor->GetApplet();
m_applet->unwind_after_reserved = false;
R_SUCCEED(); R_SUCCEED();
} }
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project
@@ -29,9 +29,8 @@ private:
Result PopContext(Out<SharedPointer<IStorage>> out_context); Result PopContext(Out<SharedPointer<IStorage>> out_context);
Result CancelWindingReservation(); Result CancelWindingReservation();
Result WindAndDoReserved(); Result WindAndDoReserved();
Result ReserveToStartAndWaitAndUnwindThis( Result ReserveToStartAndWaitAndUnwindThis();
SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor); Result ReserveToStartAndWait();
Result ReserveToStartAndWait(SharedPointer<ILibraryAppletAccessor> reserved_applet_accessor);
const std::shared_ptr<Applet> m_applet; const std::shared_ptr<Applet> m_applet;
}; };
-57
View File
@@ -9,7 +9,6 @@
#include "core/hle/service/am/applet.h" #include "core/hle/service/am/applet.h"
#include "core/hle/service/am/applet_manager.h" #include "core/hle/service/am/applet_manager.h"
#include "core/hle/service/am/event_observer.h" #include "core/hle/service/am/event_observer.h"
#include "core/hle/service/am/process_creation.h"
#include "core/hle/service/am/window_system.h" #include "core/hle/service/am/window_system.h"
namespace Service::AM { namespace Service::AM {
@@ -241,37 +240,6 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
continue; continue;
} }
// A winding applet has had its own process killed but is kept alive as a transparent slot
// while the reserved applet running in its place finishes (see WindAndDoReserved()).
if (applet->is_winding) {
if (!applet->child_applets.empty()) {
it = std::next(it);
continue;
}
const bool unwind = applet->unwind_after_reserved;
applet->is_winding = false;
applet->unwind_after_reserved = false;
if (unwind && this->RestartAppletProcessLocked(applet.get())) {
const u64 new_aruid = applet->aruid.pid;
const auto next = std::next(it);
if (new_aruid != aruid) {
auto node = m_applets.extract(it);
node.key() = new_aruid;
m_applets.insert(std::move(node));
}
applet->process->Run();
m_event_observer->RequestUpdate();
it = next;
continue;
}
applet->reserved_applet.reset();
}
// Terminated, so ensure all child applets are terminated. // Terminated, so ensure all child applets are terminated.
if (!applet->child_applets.empty()) { if (!applet->child_applets.empty()) {
this->TerminateChildAppletsLocked(applet.get()); this->TerminateChildAppletsLocked(applet.get());
@@ -333,28 +301,6 @@ void WindowSystem::PruneTerminatedAppletsLocked() {
} }
} }
bool WindowSystem::RestartAppletProcessLocked(Applet* applet) {
if (!ReinitializeProcess(m_system, *applet->process, applet->program_id)) {
LOG_ERROR(Service_AM, "Failed to restart winding applet_id={}",
static_cast<u32>(applet->applet_id));
return false;
}
applet->aruid.pid = applet->process->GetProcessId();
applet->is_process_running = false;
applet->is_completed = false;
applet->hid_registration.RegisterCurrentProcess();
applet->lifecycle_manager.ResetForRelaunch();
applet->is_activity_runnable = false;
applet->launch_reason.flag = 1;
m_event_observer->TrackAppletProcess(*applet);
return true;
}
bool WindowSystem::LockHomeMenuIntoForegroundLocked() { bool WindowSystem::LockHomeMenuIntoForegroundLocked() {
// If the home menu is not locked into foreground, then there's nothing to do. // If the home menu is not locked into foreground, then there's nothing to do.
if (m_home_menu == nullptr || !m_home_menu_foreground_locked) { if (m_home_menu == nullptr || !m_home_menu_foreground_locked) {
@@ -406,9 +352,6 @@ void WindowSystem::UpdateAppletStateLocked(Applet* applet, bool is_foreground, b
const bool has_obscuring_child_applets = [&] { const bool has_obscuring_child_applets = [&] {
for (const auto& child_applet : applet->child_applets) { for (const auto& child_applet : applet->child_applets) {
std::scoped_lock lk2{child_applet->lock}; std::scoped_lock lk2{child_applet->lock};
if (child_applet->is_winding) {
return true;
}
const auto mode = child_applet->library_applet_mode; const auto mode = child_applet->library_applet_mode;
if (child_applet->is_process_running && child_applet->window_visible && if (child_applet->is_process_running && child_applet->window_visible &&
(mode == LibraryAppletMode::AllForeground || (mode == LibraryAppletMode::AllForeground ||
-1
View File
@@ -61,7 +61,6 @@ public:
private: private:
void PruneTerminatedAppletsLocked(); void PruneTerminatedAppletsLocked();
bool RestartAppletProcessLocked(Applet* applet);
bool LockHomeMenuIntoForegroundLocked(); bool LockHomeMenuIntoForegroundLocked();
void TerminateChildAppletsLocked(Applet* applet); void TerminateChildAppletsLocked(Applet* applet);
void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false); void UpdateAppletStateLocked(Applet* applet, bool is_foreground, bool overlay_blocking = false);
+2 -2
View File
@@ -82,7 +82,7 @@ void APM::OpenSession(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISession>(ctx, system, controller); rb.PushIpcInterface<ISession>(system, controller);
} }
void APM::GetPerformanceMode(HLERequestContext& ctx) { void APM::GetPerformanceMode(HLERequestContext& ctx) {
@@ -125,7 +125,7 @@ void APM_Sys::GetPerformanceEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISession>(ctx, system, controller); rb.PushIpcInterface<ISession>(system, controller);
} }
void APM_Sys::SetCpuBoostMode(HLERequestContext& ctx) { void APM_Sys::SetCpuBoostMode(HLERequestContext& ctx) {
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -48,7 +45,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IRequest>(ctx, system); rb.PushIpcInterface<IRequest>(system);
} }
}; };
+7 -7
View File
@@ -184,7 +184,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(readable_event.Signal(system.Kernel())); rb.Push(readable_event.Signal(system.Kernel()));
rb.PushCopyObjects(ctx, readable_event); rb.PushCopyObjects(readable_event);
} }
void Cancel(HLERequestContext& ctx) { void Cancel(HLERequestContext& ctx) {
@@ -400,7 +400,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, notification_event->GetReadableEvent()); rb.PushCopyObjects(notification_event->GetReadableEvent());
} }
void Clear(HLERequestContext& ctx) { void Clear(HLERequestContext& ctx) {
@@ -476,7 +476,7 @@ private:
void Module::Interface::CreateFriendService(HLERequestContext& ctx) { void Module::Interface::CreateFriendService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IFriendService>(ctx, system); rb.PushIpcInterface<IFriendService>(system);
LOG_DEBUG(Service_Friend, "called"); LOG_DEBUG(Service_Friend, "called");
} }
@@ -488,12 +488,12 @@ void Module::Interface::CreateNotificationService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<INotificationService>(ctx, system, uuid); rb.PushIpcInterface<INotificationService>(system, uuid);
} }
Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_, const char* name) Module::Interface::Interface(std::shared_ptr<Module> module_, Core::System& system_,
: ServiceFramework{system_, name}, module{std::move(module_)} const char* name)
{} : ServiceFramework{system_, name}, module{std::move(module_)} {}
Module::Interface::~Interface() = default; Module::Interface::~Interface() = default;
+1 -1
View File
@@ -279,7 +279,7 @@ void ARP_W::AcquireRegistrar(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface(ctx, registrar); rb.PushIpcInterface(registrar);
} }
void ARP_W::UnregisterApplicationInstance(HLERequestContext& ctx) { void ARP_W::UnregisterApplicationInstance(HLERequestContext& ctx) {
+1 -1
View File
@@ -28,7 +28,7 @@ void BGTC_T::OpenTaskService(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ITaskService>(ctx, system); rb.PushIpcInterface<ITaskService>(system);
} }
ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} { ITaskService::ITaskService(Core::System& system_) : ServiceFramework{system_, "ITaskService"} {
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -59,7 +56,7 @@ ECTX_AW::~ECTX_AW() = default;
void ECTX_AW::CreateContextRegistrar(HLERequestContext& ctx) { void ECTX_AW::CreateContextRegistrar(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IContextRegistrar>(ctx, system); rb.PushIpcInterface<IContextRegistrar>(std::make_shared<IContextRegistrar>(system));
} }
} // namespace Service::Glue } // namespace Service::Glue
@@ -726,7 +726,7 @@ void IHidSystemServer::AcquireConnectionTriggerTimeoutEvent(HLERequestContext& c
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent()); rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
} }
void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) { void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLERequestContext& ctx) {
@@ -734,7 +734,7 @@ void IHidSystemServer::AcquireDeviceRegisteredEventForControllerSupport(HLEReque
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, acquire_device_registered_event->GetReadableEvent()); rb.PushCopyObjects(acquire_device_registered_event->GetReadableEvent());
} }
void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) { void IHidSystemServer::GetRegisteredDevices(HLERequestContext& ctx) {
@@ -759,7 +759,7 @@ void IHidSystemServer::AcquireUniquePadConnectionEventHandle(HLERequestContext&
LOG_WARNING(Service_HID, "(STUBBED) called"); LOG_WARNING(Service_HID, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.PushCopyObjects(ctx, unique_pad_connection_event->GetReadableEvent()); rb.PushCopyObjects(unique_pad_connection_event->GetReadableEvent());
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
} }
@@ -776,7 +776,7 @@ void IHidSystemServer::AcquireJoyDetachOnBluetoothOffEventHandle(HLERequestConte
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, joy_detach_event->GetReadableEvent()); rb.PushCopyObjects(joy_detach_event->GetReadableEvent());
} }
void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) { void IHidSystemServer::IsUsbFullKeyControllerEnabled(HLERequestContext& ctx) {
+2 -2
View File
@@ -31,7 +31,7 @@ class Memory;
} }
namespace IPC { namespace IPC {
struct ResponseBuilder; class ResponseBuilder;
} }
namespace Service { namespace Service {
@@ -392,7 +392,7 @@ public:
} }
private: private:
friend struct IPC::ResponseBuilder; friend class IPC::ResponseBuilder;
void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming); void ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming);
+153 -91
View File
@@ -24,7 +24,45 @@ namespace IPC {
constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301}; constexpr Result ResultSessionClosed{ErrorModule::HIPC, 301};
struct ResponseBuilder { class RequestHelperBase {
protected:
Service::HLERequestContext* context = nullptr;
u32* cmdbuf;
u32 index = 0;
public:
explicit RequestHelperBase(u32* command_buffer) : cmdbuf(command_buffer) {}
explicit RequestHelperBase(Service::HLERequestContext& ctx)
: context(&ctx), cmdbuf(ctx.CommandBuffer()) {}
void Skip(u32 size_in_words, bool set_to_null) {
if (set_to_null) {
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
}
index += size_in_words;
}
/**
* Aligns the current position forward to a 16-byte boundary, padding with zeros.
*/
void AlignWithPadding() {
if (index & 3) {
Skip(static_cast<u32>(4 - (index & 3)), true);
}
}
u32 GetCurrentOffset() const {
return index;
}
void SetCurrentOffset(u32 offset) {
index = offset;
}
};
class ResponseBuilder : public RequestHelperBase {
public:
/// Flags used for customizing the behavior of ResponseBuilder /// Flags used for customizing the behavior of ResponseBuilder
enum class Flags : u32 { enum class Flags : u32 {
None = 0, None = 0,
@@ -33,13 +71,14 @@ struct ResponseBuilder {
AlwaysMoveHandles = 1, AlwaysMoveHandles = 1,
}; };
inline explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_, u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0, Flags flags = Flags::None) explicit ResponseBuilder(Service::HLERequestContext& ctx, u32 normal_params_size_,
: cmdbuf(ctx.CommandBuffer()) u32 num_handles_to_copy_ = 0, u32 num_objects_to_move_ = 0,
, normal_params_size(normal_params_size_) Flags flags = Flags::None)
, num_handles_to_copy(num_handles_to_copy_) : RequestHelperBase(ctx), normal_params_size(normal_params_size_),
, num_objects_to_move(num_objects_to_move_) num_handles_to_copy(num_handles_to_copy_),
{ num_objects_to_move(num_objects_to_move_), kernel{ctx.kernel} {
std::memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
IPC::CommandHeader header{}; IPC::CommandHeader header{};
auto const mgr = ctx.GetManager().get(); auto const mgr = ctx.GetManager().get();
@@ -78,7 +117,9 @@ struct ResponseBuilder {
handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_); handle_descriptor_header.num_handles_to_copy.Assign(num_handles_to_copy_);
handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move); handle_descriptor_header.num_handles_to_move.Assign(num_handles_to_move);
PushRaw(handle_descriptor_header); PushRaw(handle_descriptor_header);
ctx.handles_offset = index; ctx.handles_offset = index;
Skip(num_handles_to_copy + num_handles_to_move, true); Skip(num_handles_to_copy + num_handles_to_move, true);
} }
@@ -90,6 +131,7 @@ struct ResponseBuilder {
domain_header.num_objects = num_domain_objects; domain_header.num_objects = num_domain_objects;
PushRaw(domain_header); PushRaw(domain_header);
} }
IPC::DataPayloadHeader data_payload_header{}; IPC::DataPayloadHeader data_payload_header{};
data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O'); data_payload_header.magic = Common::MakeMagic('S', 'F', 'C', 'O');
PushRaw(data_payload_header); PushRaw(data_payload_header);
@@ -99,39 +141,34 @@ struct ResponseBuilder {
ctx.data_payload_offset = index; ctx.data_payload_offset = index;
ctx.write_size += index; ctx.write_size += index;
ctx.domain_offset = u32(index + raw_data_size / sizeof(u32)); ctx.domain_offset = static_cast<u32>(index + raw_data_size / sizeof(u32));
} }
inline void Skip(u32 size_in_words, bool set_to_null) { template <class T>
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32)); void PushIpcInterface(std::shared_ptr<T> iface) {
index += size_in_words; auto manager{context->GetManager()};
}
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
inline u32 GetCurrentOffset() const { return index; }
inline void SetCurrentOffset(u32 offset) { index = offset; }
template <class T> inline void PushIpcInterface(Service::HLERequestContext& ctx, std::shared_ptr<T> iface) {
auto manager = ctx.GetManager();
if (manager->IsDomain()) { if (manager->IsDomain()) {
ctx.AddDomainObject(std::move(iface)); context->AddDomainObject(std::move(iface));
} else { } else {
ASSERT(Kernel::GetCurrentProcess(ctx.kernel).GetResourceLimit()->Reserve(ctx.kernel, Kernel::LimitableResource::SessionCountMax, 1)); ASSERT(Kernel::GetCurrentProcess(kernel).GetResourceLimit()->Reserve(kernel, Kernel::LimitableResource::SessionCountMax, 1));
auto* session = Kernel::KSession::Create(ctx.kernel); auto* session = Kernel::KSession::Create(kernel);
session->Initialize(ctx.kernel, nullptr, 0); session->Initialize(kernel, nullptr, 0);
Kernel::KSession::Register(ctx.kernel, session); Kernel::KSession::Register(kernel, session);
auto next_manager = std::make_shared<Service::SessionRequestManager>(ctx.kernel, manager->GetServerManager()); auto next_manager = std::make_shared<Service::SessionRequestManager>(
kernel, manager->GetServerManager());
next_manager->SetSessionHandler(iface); next_manager->SetSessionHandler(iface);
manager->GetServerManager().RegisterSession(&session->GetServerSession(), next_manager); manager->GetServerManager().RegisterSession(&session->GetServerSession(), next_manager);
ctx.AddMoveObject(&session->GetClientSession()); context->AddMoveObject(&session->GetClientSession());
} }
} }
template <class T, class... Args> inline void PushIpcInterface(Service::HLERequestContext& ctx, Args&&... args) { template <class T, class... Args>
PushIpcInterface<T>(ctx, std::make_shared<T>(std::forward<Args>(args)...)); void PushIpcInterface(Args&&... args) {
PushIpcInterface<T>(std::make_shared<T>(std::forward<Args>(args)...));
} }
void PushImpl(s8 value); void PushImpl(s8 value);
@@ -147,38 +184,59 @@ struct ResponseBuilder {
void PushImpl(bool value); void PushImpl(bool value);
void PushImpl(Result value); void PushImpl(Result value);
template <typename T> inline void Push(T value) { template <typename T>
void Push(T value) {
return PushImpl(value); return PushImpl(value);
} }
template <typename First, typename... Other> template <typename First, typename... Other>
void Push(const First& first_value, const Other&... other_values); void Push(const First& first_value, const Other&... other_values);
/// @brief Helper function for pushing strongly-typed enumeration values. /**
/// @tparam Enum The enumeration type to be pushed * Helper function for pushing strongly-typed enumeration values.
/// @param value The value to push. *
/// @note The underlying size of the enumeration type is the size of the data that gets pushed. * @tparam Enum The enumeration type to be pushed
/// e.g. "enum class SomeEnum : u16" will push a u16-sized amount of data. *
template <typename Enum> inline void PushEnum(Enum value) { * @param value The value to push.
*
* @note The underlying size of the enumeration type is the size of the
* data that gets pushed. e.g. "enum class SomeEnum : u16" will
* push a u16-sized amount of data.
*/
template <typename Enum>
void PushEnum(Enum value) {
static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call."); static_assert(std::is_enum_v<Enum>, "T must be an enum type within a PushEnum call.");
static_assert(!std::is_convertible_v<Enum, int>, "enum type in PushEnum must be a strongly typed enum."); static_assert(!std::is_convertible_v<Enum, int>,
"enum type in PushEnum must be a strongly typed enum.");
Push(static_cast<std::underlying_type_t<Enum>>(value)); Push(static_cast<std::underlying_type_t<Enum>>(value));
} }
/// @brief Copies the content of the given trivially copyable class to the buffer as a normal param /**
/// @note: The input class must be correctly packed/padded to fit hardware layout. * @brief Copies the content of the given trivially copyable class to the buffer as a normal
template <typename T> void PushRaw(const T& value); * param
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers); * @note: The input class must be correctly packed/padded to fit hardware layout.
template <typename... O> void PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers); */
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers); template <typename T>
template <typename... O> void PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers); void PushRaw(const T& value);
u32* cmdbuf; template <typename... O>
u32 index = 0; void PushMoveObjects(O*... pointers);
template <typename... O>
void PushMoveObjects(O&... pointers);
template <typename... O>
void PushCopyObjects(O*... pointers);
template <typename... O>
void PushCopyObjects(O&... pointers);
private:
u32 normal_params_size{}; u32 normal_params_size{};
u32 num_handles_to_copy{}; u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
u32 data_payload_index{}; u32 data_payload_index{};
Kernel::KernelCore& kernel;
}; };
/// Push /// /// Push ///
@@ -193,7 +251,8 @@ inline void ResponseBuilder::PushImpl(u32 value) {
template <typename T> template <typename T>
void ResponseBuilder::PushRaw(const T& value) { void ResponseBuilder::PushRaw(const T& value) {
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects"); static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(cmdbuf + index, &value, sizeof(T)); std::memcpy(cmdbuf + index, &value, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length index += (sizeof(T) + 3) / 4; // round up to word length
} }
@@ -213,8 +272,8 @@ inline void ResponseBuilder::PushImpl(s16 value) {
} }
inline void ResponseBuilder::PushImpl(s64 value) { inline void ResponseBuilder::PushImpl(s64 value) {
PushImpl(u32(value)); PushImpl(static_cast<u32>(value));
PushImpl(u32(value >> 32)); PushImpl(static_cast<u32>(value >> 32));
} }
inline void ResponseBuilder::PushImpl(u8 value) { inline void ResponseBuilder::PushImpl(u8 value) {
@@ -226,8 +285,8 @@ inline void ResponseBuilder::PushImpl(u16 value) {
} }
inline void ResponseBuilder::PushImpl(u64 value) { inline void ResponseBuilder::PushImpl(u64 value) {
PushImpl(u32(value)); PushImpl(static_cast<u32>(value));
PushImpl(u32(value >> 32)); PushImpl(static_cast<u32>(value >> 32));
} }
inline void ResponseBuilder::PushImpl(float value) { inline void ResponseBuilder::PushImpl(float value) {
@@ -253,88 +312,90 @@ void ResponseBuilder::Push(const First& first_value, const Other&... other_value
} }
template <typename... O> template <typename... O>
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O*... pointers) { inline void ResponseBuilder::PushCopyObjects(O*... pointers) {
auto objects = {pointers...}; auto objects = {pointers...};
for (auto& object : objects) { for (auto& object : objects) {
ctx.AddCopyObject(object); context->AddCopyObject(object);
} }
} }
template <typename... O> template <typename... O>
inline void ResponseBuilder::PushCopyObjects(Service::HLERequestContext& ctx, O&... pointers) { inline void ResponseBuilder::PushCopyObjects(O&... pointers) {
auto objects = {&pointers...}; auto objects = {&pointers...};
for (auto& object : objects) { for (auto& object : objects) {
ctx.AddCopyObject(object); context->AddCopyObject(object);
} }
} }
template <typename... O> template <typename... O>
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O*... pointers) { inline void ResponseBuilder::PushMoveObjects(O*... pointers) {
auto objects = {pointers...}; auto objects = {pointers...};
for (auto& object : objects) { for (auto& object : objects) {
ctx.AddMoveObject(object); context->AddMoveObject(object);
} }
} }
template <typename... O> template <typename... O>
inline void ResponseBuilder::PushMoveObjects(Service::HLERequestContext& ctx, O&... pointers) { inline void ResponseBuilder::PushMoveObjects(O&... pointers) {
auto objects = {&pointers...}; auto objects = {&pointers...};
for (auto& object : objects) { for (auto& object : objects) {
ctx.AddMoveObject(object); context->AddMoveObject(object);
} }
} }
struct RequestParser { class RequestParser : public RequestHelperBase {
inline explicit RequestParser(u32* command_buffer) : cmdbuf(command_buffer) {} public:
inline explicit RequestParser(Service::HLERequestContext& ctx) explicit RequestParser(u32* command_buffer) : RequestHelperBase(command_buffer) {}
: cmdbuf(ctx.CommandBuffer())
{ explicit RequestParser(Service::HLERequestContext& ctx) : RequestHelperBase(ctx) {
// TIPC does not have data payload offset // TIPC does not have data payload offset
if (!ctx.IsTipc()) { if (!ctx.IsTipc()) {
ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete"); ASSERT_MSG(ctx.GetDataPayloadOffset(), "context is incomplete");
Skip(ctx.GetDataPayloadOffset(), false); Skip(ctx.GetDataPayloadOffset(), false);
} }
// Skip the u64 command id, it's already stored in the context // Skip the u64 command id, it's already stored in the context
static constexpr u32 CommandIdSize = 2; static constexpr u32 CommandIdSize = 2;
Skip(CommandIdSize, false); Skip(CommandIdSize, false);
} }
inline void Skip(u32 size_in_words, bool set_to_null) { template <typename T>
if (set_to_null) std::memset(cmdbuf + index, 0, size_in_words * sizeof(u32)); T Pop();
index += size_in_words;
}
/// @brief Aligns the current position forward to a 16-byte boundary, padding with zeros.
inline void AlignWithPadding() { if (index & 3) Skip(u32(4 - (index & 3)), true); }
inline u32 GetCurrentOffset() const { return index; }
inline void SetCurrentOffset(u32 offset) { index = offset; }
template <typename T> T Pop(); template <typename T>
template <typename T> void Pop(T& value); void Pop(T& value);
template <typename First, typename... Other> void Pop(First& first_value, Other&... other_values);
template <typename First, typename... Other>
void Pop(First& first_value, Other&... other_values);
template <typename T> template <typename T>
T PopEnum() { T PopEnum() {
static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call."); static_assert(std::is_enum_v<T>, "T must be an enum type within a PopEnum call.");
static_assert(!std::is_convertible_v<T, int>, "enum type in PopEnum must be a strongly typed enum."); static_assert(!std::is_convertible_v<T, int>,
return T(Pop<std::underlying_type_t<T>>()); "enum type in PopEnum must be a strongly typed enum.");
return static_cast<T>(Pop<std::underlying_type_t<T>>());
} }
/// @brief Reads the next normal parameters as a struct, by copying it /**
/// @note: The output class must be correctly packed/padded to fit hardware layout. * @brief Reads the next normal parameters as a struct, by copying it
template <typename T> void PopRaw(T& value); * @note: The output class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
void PopRaw(T& value);
/// @brief Reads the next normal parameters as a struct, by copying it into a new value /**
/// @note: The output class must be correctly packed/padded to fit hardware layout. * @brief Reads the next normal parameters as a struct, by copying it into a new value
template <typename T> T PopRaw(); * @note: The output class must be correctly packed/padded to fit hardware layout.
*/
template <typename T>
T PopRaw();
template <class T> [[nodiscard]] std::weak_ptr<T> PopIpcInterface(Service::HLERequestContext& ctx) { template <class T>
ASSERT(ctx.GetManager()->IsDomain()); std::weak_ptr<T> PopIpcInterface() {
ASSERT(ctx.GetDomainMessageHeader().input_object_count > 0); ASSERT(context->GetManager()->IsDomain());
return ctx.GetDomainHandler<T>(Pop<u32>() - 1); ASSERT(context->GetDomainMessageHeader().input_object_count > 0);
return context->GetDomainHandler<T>(Pop<u32>() - 1);
} }
u32* cmdbuf;
u32 index = 0;
}; };
/// Pop /// /// Pop ///
@@ -346,7 +407,7 @@ inline u32 RequestParser::Pop() {
template <> template <>
inline s32 RequestParser::Pop() { inline s32 RequestParser::Pop() {
return s32(Pop<u32>()); return static_cast<s32>(Pop<u32>());
} }
// Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects. // Ignore the -Wclass-memaccess warning on memcpy for non-trivially default constructible objects.
@@ -356,7 +417,8 @@ inline s32 RequestParser::Pop() {
#endif #endif
template <typename T> template <typename T>
void RequestParser::PopRaw(T& value) { void RequestParser::PopRaw(T& value) {
static_assert(std::is_trivially_copyable_v<T>, "It's undefined behavior to use memcpy with non-trivially copyable objects"); static_assert(std::is_trivially_copyable_v<T>,
"It's undefined behavior to use memcpy with non-trivially copyable objects");
std::memcpy(&value, cmdbuf + index, sizeof(T)); std::memcpy(&value, cmdbuf + index, sizeof(T));
index += (sizeof(T) + 3) / 4; // round up to word length index += (sizeof(T) + 3) / 4; // round up to word length
} }
+1 -1
View File
@@ -353,7 +353,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ILogger>(ctx, system); rb.PushIpcInterface<ILogger>(system);
} }
}; };
+4 -4
View File
@@ -151,7 +151,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IAm>(ctx, system); rb.PushIpcInterface<IAm>(system);
} }
}; };
@@ -173,7 +173,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<MFIUser>(ctx, system); rb.PushIpcInterface<MFIUser>(system);
} }
}; };
@@ -195,7 +195,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IUser>(ctx, system); rb.PushIpcInterface<IUser>(system);
} }
}; };
@@ -217,7 +217,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystem>(ctx, system); rb.PushIpcInterface<ISystem>(system);
} }
}; };
+3 -3
View File
@@ -143,7 +143,7 @@ void NfcInterface::AttachAvailabilityChangeEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, GetManager()->AttachAvailabilityChangeEvent()); rb.PushCopyObjects(GetManager()->AttachAvailabilityChangeEvent());
} }
void NfcInterface::StartDetection(HLERequestContext& ctx) { void NfcInterface::StartDetection(HLERequestContext& ctx) {
@@ -203,7 +203,7 @@ void NfcInterface::AttachActivateEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(result); rb.Push(result);
rb.PushCopyObjects(ctx, out_event); rb.PushCopyObjects(out_event);
} }
void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) { void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
@@ -217,7 +217,7 @@ void NfcInterface::AttachDeactivateEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(result); rb.Push(result);
rb.PushCopyObjects(ctx, out_event); rb.PushCopyObjects(out_event);
} }
void NfcInterface::SetNfcEnabled(HLERequestContext& ctx) { void NfcInterface::SetNfcEnabled(HLERequestContext& ctx) {
+3 -3
View File
@@ -157,7 +157,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IUser>(ctx, system); rb.PushIpcInterface<IUser>(system);
} }
}; };
@@ -179,7 +179,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISystem>(ctx, system); rb.PushIpcInterface<ISystem>(system);
} }
}; };
@@ -201,7 +201,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IDebug>(ctx, system); rb.PushIpcInterface<IDebug>(system);
} }
}; };
+8 -8
View File
@@ -307,7 +307,7 @@ private:
void GetSystemEventReadableHandle(HLERequestContext& ctx) { void GetSystemEventReadableHandle(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 2}; IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, evt_scan_complete->GetReadableEvent(), rb.PushCopyObjects(evt_scan_complete->GetReadableEvent(),
evt_processing->GetReadableEvent()); evt_processing->GetReadableEvent());
} }
@@ -452,7 +452,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 2}; IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, event1->GetReadableEvent(), event2->GetReadableEvent()); rb.PushCopyObjects(event1->GetReadableEvent(), event2->GetReadableEvent());
} }
void Cancel(HLERequestContext& ctx) { void Cancel(HLERequestContext& ctx) {
@@ -528,7 +528,7 @@ void IGeneralService::CreateScanRequest(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IScanRequest>(ctx, system); rb.PushIpcInterface<IScanRequest>(system);
} }
void IGeneralService::CreateRequest(HLERequestContext& ctx) { void IGeneralService::CreateRequest(HLERequestContext& ctx) {
@@ -537,7 +537,7 @@ void IGeneralService::CreateRequest(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IRequest>(ctx, system); rb.PushIpcInterface<IRequest>(system);
} }
void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) { void IGeneralService::GetCurrentNetworkProfile(HLERequestContext& ctx) {
@@ -716,7 +716,7 @@ void IGeneralService::GetNetworkProfile(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<INetworkProfile>(ctx, system); rb.PushIpcInterface<INetworkProfile>(system);
} }
void IGeneralService::SetNetworkProfile(HLERequestContext& ctx) { void IGeneralService::SetNetworkProfile(HLERequestContext& ctx) {
@@ -869,7 +869,7 @@ void IGeneralService::CreateTemporaryNetworkProfile(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 6, 0, 1}; IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<INetworkProfile>(ctx, system); rb.PushIpcInterface<INetworkProfile>(system);
rb.PushRaw<u128>(uuid); rb.PushRaw<u128>(uuid);
} }
@@ -1124,7 +1124,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IGeneralService>(ctx, system); rb.PushIpcInterface<IGeneralService>(system);
} }
void CreateGeneralService(HLERequestContext& ctx) { void CreateGeneralService(HLERequestContext& ctx) {
@@ -1132,7 +1132,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IGeneralService>(ctx, system); rb.PushIpcInterface<IGeneralService>(system);
} }
}; };
+6 -6
View File
@@ -53,7 +53,7 @@ private:
LOG_WARNING(Service_NIM, "(STUBBED) called"); LOG_WARNING(Service_NIM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAsync>(ctx, system); rb.PushIpcInterface<IShopServiceAsync>(system);
} }
}; };
@@ -75,7 +75,7 @@ private:
LOG_WARNING(Service_NIM, "(STUBBED) called"); LOG_WARNING(Service_NIM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessor>(ctx, system); rb.PushIpcInterface<IShopServiceAccessor>(system);
} }
}; };
@@ -336,7 +336,7 @@ private:
LOG_DEBUG(Service_NIM, "(STUBBED) called"); LOG_DEBUG(Service_NIM, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system); rb.PushIpcInterface<IShopServiceAccessServer>(system);
} }
void IsLargeResourceAvailable(HLERequestContext& ctx) { void IsLargeResourceAvailable(HLERequestContext& ctx) {
@@ -356,7 +356,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IShopServiceAccessServer>(ctx, system); rb.PushIpcInterface<IShopServiceAccessServer>(system);
} }
}; };
@@ -439,7 +439,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, finished_event->GetReadableEvent()); rb.PushCopyObjects(finished_event->GetReadableEvent());
} }
void GetResult(HLERequestContext& ctx) { void GetResult(HLERequestContext& ctx) {
@@ -500,7 +500,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(ctx, system); rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>(system);
} }
// TODO(ogniK): Do we need these? // TODO(ogniK): Do we need these?
@@ -359,8 +359,8 @@ void IReadOnlyApplicationControlDataInterface::ListApplicationTitle(HLERequestCo
IPC::ResponseBuilder rb{ctx, 2, 1, 1}; IPC::ResponseBuilder rb{ctx, 2, 1, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, async_value->ReadableEvent()); rb.PushCopyObjects(async_value->ReadableEvent());
rb.PushIpcInterface(ctx, std::move(async_value)); rb.PushIpcInterface(std::move(async_value));
} }
Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3( Result IReadOnlyApplicationControlDataInterface::GetApplicationControlData3(
@@ -199,7 +199,7 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3, 1}; IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
auto& readable_event = event->GetReadableEvent(); auto& readable_event = event->GetReadableEvent();
rb.PushCopyObjects(ctx, readable_event); rb.PushCopyObjects(readable_event);
rb.PushEnum(NvResult::Success); rb.PushEnum(NvResult::Success);
} else { } else {
LOG_ERROR(Service_NVDRV, "Invalid event request!"); LOG_ERROR(Service_NVDRV, "Invalid event request!");
-11
View File
@@ -6,8 +6,6 @@
#pragma once #pragma once
#include <utility>
#include "common/common_types.h" #include "common/common_types.h"
namespace Core { namespace Core {
@@ -30,15 +28,6 @@ public:
inline explicit Process(Core::System& system) noexcept : m_system(system) {} inline explicit Process(Core::System& system) noexcept : m_system(system) {}
inline ~Process() { this->Finalize(); } inline ~Process() { this->Finalize(); }
Process(const Process&) = delete;
Process& operator=(const Process&) = delete;
Process& operator=(Process&&) = delete;
inline Process(Process&& other) noexcept
: m_system(other.m_system), m_process(std::exchange(other.m_process, nullptr)),
m_main_thread_stack_size(std::exchange(other.m_main_thread_stack_size, 0)),
m_main_thread_priority(std::exchange(other.m_main_thread_priority, 0)),
m_process_started(std::exchange(other.m_process_started, false)) {}
bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result); bool Initialize(Loader::AppLoader& loader, Loader::ResultStatus& out_load_result);
void Finalize(); void Finalize();
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -127,7 +124,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IClkrstSession>(ctx, system, device_code); rb.PushIpcInterface<IClkrstSession>(system, device_code);
} }
}; };
+1 -1
View File
@@ -161,7 +161,7 @@ private:
IPC::ResponseBuilder rb{ctx, 10, 1}; IPC::ResponseBuilder rb{ctx, 10, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, *process); rb.PushCopyObjects(*process);
rb.PushRaw(program_location); rb.PushRaw(program_location);
rb.PushRaw(override_status); rb.PushRaw(override_status);
} }
+3 -3
View File
@@ -147,7 +147,7 @@ void IAlarmService::CreateWakeupAlarm(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::WakeupAlarm); rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::WakeupAlarm);
} }
void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) { void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
@@ -155,7 +155,7 @@ void IAlarmService::CreateBackgroundTaskAlarm(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISteadyClockAlarm>(ctx, system, m_alarms, AlarmType::BackgroundTaskAlarm); rb.PushIpcInterface<ISteadyClockAlarm>(system, m_alarms, AlarmType::BackgroundTaskAlarm);
} }
ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type) ISteadyClockAlarm::ISteadyClockAlarm(Core::System& system_, Alarms& alarms, AlarmType type)
@@ -179,7 +179,7 @@ void ISteadyClockAlarm::GetAlarmEvent(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, m_alarm.GetEventHandle()); rb.PushCopyObjects(m_alarm.GetEventHandle());
} }
void ISteadyClockAlarm::Enable(HLERequestContext& ctx) { void ISteadyClockAlarm::Enable(HLERequestContext& ctx) {
+2 -2
View File
@@ -67,7 +67,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1}; IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushCopyObjects(ctx, state_change_event->GetReadableEvent()); rb.PushCopyObjects(state_change_event->GetReadableEvent());
} }
void UnbindStateChangeEvent(HLERequestContext& ctx) { void UnbindStateChangeEvent(HLERequestContext& ctx) {
@@ -186,7 +186,7 @@ void PSM::OpenSession(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IPsmSession>(ctx, system); rb.PushIpcInterface<IPsmSession>(system);
} }
void PSM::GetBatteryVoltageState(HLERequestContext& ctx) { void PSM::GetBatteryVoltageState(HLERequestContext& ctx) {
+1 -4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
@@ -85,7 +82,7 @@ void TS::OpenSession(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISession>(ctx, system); rb.PushIpcInterface<ISession>(system);
} }
} // namespace Service::PTM } // namespace Service::PTM
+5 -4
View File
@@ -140,7 +140,7 @@ void SM::GetServiceCmif(HLERequestContext& ctx) {
if (result == ResultSuccess) { if (result == ResultSuccess) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(result); rb.Push(result);
rb.PushMoveObjects(ctx, client_session); rb.PushMoveObjects(client_session);
} else { } else {
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result); rb.Push(result);
@@ -157,7 +157,7 @@ void SM::GetServiceTipc(HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(result); rb.Push(result);
rb.PushMoveObjects(ctx, result == ResultSuccess ? client_session : nullptr); rb.PushMoveObjects(result == ResultSuccess ? client_session : nullptr);
} }
static std::string PopServiceName(IPC::RequestParser& rp) { static std::string PopServiceName(IPC::RequestParser& rp) {
@@ -230,7 +230,8 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
max_session_count, is_light); max_session_count, is_light);
Kernel::KServerPort* server_port{}; Kernel::KServerPort* server_port{};
if (const auto result = service_manager.RegisterService(std::addressof(server_port), name, max_session_count, nullptr); if (const auto result = service_manager.RegisterService(std::addressof(server_port), name,
max_session_count, nullptr);
result.IsError()) { result.IsError()) {
LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw); LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}", result.raw);
IPC::ResponseBuilder rb{ctx, 2}; IPC::ResponseBuilder rb{ctx, 2};
@@ -240,7 +241,7 @@ void SM::RegisterServiceImpl(HLERequestContext& ctx, std::string name, u32 max_s
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushMoveObjects(ctx, server_port); rb.PushMoveObjects(server_port);
} }
void SM::UnregisterService(HLERequestContext& ctx) { void SM::UnregisterService(HLERequestContext& ctx) {
+1 -1
View File
@@ -60,7 +60,7 @@ void Controller::CloneCurrentObject(HLERequestContext& ctx) {
// We succeeded. // We succeeded.
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles}; IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushMoveObjects(ctx, session->GetClientSession()); rb.PushMoveObjects(session->GetClientSession());
} }
void Controller::CloneCurrentObjectEx(HLERequestContext& ctx) { void Controller::CloneCurrentObjectEx(HLERequestContext& ctx) {
+5 -3
View File
@@ -543,7 +543,8 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(res); rb.Push(res);
if (res == ResultSuccess) { if (res == ResultSuccess) {
rb.PushIpcInterface<ISslConnection>(ctx, system, ssl_version, shared_data, std::move(backend)); rb.PushIpcInterface<ISslConnection>(system, ssl_version, shared_data,
std::move(backend));
} }
} }
@@ -623,11 +624,12 @@ private:
IPC::RequestParser rp{ctx}; IPC::RequestParser rp{ctx};
const auto parameters = rp.PopRaw<Parameters>(); const auto parameters = rp.PopRaw<Parameters>();
LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}", parameters.ssl_version.api_version, parameters.pid_placeholder); LOG_WARNING(Service_SSL, "(STUBBED) called, api_version={}, pid_placeholder={}",
parameters.ssl_version.api_version, parameters.pid_placeholder);
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<ISslContext>(ctx, system, parameters.ssl_version); rb.PushIpcInterface<ISslContext>(system, parameters.ssl_version);
} }
void SetInterfaceVersion(HLERequestContext& ctx) { void SetInterfaceVersion(HLERequestContext& ctx) {
+2 -2
View File
@@ -155,7 +155,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IPdSession>(ctx, system); rb.PushIpcInterface<IPdSession>(system);
} }
}; };
@@ -199,7 +199,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1}; IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(ResultSuccess); rb.Push(ResultSuccess);
rb.PushIpcInterface<IPdCradleSession>(ctx, system); rb.PushIpcInterface<IPdCradleSession>(system);
} }
}; };
+2 -2
View File
@@ -50,8 +50,8 @@ NPad::NPad(Core::HID::HIDCore& hid_core_, KernelHelpers::ServiceContext& service
auto& controller = controller_data[aruid_index][i]; auto& controller = controller_data[aruid_index][i];
controller.device = hid_core.GetEmulatedControllerByIndex(i); controller.device = hid_core.GetEmulatedControllerByIndex(i);
Core::HID::ControllerUpdateCallback engine_callback{ Core::HID::ControllerUpdateCallback engine_callback{
.on_change = [this, i, kernel = &hid_core.kernel](Core::HID::ControllerTriggerType type) { .on_change = [this, i](Core::HID::ControllerTriggerType type) {
ControllerUpdate(*kernel, type, i); ControllerUpdate(hid_core.kernel, type, i);
}, },
.is_npad_service = true, .is_npad_service = true,
}; };
@@ -403,9 +403,6 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
if (info.uses_sampled_1d) { if (info.uses_sampled_1d) {
ctx.AddCapability(spv::Capability::Sampled1D); ctx.AddCapability(spv::Capability::Sampled1D);
} }
if (info.uses_image_1d) {
ctx.AddCapability(spv::Capability::Image1D);
}
if (info.uses_sparse_residency) { if (info.uses_sparse_residency) {
ctx.AddCapability(spv::Capability::SparseResidency); ctx.AddCapability(spv::Capability::SparseResidency);
} }
@@ -435,7 +432,7 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
} }
if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id || if ((info.uses_subgroup_vote || info.uses_subgroup_invocation_id ||
info.uses_subgroup_shuffles) && info.uses_subgroup_shuffles) &&
profile.support_vote && profile.SupportsSubgroupStage(ctx.stage)) { profile.support_vote) {
ctx.AddCapability(spv::Capability::GroupNonUniformBallot); ctx.AddCapability(spv::Capability::GroupNonUniformBallot);
ctx.AddCapability(spv::Capability::GroupNonUniformShuffle); ctx.AddCapability(spv::Capability::GroupNonUniformShuffle);
if (!profile.warp_size_potentially_larger_than_guest) { if (!profile.warp_size_potentially_larger_than_guest) {
@@ -202,8 +202,7 @@ void EmitGetIndirectBranchVariable(EmitContext&) {
} }
Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8 && if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
ctx.profile.support_uniform_and_storage_buffer_8bit) {
const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset, const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset,
ctx.load_const_func_u8)}; ctx.load_const_func_u8)};
return ctx.OpUConvert(ctx.U32[1], load); return ctx.OpUConvert(ctx.U32[1], load);
@@ -220,8 +219,7 @@ Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
} }
Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8 && if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) {
ctx.profile.support_uniform_and_storage_buffer_8bit) {
const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset, const Id load{GetCbuf(ctx, ctx.S8, &UniformDefinitions::S8, sizeof(s8), binding, offset,
ctx.load_const_func_u8)}; ctx.load_const_func_u8)};
return ctx.OpSConvert(ctx.U32[1], load); return ctx.OpSConvert(ctx.U32[1], load);
@@ -238,8 +236,7 @@ Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& of
} }
Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16 && if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
ctx.profile.support_uniform_and_storage_buffer_16bit) {
const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset, const Id load{GetCbuf(ctx, ctx.U16, &UniformDefinitions::U16, sizeof(u16), binding, offset,
ctx.load_const_func_u16)}; ctx.load_const_func_u16)};
return ctx.OpUConvert(ctx.U32[1], load); return ctx.OpUConvert(ctx.U32[1], load);
@@ -256,8 +253,7 @@ Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& o
} }
Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { Id EmitGetCbufS16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) {
if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16 && if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int16) {
ctx.profile.support_uniform_and_storage_buffer_16bit) {
const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset, const Id load{GetCbuf(ctx, ctx.S16, &UniformDefinitions::S16, sizeof(s16), binding, offset,
ctx.load_const_func_u16)}; ctx.load_const_func_u16)};
return ctx.OpSConvert(ctx.U32[1], load); return ctx.OpSConvert(ctx.U32[1], load);
@@ -260,13 +260,6 @@ bool IsTextureMsaa(EmitContext& ctx, const IR::TextureInstInfo& info) {
return ctx.textures.at(info.descriptor_index).is_multisample; return ctx.textures.at(info.descriptor_index).is_multisample;
} }
bool IsTextureInteger(EmitContext& ctx, const IR::TextureInstInfo& info) {
if (info.type == TextureType::Buffer) {
return false;
}
return ctx.textures.at(info.descriptor_index).is_integer;
}
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
if (info.relaxed_precision != 0) { if (info.relaxed_precision != 0) {
@@ -487,14 +480,11 @@ Id EmitBoundImageWrite(EmitContext&) {
Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id bias_lc, const IR::Value& offset) { Id bias_lc, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
Id color;
if (ctx.stage == Stage::Fragment) { if (ctx.stage == Stage::Fragment) {
const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0, const ImageOperands operands(ctx, info.has_bias != 0, false, info.has_lod_clamp != 0,
bias_lc, offset); bias_lc, offset);
color = Emit(&EmitContext::OpImageSparseSampleImplicitLod, return Emit(&EmitContext::OpImageSparseSampleImplicitLod,
&EmitContext::OpImageSampleImplicitLod, ctx, inst, result_type, &EmitContext::OpImageSampleImplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); Texture(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
} else { } else {
// We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as // We can't use implicit lods on non-fragment stages on SPIR-V. Maxwell hardware behaves as
@@ -502,29 +492,26 @@ Id EmitImageSampleImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value&
// derivatives // derivatives
const Id lod{ctx.Const(0.0f)}; const Id lod{ctx.Const(0.0f)};
const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset); const ImageOperands operands(ctx, false, true, info.has_lod_clamp != 0, lod, offset);
color = Emit(&EmitContext::OpImageSparseSampleExplicitLod, return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type, &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
} }
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageSampleExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id lod, const IR::Value& offset) { Id lod, const IR::Value& offset) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
const ImageOperands operands(ctx, false, true, false, lod, offset); const ImageOperands operands(ctx, false, true, false, lod, offset);
Id result = Emit(&EmitContext::OpImageSparseSampleExplicitLod, Id result = Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type, &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
#ifdef __ANDROID__ #ifdef __ANDROID__
if (!is_integer && Settings::values.fix_bloom_effects.GetValue()) { if (Settings::values.fix_bloom_effects.GetValue()) {
result = ctx.OpVectorTimesScalar(ctx.F32[4], result, ctx.Const(0.98f)); result = ctx.OpVectorTimesScalar(ctx.F32[4], result, ctx.Const(0.98f));
} }
#endif #endif
return is_integer ? ctx.OpBitcast(ctx.F32[4], result) : result; return result;
} }
Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id EmitImageSampleDrefImplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index,
@@ -560,39 +547,30 @@ Id EmitImageSampleDrefExplicitLod(EmitContext& ctx, IR::Inst* inst, const IR::Va
Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGather(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2) { const IR::Value& offset, const IR::Value& offset2) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
const ImageOperands operands(ctx, offset, offset2); const ImageOperands operands(ctx, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) { if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
} }
const Id color{Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst, return Emit(&EmitContext::OpImageSparseGather, &EmitContext::OpImageGather, ctx, inst,
result_type, Texture(ctx, info, index), coords, ctx.F32[4], Texture(ctx, info, index), coords, ctx.Const(info.gather_component),
ctx.Const(info.gather_component), operands.MaskOptional(), operands.MaskOptional(), operands.Span());
operands.Span())};
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGatherDref(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
const IR::Value& offset, const IR::Value& offset2, Id dref) { const IR::Value& offset, const IR::Value& offset2, Id dref) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
const ImageOperands operands(ctx, offset, offset2); const ImageOperands operands(ctx, offset, offset2);
if (ctx.profile.need_gather_subpixel_offset) { if (ctx.profile.need_gather_subpixel_offset) {
coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords); coords = ImageGatherSubpixelOffset(ctx, info, TextureImage(ctx, info, index), coords);
} }
const Id color{Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, return Emit(&EmitContext::OpImageSparseDrefGather, &EmitContext::OpImageDrefGather, ctx, inst,
ctx, inst, result_type, Texture(ctx, info, index), coords, dref, ctx.F32[4], Texture(ctx, info, index), coords, dref, operands.MaskOptional(),
operands.MaskOptional(), operands.Span())}; operands.Span());
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset, Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id offset,
Id lod, Id ms) { Id lod, Id ms) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
AddOffsetToCoordinates(ctx, info, coords, offset); AddOffsetToCoordinates(ctx, info, coords, offset);
if (info.type == TextureType::Buffer) { if (info.type == TextureType::Buffer) {
lod = Id{}; lod = Id{};
@@ -602,10 +580,8 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c
lod = Id{}; lod = Id{};
} }
const ImageOperands operands(lod, ms); const ImageOperands operands(lod, ms);
const Id color{Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],
result_type, TextureImage(ctx, info, index), coords, TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span());
operands.MaskOptional(), operands.Span())};
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod, Id EmitImageQueryDimensions(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id lod,
@@ -650,17 +626,14 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I
Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords,
Id derivatives, const IR::Value& offset, Id lod_clamp) { Id derivatives, const IR::Value& offset, Id lod_clamp) {
const auto info{inst->Flags<IR::TextureInstInfo>()}; const auto info{inst->Flags<IR::TextureInstInfo>()};
const bool is_integer{IsTextureInteger(ctx, info)};
const Id result_type{is_integer ? ctx.U32[4] : ctx.F32[4]};
const auto operands = info.num_derivatives == 3 const auto operands = info.num_derivatives == 3
? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, ? ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
ctx.Def(offset), {}, lod_clamp) ctx.Def(offset), {}, lod_clamp)
: ImageOperands(ctx, info.has_lod_clamp != 0, derivatives, : ImageOperands(ctx, info.has_lod_clamp != 0, derivatives,
info.num_derivatives, offset, lod_clamp); info.num_derivatives, offset, lod_clamp);
const Id color{Emit(&EmitContext::OpImageSparseSampleExplicitLod, return Emit(&EmitContext::OpImageSparseSampleExplicitLod,
&EmitContext::OpImageSampleExplicitLod, ctx, inst, result_type, &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4],
Texture(ctx, info, index), coords, operands.Mask(), operands.Span())}; Texture(ctx, info, index), coords, operands.Mask(), operands.Span());
return is_integer ? ctx.OpBitcast(ctx.F32[4], color) : color;
} }
Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) { Id EmitImageRead(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords) {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -13,14 +10,7 @@ Id SubgroupScope(EmitContext& ctx) {
return ctx.Const(static_cast<u32>(spv::Scope::Subgroup)); return ctx.Const(static_cast<u32>(spv::Scope::Subgroup));
} }
bool StageSupportsSubgroups(EmitContext& ctx) {
return ctx.profile.SupportsSubgroupStage(ctx.stage);
}
Id GetThreadId(EmitContext& ctx) { Id GetThreadId(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id); return ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id);
} }
@@ -78,9 +68,6 @@ Id GetMaxThreadId(EmitContext& ctx, Id thread_id, Id clamp, Id segmentation_mask
} }
Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) { Id SelectValue(EmitContext& ctx, Id in_range, Id value, Id src_thread_id) {
if (!StageSupportsSubgroups(ctx)) {
return value;
}
return ctx.OpSelect( return ctx.OpSelect(
ctx.U32[1], in_range, ctx.U32[1], in_range,
ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value); ctx.OpGroupNonUniformShuffle(ctx.U32[1], SubgroupScope(ctx), value, src_thread_id), value);
@@ -102,9 +89,6 @@ Id EmitLaneId(EmitContext& ctx) {
} }
Id EmitVoteAll(EmitContext& ctx, Id pred) { Id EmitVoteAll(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return pred;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAll(ctx.U1, SubgroupScope(ctx), pred);
} }
@@ -118,9 +102,6 @@ Id EmitVoteAll(EmitContext& ctx, Id pred) {
} }
Id EmitVoteAny(EmitContext& ctx, Id pred) { Id EmitVoteAny(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return pred;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAny(ctx.U1, SubgroupScope(ctx), pred);
} }
@@ -134,9 +115,6 @@ Id EmitVoteAny(EmitContext& ctx, Id pred) {
} }
Id EmitVoteEqual(EmitContext& ctx, Id pred) { Id EmitVoteEqual(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.true_value;
}
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred); return ctx.OpGroupNonUniformAllEqual(ctx.U1, SubgroupScope(ctx), pred);
} }
@@ -151,9 +129,6 @@ Id EmitVoteEqual(EmitContext& ctx, Id pred) {
} }
Id EmitSubgroupBallot(EmitContext& ctx, Id pred) { Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.OpSelect(ctx.U32[1], pred, ctx.Const(1u), ctx.u32_zero_value);
}
const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)}; const Id ballot{ctx.OpGroupNonUniformBallot(ctx.U32[4], SubgroupScope(ctx), pred)};
if (!ctx.profile.warp_size_potentially_larger_than_guest) { if (!ctx.profile.warp_size_potentially_larger_than_guest) {
return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U); return ctx.OpCompositeExtract(ctx.U32[1], ballot, 0U);
@@ -162,37 +137,22 @@ Id EmitSubgroupBallot(EmitContext& ctx, Id pred) {
} }
Id EmitSubgroupEqMask(EmitContext& ctx) { Id EmitSubgroupEqMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_eq); return LoadMask(ctx, ctx.subgroup_mask_eq);
} }
Id EmitSubgroupLtMask(EmitContext& ctx) { Id EmitSubgroupLtMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return LoadMask(ctx, ctx.subgroup_mask_lt); return LoadMask(ctx, ctx.subgroup_mask_lt);
} }
Id EmitSubgroupLeMask(EmitContext& ctx) { Id EmitSubgroupLeMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_le); return LoadMask(ctx, ctx.subgroup_mask_le);
} }
Id EmitSubgroupGtMask(EmitContext& ctx) { Id EmitSubgroupGtMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.u32_zero_value;
}
return LoadMask(ctx, ctx.subgroup_mask_gt); return LoadMask(ctx, ctx.subgroup_mask_gt);
} }
Id EmitSubgroupGeMask(EmitContext& ctx) { Id EmitSubgroupGeMask(EmitContext& ctx) {
if (!StageSupportsSubgroups(ctx)) {
return ctx.Const(1u);
}
return LoadMask(ctx, ctx.subgroup_mask_ge); return LoadMask(ctx, ctx.subgroup_mask_ge);
} }
@@ -262,7 +222,7 @@ Id EmitShuffleButterfly(EmitContext& ctx, IR::Inst* inst, Id value, Id index, Id
Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) { Id EmitFSwizzleAdd(EmitContext& ctx, Id op_a, Id op_b, Id swizzle) {
const Id three{ctx.Const(3U)}; const Id three{ctx.Const(3U)};
Id mask{GetThreadId(ctx)}; Id mask{ctx.OpLoad(ctx.U32[1], ctx.subgroup_local_invocation_id)};
mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three); mask = ctx.OpBitwiseAnd(ctx.U32[1], mask, three);
mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U)); mask = ctx.OpShiftLeftLogical(ctx.U32[1], mask, ctx.Const(1U));
mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask); mask = ctx.OpShiftRightLogical(ctx.U32[1], swizzle, mask);
@@ -30,7 +30,7 @@ enum class Operation {
Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) { Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {
const spv::ImageFormat format{spv::ImageFormat::Unknown}; const spv::ImageFormat format{spv::ImageFormat::Unknown};
const Id type{desc.is_integer ? ctx.U32[1] : ctx.F32[1]}; const Id type{ctx.F32[1]};
const bool depth{desc.is_depth}; const bool depth{desc.is_depth};
const bool ms{desc.is_multisample}; const bool ms{desc.is_multisample};
switch (desc.type) { switch (desc.type) {
@@ -1126,7 +1126,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
} }
IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types}; IR::Type types{info.used_constant_buffer_types | info.used_indirect_cbuf_types};
if (True(types & IR::Type::U8)) { if (True(types & IR::Type::U8)) {
if (profile.support_int8 && profile.support_uniform_and_storage_buffer_8bit) { if (profile.support_int8) {
DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8)); DefineConstBuffers(*this, info, &UniformDefinitions::U8, binding, U8, 'u', sizeof(u8));
DefineConstBuffers(*this, info, &UniformDefinitions::S8, binding, S8, 's', sizeof(s8)); DefineConstBuffers(*this, info, &UniformDefinitions::S8, binding, S8, 's', sizeof(s8));
} else { } else {
@@ -1134,7 +1134,7 @@ void EmitContext::DefineConstantBuffers(const Info& info, u32& binding) {
} }
} }
if (True(types & IR::Type::U16)) { if (True(types & IR::Type::U16)) {
if (profile.support_int16 && profile.support_uniform_and_storage_buffer_16bit) { if (profile.support_int16) {
DefineConstBuffers(*this, info, &UniformDefinitions::U16, binding, U16, 'u', DefineConstBuffers(*this, info, &UniformDefinitions::U16, binding, U16, 'u',
sizeof(u16)); sizeof(u16));
DefineConstBuffers(*this, info, &UniformDefinitions::S16, binding, S16, 's', DefineConstBuffers(*this, info, &UniformDefinitions::S16, binding, S16, 's',
@@ -1196,18 +1196,10 @@ void EmitContext::DefineConstantBufferIndirectFunctions(const Info& info) {
IR::Type types{info.used_indirect_cbuf_types}; IR::Type types{info.used_indirect_cbuf_types};
bool supports_aliasing = profile.support_descriptor_aliasing; bool supports_aliasing = profile.support_descriptor_aliasing;
if (supports_aliasing && True(types & IR::Type::U8)) { if (supports_aliasing && True(types & IR::Type::U8)) {
if (profile.support_int8 && profile.support_uniform_and_storage_buffer_8bit) { load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
load_const_func_u8 = make_accessor(U8, &UniformDefinitions::U8);
} else {
types |= IR::Type::U32;
}
} }
if (supports_aliasing && True(types & IR::Type::U16)) { if (supports_aliasing && True(types & IR::Type::U16)) {
if (profile.support_int16 && profile.support_uniform_and_storage_buffer_16bit) { load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
load_const_func_u16 = make_accessor(U16, &UniformDefinitions::U16);
} else {
types |= IR::Type::U32;
}
} }
if (supports_aliasing && True(types & IR::Type::F32)) { if (supports_aliasing && True(types & IR::Type::F32)) {
load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32); load_const_func_f32 = make_accessor(F32[1], &UniformDefinitions::F32);
@@ -1383,7 +1375,6 @@ void EmitContext::DefineTextures(const Info& info, u32& binding, u32& scaling_in
.image_type = image_type, .image_type = image_type,
.count = desc.count, .count = desc.count,
.is_multisample = desc.is_multisample, .is_multisample = desc.is_multisample,
.is_integer = desc.is_integer,
}); });
if (profile.supported_spirv >= 0x00010400) { if (profile.supported_spirv >= 0x00010400) {
interfaces.push_back(id); interfaces.push_back(id);
@@ -1447,7 +1438,7 @@ void EmitContext::DefineInputs(const IR::Program& program) {
if (info.uses_is_helper_invocation) { if (info.uses_is_helper_invocation) {
is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation); is_helper_invocation = DefineInput(*this, U1, false, spv::BuiltIn::HelperInvocation);
} }
if (info.uses_subgroup_mask && profile.SupportsSubgroupStage(stage)) { if (info.uses_subgroup_mask) {
subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR); subgroup_mask_eq = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupEqMaskKHR);
subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR); subgroup_mask_lt = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLtMaskKHR);
subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR); subgroup_mask_le = DefineInput(*this, U32[4], false, spv::BuiltIn::SubgroupLeMaskKHR);
@@ -1461,10 +1452,9 @@ void EmitContext::DefineInputs(const IR::Program& program) {
Decorate(subgroup_mask_ge, spv::Decoration::Flat); Decorate(subgroup_mask_ge, spv::Decoration::Flat);
} }
} }
if ((info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles || if (info.uses_fswzadd || info.uses_subgroup_invocation_id || info.uses_subgroup_shuffles ||
(profile.warp_size_potentially_larger_than_guest && (profile.warp_size_potentially_larger_than_guest &&
(info.uses_subgroup_vote || info.uses_subgroup_mask))) && (info.uses_subgroup_vote || info.uses_subgroup_mask))) {
profile.SupportsSubgroupStage(stage)) {
AddCapability(spv::Capability::GroupNonUniform); AddCapability(spv::Capability::GroupNonUniform);
subgroup_local_invocation_id = subgroup_local_invocation_id =
DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId); DefineInput(*this, U32[1], false, spv::BuiltIn::SubgroupLocalInvocationId);
@@ -42,7 +42,6 @@ struct TextureDefinition {
Id image_type; Id image_type;
u32 count; u32 count;
bool is_multisample; bool is_multisample;
bool is_integer;
}; };
struct TextureBufferDefinition { struct TextureBufferDefinition {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -46,10 +43,6 @@ union TextureInstInfo {
BitField<25, 2, u32> num_derivatives; BitField<25, 2, u32> num_derivatives;
BitField<27, 3, ImageFormat> image_format; BitField<27, 3, ImageFormat> image_format;
BitField<30, 1, u32> ndv_is_active; BitField<30, 1, u32> ndv_is_active;
/// Only meaningful for the sampled-texture family (ImageSampleImplicitLod, ImageFetch,
/// ImageGather, etc.); unused/zero for storage-image opcodes, which carry their own
/// is_integer via ImageDescriptor/ImageBufferDescriptor instead.
BitField<31, 1, u32> is_integer;
}; };
static_assert(sizeof(TextureInstInfo) <= sizeof(u32)); static_assert(sizeof(TextureInstInfo) <= sizeof(u32));
@@ -236,11 +236,8 @@ void LowerGeometryPassthrough(const IR::Program& program, const HostTranslateInf
IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) { Environment& env, Flow::CFG& cfg, const HostTranslateInfo& host_info) {
HostTranslateInfo normalized_host_info{host_info};
normalized_host_info.ApplyDescriptorLimitPolicy();
IR::Program program; IR::Program program;
program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, normalized_host_info); program.syntax_list = BuildASL(inst_pool, block_pool, env, cfg, host_info);
program.blocks = GenerateBlocks(program.syntax_list); program.blocks = GenerateBlocks(program.syntax_list);
program.post_order_blocks = PostOrder(program.syntax_list.front()); program.post_order_blocks = PostOrder(program.syntax_list.front());
program.stage = env.ShaderStage(); program.stage = env.ShaderStage();
@@ -263,9 +260,9 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0; program.info.passthrough.mask[i] = ((mask[i / 32] >> (i % 32)) & 1) == 0;
} }
if (!normalized_host_info.support_geometry_shader_passthrough) { if (!host_info.support_geometry_shader_passthrough) {
program.output_vertices = GetOutputTopologyVertices(program.output_topology); program.output_vertices = GetOutputTopologyVertices(program.output_topology);
LowerGeometryPassthrough(program, normalized_host_info); LowerGeometryPassthrough(program, host_info);
} }
} }
break; break;
@@ -280,16 +277,16 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
RemoveUnreachableBlocks(program); RemoveUnreachableBlocks(program);
// Replace instructions before the SSA rewrite // Replace instructions before the SSA rewrite
if (!normalized_host_info.support_float64) { if (!host_info.support_float64) {
Optimization::LowerFp64ToFp32(program); Optimization::LowerFp64ToFp32(program);
} }
if (!normalized_host_info.support_float16) { if (!host_info.support_float16) {
Optimization::LowerFp16ToFp32(program); Optimization::LowerFp16ToFp32(program);
} }
if (!normalized_host_info.support_int64) { if (!host_info.support_int64) {
Optimization::LowerInt64ToInt32(program); Optimization::LowerInt64ToInt32(program);
} }
if (!normalized_host_info.support_conditional_barrier) { if (!host_info.support_conditional_barrier) {
Optimization::ConditionalBarrierPass(program); Optimization::ConditionalBarrierPass(program);
} }
Optimization::SsaRewritePass(program); Optimization::SsaRewritePass(program);
@@ -298,8 +295,8 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::PositionPass(env, program); Optimization::PositionPass(env, program);
Optimization::GlobalMemoryToStorageBufferPass(program, normalized_host_info); Optimization::GlobalMemoryToStorageBufferPass(program, host_info);
Optimization::TexturePass(env, program, normalized_host_info); Optimization::TexturePass(env, program, host_info);
if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) { if (Settings::values.resolution_info.active || Settings::values.rescale_hack.GetValue()) {
Optimization::RescalingPass(program); Optimization::RescalingPass(program);
@@ -309,7 +306,7 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo
Optimization::VerificationPass(program); Optimization::VerificationPass(program);
} }
Optimization::CollectShaderInfoPass(env, program); Optimization::CollectShaderInfoPass(env, program);
Optimization::LayerPass(program, normalized_host_info); Optimization::LayerPass(program, host_info);
Optimization::VendorWorkaroundPass(program); Optimization::VendorWorkaroundPass(program);
CollectInterpolationInfo(env, program); CollectInterpolationInfo(env, program);
+4 -38
View File
@@ -6,8 +6,6 @@
#pragma once #pragma once
#include "common/common_types.h"
namespace Shader { namespace Shader {
// Try to keep entries here to a minimum // Try to keep entries here to a minimum
@@ -15,52 +13,20 @@ namespace Shader {
/// Misc information about the host /// Misc information about the host
struct HostTranslateInfo { struct HostTranslateInfo {
static constexpr u32 DEFAULT_DESCRIPTOR_LIMIT = 1024;
u64 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
u32 max_per_stage_descriptor_sampled_images{}; ///< maximum sampled descriptors per stage
u32 max_per_stage_resources{}; ///< maximum resources per stage
u32 max_descriptor_set_samplers{};
u32 max_descriptor_set_uniform_buffers{};
u32 max_descriptor_set_uniform_buffers_dynamic{};
u32 max_descriptor_set_storage_buffers{};
u32 max_descriptor_set_storage_buffers_dynamic{};
u32 max_descriptor_set_sampled_images{};
u32 max_descriptor_set_storage_images{};
u32 max_descriptor_set_input_attachements{};
bool support_float64{}; ///< True when the device supports 64-bit floats bool support_float64{}; ///< True when the device supports 64-bit floats
bool support_float16{}; ///< True when the device supports 16-bit floats bool support_float16{}; ///< True when the device supports 16-bit floats
bool support_int64{}; ///< True when the device supports 64-bit integers bool support_int64{}; ///< True when the device supports 64-bit integers
bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered bool needs_demote_reorder{}; ///< True when the device needs DemoteToHelperInvocation reordered
bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers bool support_snorm_render_buffer{}; ///< True when the device supports SNORM render buffers
bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS bool support_viewport_index_layer{}; ///< True when the device supports gl_Layer in VS
u32 min_ssbo_alignment{}; ///< Minimum alignment supported by the device for SSBOs
u32 max_per_stage_descriptor_sampled_images{1024}; ///< maximum sampled descriptors per stage
u32 max_per_stage_resources{4096}; ///< maximum resources per stage
u32 max_descriptor_set_sampled_images{1024}; ///< maximum sampled descriptors per set
bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry bool support_geometry_shader_passthrough{}; ///< True when the device supports geometry
///< passthrough shaders ///< passthrough shaders
bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional bool support_conditional_barrier{}; ///< True when the device supports barriers in conditional
///< control flow ///< control flow
void ApplyDescriptorLimitPolicy() noexcept {
if (min_ssbo_alignment == 0) {
min_ssbo_alignment = 1;
}
ApplyDescriptorLimitFallback(max_per_stage_descriptor_sampled_images);
ApplyDescriptorLimitFallback(max_per_stage_resources);
ApplyDescriptorLimitFallback(max_descriptor_set_samplers);
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers);
ApplyDescriptorLimitFallback(max_descriptor_set_uniform_buffers_dynamic);
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers);
ApplyDescriptorLimitFallback(max_descriptor_set_storage_buffers_dynamic);
ApplyDescriptorLimitFallback(max_descriptor_set_sampled_images);
ApplyDescriptorLimitFallback(max_descriptor_set_storage_images);
ApplyDescriptorLimitFallback(max_descriptor_set_input_attachements);
}
private:
static void ApplyDescriptorLimitFallback(u32& limit) noexcept {
if (limit == 0) {
limit = DEFAULT_DESCRIPTOR_LIMIT;
}
}
}; };
} // namespace Shader } // namespace Shader
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
@@ -569,8 +569,6 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::ImageRead: { case IR::Opcode::ImageRead: {
const auto flags{inst.Flags<IR::TextureInstInfo>()}; const auto flags{inst.Flags<IR::TextureInstInfo>()};
info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless; info.uses_typeless_image_reads |= flags.image_format == ImageFormat::Typeless;
info.uses_image_1d |=
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
info.uses_sparse_residency |= info.uses_sparse_residency |=
inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr; inst.GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp) != nullptr;
break; break;
@@ -579,8 +577,6 @@ void VisitUsages(Info& info, IR::Inst& inst) {
const auto flags{inst.Flags<IR::TextureInstInfo>()}; const auto flags{inst.Flags<IR::TextureInstInfo>()};
info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless; info.uses_typeless_image_writes |= flags.image_format == ImageFormat::Typeless;
info.uses_image_buffers |= flags.type == TextureType::Buffer; info.uses_image_buffers |= flags.type == TextureType::Buffer;
info.uses_image_1d |=
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
break; break;
} }
case IR::Opcode::SubgroupEqMask: case IR::Opcode::SubgroupEqMask:
@@ -765,13 +761,9 @@ void VisitUsages(Info& info, IR::Inst& inst) {
case IR::Opcode::ImageAtomicAnd32: case IR::Opcode::ImageAtomicAnd32:
case IR::Opcode::ImageAtomicOr32: case IR::Opcode::ImageAtomicOr32:
case IR::Opcode::ImageAtomicXor32: case IR::Opcode::ImageAtomicXor32:
case IR::Opcode::ImageAtomicExchange32: { case IR::Opcode::ImageAtomicExchange32:
const auto flags{inst.Flags<IR::TextureInstInfo>()};
info.uses_atomic_image_u32 = true; info.uses_atomic_image_u32 = true;
info.uses_image_1d |=
flags.type == TextureType::Color1D || flags.type == TextureType::ColorArray1D;
break; break;
}
default: default:
break; break;
} }
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -548,7 +545,7 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program, const HostTranslateIn
IR::Block* const block{storage_inst.block}; IR::Block* const block{storage_inst.block};
IR::Inst* const inst{storage_inst.inst}; IR::Inst* const inst{storage_inst.inst};
const IR::U32 offset{ const IR::U32 offset{
StorageOffset(*block, *inst, storage_buffer, u32(host_info.min_ssbo_alignment))}; StorageOffset(*block, *inst, storage_buffer, host_info.min_ssbo_alignment)};
Replace(*block, *inst, index, offset); Replace(*block, *inst, index, offset);
} }
} }
+72 -85
View File
@@ -32,62 +32,71 @@ struct TextureInst {
using TextureInstVector = boost::container::small_vector<TextureInst, 24>; using TextureInstVector = boost::container::small_vector<TextureInst, 24>;
constexpr u32 DESCRIPTOR_SIZE = 8; constexpr u32 DESCRIPTOR_SIZE = 8;
constexpr u32 DESCRIPTOR_SIZE_SHIFT = u32(std::countr_zero(DESCRIPTOR_SIZE)); constexpr u32 DESCRIPTOR_SIZE_SHIFT = static_cast<u32>(std::countr_zero(DESCRIPTOR_SIZE));
constexpr u32 DESCRIPTOR_MAX_COUNT = 1024; constexpr u32 DYNAMIC_DESCRIPTOR_CBUF_BYTES = 16 * 1024;
constexpr u32 MAX_DYNAMIC_DESCRIPTOR_COUNT = 1024;
u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) { u32 DynamicDescriptorSizeShift(const IR::U32& dynamic_offset) {
const IR::Inst* const inst = dynamic_offset.InstRecursive(); const IR::Inst* const inst{dynamic_offset.InstRecursive()};
if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32) if (!inst || inst->GetOpcode() != IR::Opcode::ShiftLeftLogical32) {
return DESCRIPTOR_SIZE_SHIFT; return DESCRIPTOR_SIZE_SHIFT;
const IR::Value shift = inst->Arg(1); }
if (!shift.IsImmediate()) const IR::Value shift{inst->Arg(1)};
if (!shift.IsImmediate()) {
return DESCRIPTOR_SIZE_SHIFT; return DESCRIPTOR_SIZE_SHIFT;
const u32 size_shift = shift.U32(); }
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift : DESCRIPTOR_SIZE_SHIFT; const u32 size_shift{shift.U32()};
return size_shift >= DESCRIPTOR_SIZE_SHIFT && size_shift < 31 ? size_shift
: DESCRIPTOR_SIZE_SHIFT;
} }
u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift, u32 max_descriptors) { u32 DynamicDescriptorCount(u32 base_offset, u32 size_shift) {
auto const descriptor_limit = (std::max)(1U, max_descriptors); if (size_shift >= 31 || base_offset >= DYNAMIC_DESCRIPTOR_CBUF_BYTES) {
auto const max_cbuf_bytes = 16 * descriptor_limit;
if (size_shift >= 31 || base_offset >= max_cbuf_bytes)
return 1; return 1;
auto const stride = 1U << size_shift; }
auto const available = max_cbuf_bytes - base_offset; const u32 stride{1U << size_shift};
if (available < DESCRIPTOR_SIZE) const u32 available{DYNAMIC_DESCRIPTOR_CBUF_BYTES - base_offset};
if (available < DESCRIPTOR_SIZE) {
return 1; return 1;
auto const available_count = 1U + (available - DESCRIPTOR_SIZE) / stride; }
return std::min(descriptor_limit, available_count); const u32 available_count{1U + (available - DESCRIPTOR_SIZE) / stride};
return std::min(MAX_DYNAMIC_DESCRIPTOR_COUNT, available_count);
} }
u32 SaturatingSub(u32 lhs, u32 rhs) { u32 SaturatingSub(u32 lhs, u32 rhs) {
return lhs > rhs ? lhs - rhs : 0; return lhs > rhs ? lhs - rhs : 0;
} }
template <typename T> template <typename Descriptors>
[[nodiscard]] u32 StaticDescriptorCount(T const& descriptors) noexcept { u32 StaticDescriptorCount(const Descriptors& descriptors) {
return std::accumulate(descriptors.cbegin(), descriptors.cend(), 0U, [](auto const& acc, auto const& e) { u32 count{};
return acc + (e.count <= 1 ? e.count : 0); for (const auto& desc : descriptors) {
}); if (desc.count <= 1) {
count += desc.count;
}
}
return count;
} }
u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info, u32 dynamic_arrays) { u32 DynamicSampledTextureCap(const Info& info, const HostTranslateInfo& host_info,
auto const sampled_limit = (std::max)(1U, std::min(host_info.max_per_stage_descriptor_sampled_images, u32 dynamic_arrays) {
host_info.max_descriptor_set_sampled_images)); if (dynamic_arrays == 0) {
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources); return MAX_DYNAMIC_DESCRIPTOR_COUNT;
if (dynamic_arrays > 0) {
auto const sampled_static_count = StaticDescriptorCount(info.texture_buffer_descriptors) + StaticDescriptorCount(info.texture_descriptors);
auto const resource_static_count =
NumDescriptors(info.constant_buffer_descriptors)
+ NumDescriptors(info.storage_buffers_descriptors)
+ sampled_static_count + NumDescriptors(info.image_buffer_descriptors)
+ NumDescriptors(info.image_descriptors);
auto const sampled_budget = SaturatingSub(sampled_limit, sampled_static_count);
auto const resource_budget = SaturatingSub(resource_limit, resource_static_count);
auto const sampled_cap = sampled_budget / dynamic_arrays;
auto const resource_cap = resource_budget / dynamic_arrays;
return (std::max)(1U, (std::min)(sampled_cap, resource_cap));
} }
return (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit}); const u32 sampled_static_count{StaticDescriptorCount(info.texture_buffer_descriptors) +
StaticDescriptorCount(info.texture_descriptors)};
const u32 resource_static_count{
NumDescriptors(info.constant_buffer_descriptors) +
NumDescriptors(info.storage_buffers_descriptors) + sampled_static_count +
NumDescriptors(info.image_buffer_descriptors) + NumDescriptors(info.image_descriptors)};
const u32 sampled_limit{std::min(host_info.max_per_stage_descriptor_sampled_images,
host_info.max_descriptor_set_sampled_images)};
const u32 sampled_budget{SaturatingSub(sampled_limit, sampled_static_count)};
const u32 resource_budget{SaturatingSub(host_info.max_per_stage_resources,
resource_static_count)};
const u32 sampled_cap{sampled_budget / dynamic_arrays};
const u32 resource_cap{resource_budget / dynamic_arrays};
return std::max(1U, std::min({MAX_DYNAMIC_DESCRIPTOR_COUNT, sampled_cap, resource_cap}));
} }
IR::Opcode IndexedInstruction(const IR::Inst& inst) { IR::Opcode IndexedInstruction(const IR::Inst& inst) {
@@ -295,23 +304,21 @@ static inline bool IsTexturePixelFormatIntegerCached(Environment& env,
} }
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info); std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env);
static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env, const HostTranslateInfo& host_info) { static inline std::optional<ConstBufferAddr> TrackCached(const IR::Value& v, Environment& env) {
if (const IR::Inst* key = v.InstRecursive()) { if (const IR::Inst* key = v.InstRecursive()) {
if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second; if (auto it = env.track_cache.find(key); it != env.track_cache.end()) return it->second;
auto found = Track(v, env, host_info); auto found = Track(v, env);
if (found) env.track_cache.emplace(key, *found); if (found) env.track_cache.emplace(key, *found);
return found; return found;
} }
return Track(v, env, host_info); return Track(v, env);
} }
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info); std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env);
std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env, const HostTranslateInfo& host_info) { std::optional<ConstBufferAddr> Track(const IR::Value& value, Environment& env) {
return IR::BreadthFirstSearch(value, [&env, &host_info](const IR::Inst* inst) { return IR::BreadthFirstSearch(value, [&env](const IR::Inst* inst) { return TryGetConstBuffer(inst, env); });
return TryGetConstBuffer(inst, env, host_info);
});
} }
std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) { std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
@@ -335,13 +342,13 @@ std::optional<u32> TryGetConstant(IR::Value& value, Environment& env) {
return ReadCbufCached(env, index_number, offset_number); return ReadCbufCached(env, index_number, offset_number);
} }
std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env, const HostTranslateInfo& host_info) { std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environment& env) {
switch (inst->GetOpcode()) { switch (inst->GetOpcode()) {
default: default:
return std::nullopt; return std::nullopt;
case IR::Opcode::BitwiseOr32: { case IR::Opcode::BitwiseOr32: {
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)}; std::optional lhs{TrackCached(inst->Arg(0), env)};
std::optional rhs{TrackCached(inst->Arg(1), env, host_info)}; std::optional rhs{TrackCached(inst->Arg(1), env)};
if (!lhs || !rhs) { if (!lhs || !rhs) {
return std::nullopt; return std::nullopt;
} }
@@ -371,11 +378,12 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
if (!shift.IsImmediate()) { if (!shift.IsImmediate()) {
return std::nullopt; return std::nullopt;
} }
std::optional lhs{TrackCached(inst->Arg(0), env, host_info)}; std::optional lhs{TrackCached(inst->Arg(0), env)};
if (lhs) { if (lhs) {
lhs->shift_left = shift.U32(); lhs->shift_left = shift.U32();
} }
return lhs; return lhs;
break;
} }
case IR::Opcode::BitwiseAnd32: { case IR::Opcode::BitwiseAnd32: {
IR::Value op1{inst->Arg(0)}; IR::Value op1{inst->Arg(0)};
@@ -399,7 +407,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
return std::nullopt; return std::nullopt;
} while (false); } while (false);
} }
std::optional lhs{TrackCached(op1, env, host_info)}; std::optional lhs{TrackCached(op1, env)};
if (lhs) { if (lhs) {
lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32())); lhs->shift_left = static_cast<u32>(std::countr_zero(op2.U32()));
} }
@@ -445,10 +453,7 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
} else { } else {
return std::nullopt; return std::nullopt;
} }
auto const size_shift = DynamicDescriptorSizeShift(dynamic_offset); const u32 size_shift{DynamicDescriptorSizeShift(dynamic_offset)};
auto const sampled_limit = (std::max)(1U, (std::min)(host_info.max_per_stage_descriptor_sampled_images,
host_info.max_descriptor_set_sampled_images));
auto const resource_limit = (std::max)(1U, host_info.max_per_stage_resources);
return ConstBufferAddr{ return ConstBufferAddr{
.index = index.U32(), .index = index.U32(),
.offset = base_offset, .offset = base_offset,
@@ -457,15 +462,15 @@ std::optional<ConstBufferAddr> TryGetConstBuffer(const IR::Inst* inst, Environme
.secondary_offset = 0, .secondary_offset = 0,
.secondary_shift_left = 0, .secondary_shift_left = 0,
.dynamic_offset = dynamic_offset, .dynamic_offset = dynamic_offset,
.count = DynamicDescriptorCount(base_offset, size_shift, (std::min)({DESCRIPTOR_MAX_COUNT, sampled_limit, resource_limit})), .count = DynamicDescriptorCount(base_offset, size_shift),
.has_secondary = false, .has_secondary = false,
}; };
} }
TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst, const HostTranslateInfo& host_info) { TextureInst MakeInst(Environment& env, IR::Block* block, IR::Inst& inst) {
ConstBufferAddr addr; ConstBufferAddr addr;
if (IsBindless(inst)) { if (IsBindless(inst)) {
const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env, host_info)}; const std::optional<ConstBufferAddr> track_addr{TrackCached(inst.Arg(0), env)};
if (!track_addr) { if (!track_addr) {
throw NotImplementedException("Failed to track bindless texture constant buffer"); throw NotImplementedException("Failed to track bindless texture constant buffer");
@@ -501,15 +506,15 @@ u32 GetTextureHandle(Environment& env, const ConstBufferAddr& cbuf) {
return lhs_raw | rhs_raw; return lhs_raw | rhs_raw;
} }
[[maybe_unused]] TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) { [[maybe_unused]]TextureType ReadTextureType(Environment& env, const ConstBufferAddr& cbuf) {
return env.ReadTextureType(GetTextureHandle(env, cbuf)); return env.ReadTextureType(GetTextureHandle(env, cbuf));
} }
[[maybe_unused]] TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) { [[maybe_unused]]TexturePixelFormat ReadTexturePixelFormat(Environment& env, const ConstBufferAddr& cbuf) {
return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf)); return env.ReadTexturePixelFormat(GetTextureHandle(env, cbuf));
} }
[[maybe_unused]] bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) { [[maybe_unused]]bool IsTexturePixelFormatInteger(Environment& env, const ConstBufferAddr& cbuf) {
return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf)); return env.IsTexturePixelFormatInteger(GetTextureHandle(env, cbuf));
} }
@@ -562,7 +567,6 @@ public:
})}; })};
// TODO: Read this from TIC // TODO: Read this from TIC
texture_descriptors[index].is_multisample |= desc.is_multisample; texture_descriptors[index].is_multisample |= desc.is_multisample;
texture_descriptors[index].is_integer |= desc.is_integer;
return index; return index;
} }
@@ -671,7 +675,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
if (!IsTextureInstruction(inst)) { if (!IsTextureInstruction(inst)) {
continue; continue;
} }
to_replace.push_back(MakeInst(env, block, inst, host_info)); to_replace.push_back(MakeInst(env, block, inst));
} }
} }
// Sort instructions to visit textures by constant buffer index, then by offset // Sort instructions to visit textures by constant buffer index, then by offset
@@ -685,22 +689,8 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
program.info.texture_descriptors, program.info.texture_descriptors,
program.info.image_descriptors, program.info.image_descriptors,
}; };
const u32 sampled_dynamic_cap = DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace)); const u32 sampled_dynamic_cap{
bool has_last_is_integer{false}; DynamicSampledTextureCap(program.info, host_info, DynamicSampledTextureArrayCount(to_replace))};
u32 last_cbuf_index{};
u32 last_cbuf_offset{};
bool last_is_integer{false};
const auto is_texture_pixel_format_integer{[&](const ConstBufferAddr& cbuf_addr) {
if (has_last_is_integer && last_cbuf_index == cbuf_addr.index &&
last_cbuf_offset == cbuf_addr.offset) {
return last_is_integer;
}
last_is_integer = IsTexturePixelFormatIntegerCached(env, cbuf_addr);
last_cbuf_index = cbuf_addr.index;
last_cbuf_offset = cbuf_addr.offset;
has_last_is_integer = true;
return last_is_integer;
}};
for (TextureInst& texture_inst : to_replace) { for (TextureInst& texture_inst : to_replace) {
// TODO: Handle arrays // TODO: Handle arrays
IR::Inst* const inst{texture_inst.inst}; IR::Inst* const inst{texture_inst.inst};
@@ -765,7 +755,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
} }
const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead}; const bool is_written{inst->GetOpcode() != IR::Opcode::ImageRead};
const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite}; const bool is_read{inst->GetOpcode() != IR::Opcode::ImageWrite};
const bool is_integer{is_texture_pixel_format_integer(cbuf)}; const bool is_integer{IsTexturePixelFormatIntegerCached(env, cbuf)};
if (flags.type == TextureType::Buffer) { if (flags.type == TextureType::Buffer) {
index = descriptors.Add(ImageBufferDescriptor{ index = descriptors.Add(ImageBufferDescriptor{
.format = flags.image_format, .format = flags.image_format,
@@ -807,12 +797,10 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
}); });
} else { } else {
count = std::min(count, sampled_dynamic_cap); count = std::min(count, sampled_dynamic_cap);
const bool is_integer{is_texture_pixel_format_integer(cbuf)};
index = descriptors.Add(TextureDescriptor{ index = descriptors.Add(TextureDescriptor{
.type = flags.type, .type = flags.type,
.is_depth = flags.is_depth != 0, .is_depth = flags.is_depth != 0,
.is_multisample = is_multisample, .is_multisample = is_multisample,
.is_integer = is_integer,
.has_secondary = cbuf.has_secondary, .has_secondary = cbuf.has_secondary,
.cbuf_index = cbuf.index, .cbuf_index = cbuf.index,
.cbuf_offset = cbuf.offset, .cbuf_offset = cbuf.offset,
@@ -823,7 +811,6 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo
.count = count, .count = count,
.size_shift = size_shift, .size_shift = size_shift,
}); });
flags.is_integer.Assign(is_integer ? 1 : 0);
} }
break; break;
} }
+1 -9
View File
@@ -10,16 +10,12 @@
namespace Shader { namespace Shader {
enum class Stage : u32;
struct Profile { struct Profile {
u32 supported_spirv{0x00010000}; u32 supported_spirv{0x00010000};
bool unified_descriptor_binding{}; bool unified_descriptor_binding{};
bool support_descriptor_aliasing{}; bool support_descriptor_aliasing{};
bool support_int8{}; bool support_int8{};
bool support_uniform_and_storage_buffer_8bit{};
bool support_int16{}; bool support_int16{};
bool support_uniform_and_storage_buffer_16bit{};
bool support_int64{}; bool support_int64{};
bool support_vertex_instance_id{}; bool support_vertex_instance_id{};
bool support_float_controls{}; bool support_float_controls{};
@@ -34,7 +30,6 @@ struct Profile {
bool support_fp64_signed_zero_nan_preserve{}; bool support_fp64_signed_zero_nan_preserve{};
bool support_explicit_workgroup_layout{}; bool support_explicit_workgroup_layout{};
bool support_vote{}; bool support_vote{};
u32 supported_subgroup_stages{0x7F};
bool support_viewport_index_layer_non_geometry{}; bool support_viewport_index_layer_non_geometry{};
bool support_viewport_mask{}; bool support_viewport_mask{};
bool support_typeless_image_loads{}; bool support_typeless_image_loads{};
@@ -97,11 +92,8 @@ struct Profile {
bool has_broken_robust{}; bool has_broken_robust{};
u64 min_ssbo_alignment{}; u64 min_ssbo_alignment{};
u32 max_user_clip_distances{};
bool SupportsSubgroupStage(Stage stage) const { u32 max_user_clip_distances{};
return (supported_subgroup_stages & (1u << static_cast<u32>(stage))) != 0;
}
}; };
} // namespace Shader } // namespace Shader
-1
View File
@@ -209,7 +209,6 @@ struct TextureDescriptor {
TextureType type; TextureType type;
bool is_depth; bool is_depth;
bool is_multisample; bool is_multisample;
bool is_integer;
bool has_secondary; bool has_secondary;
u32 cbuf_index; u32 cbuf_index;
u32 cbuf_offset; u32 cbuf_offset;
+2 -27
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -18,19 +15,12 @@ static constexpr auto PERMS = Common::MemoryPermission::ReadWrite;
static constexpr auto HEAP = false; static constexpr auto HEAP = false;
TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") { TEST_CASE("HostMemory: Initialize and deinitialize", "[common]") {
{ { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); { HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); }
REQUIRE(mem.BackingBasePointer() != nullptr);
}
{
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
}
} }
TEST_CASE("HostMemory: Simple map", "[common]") { TEST_CASE("HostMemory: Simple map", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP); mem.Map(0x5000, 0x8000, 0x1000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000; volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
@@ -40,7 +30,6 @@ TEST_CASE("HostMemory: Simple map", "[common]") {
TEST_CASE("HostMemory: Simple mirror map", "[common]") { TEST_CASE("HostMemory: Simple mirror map", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP); mem.Map(0x8000, 0x4000, 0x1000, PERMS, HEAP);
@@ -52,7 +41,6 @@ TEST_CASE("HostMemory: Simple mirror map", "[common]") {
TEST_CASE("HostMemory: Simple unmap", "[common]") { TEST_CASE("HostMemory: Simple unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000; volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
@@ -64,7 +52,6 @@ TEST_CASE("HostMemory: Simple unmap", "[common]") {
TEST_CASE("HostMemory: Simple unmap and remap", "[common]") { TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP); mem.Map(0x5000, 0x3000, 0x2000, PERMS, HEAP);
volatile u8* const data = mem.VirtualBasePointer() + 0x5000; volatile u8* const data = mem.VirtualBasePointer() + 0x5000;
@@ -82,7 +69,6 @@ TEST_CASE("HostMemory: Simple unmap and remap", "[common]") {
TEST_CASE("HostMemory: Nieche allocation", "[common]") { TEST_CASE("HostMemory: Nieche allocation", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x0000, 0, 0x20000, PERMS, HEAP); mem.Map(0x0000, 0, 0x20000, PERMS, HEAP);
mem.Unmap(0x0000, 0x4000, HEAP); mem.Unmap(0x0000, 0x4000, HEAP);
mem.Map(0x1000, 0, 0x2000, PERMS, HEAP); mem.Map(0x1000, 0, 0x2000, PERMS, HEAP);
@@ -92,7 +78,6 @@ TEST_CASE("HostMemory: Nieche allocation", "[common]") {
TEST_CASE("HostMemory: Full unmap", "[common]") { TEST_CASE("HostMemory: Full unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x8000, 0x4000, HEAP); mem.Unmap(0x8000, 0x4000, HEAP);
mem.Map(0x6000, 0, 0x16000, PERMS, HEAP); mem.Map(0x6000, 0, 0x16000, PERMS, HEAP);
@@ -100,7 +85,6 @@ TEST_CASE("HostMemory: Full unmap", "[common]") {
TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") { TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x2000, 0x4000, HEAP); mem.Unmap(0x2000, 0x4000, HEAP);
mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP); mem.Map(0x2000, 0x80000, 0x4000, PERMS, HEAP);
@@ -108,7 +92,6 @@ TEST_CASE("HostMemory: Right out of bounds unmap", "[common]") {
TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") { TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x6000, 0x4000, HEAP); mem.Unmap(0x6000, 0x4000, HEAP);
mem.Map(0x8000, 0, 0x2000, PERMS, HEAP); mem.Map(0x8000, 0, 0x2000, PERMS, HEAP);
@@ -116,7 +99,6 @@ TEST_CASE("HostMemory: Left out of bounds unmap", "[common]") {
TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") { TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP); mem.Map(0x4000, 0, 0x1b000, PERMS, HEAP);
mem.Unmap(0x3000, 0x1c000, HEAP); mem.Unmap(0x3000, 0x1c000, HEAP);
@@ -125,7 +107,6 @@ TEST_CASE("HostMemory: Multiple placeholder unmap", "[common]") {
TEST_CASE("HostMemory: Unmap between placeholders", "[common]") { TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x0000, 0, 0x4000, PERMS, HEAP); mem.Map(0x0000, 0, 0x4000, PERMS, HEAP);
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x2000, 0x4000, HEAP); mem.Unmap(0x2000, 0x4000, HEAP);
@@ -134,7 +115,6 @@ TEST_CASE("HostMemory: Unmap between placeholders", "[common]") {
TEST_CASE("HostMemory: Unmap to origin", "[common]") { TEST_CASE("HostMemory: Unmap to origin", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x4000, 0x4000, HEAP); mem.Unmap(0x4000, 0x4000, HEAP);
@@ -144,7 +124,6 @@ TEST_CASE("HostMemory: Unmap to origin", "[common]") {
TEST_CASE("HostMemory: Unmap to right", "[common]") { TEST_CASE("HostMemory: Unmap to right", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0, 0x4000, PERMS, HEAP); mem.Map(0x4000, 0, 0x4000, PERMS, HEAP);
mem.Map(0x8000, 0, 0x4000, PERMS, HEAP); mem.Map(0x8000, 0, 0x4000, PERMS, HEAP);
mem.Unmap(0x8000, 0x4000, HEAP); mem.Unmap(0x8000, 0x4000, HEAP);
@@ -153,7 +132,6 @@ TEST_CASE("HostMemory: Unmap to right", "[common]") {
TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") { TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
@@ -166,7 +144,6 @@ TEST_CASE("HostMemory: Partial right unmap check bindings", "[common]") {
TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") { TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
@@ -181,7 +158,6 @@ TEST_CASE("HostMemory: Partial left unmap check bindings", "[common]") {
TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") { TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP); mem.Map(0x4000, 0x10000, 0x4000, PERMS, HEAP);
volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000; volatile u8* const ptr = mem.VirtualBasePointer() + 0x4000;
@@ -196,7 +172,6 @@ TEST_CASE("HostMemory: Partial middle unmap check bindings", "[common]") {
TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") { TEST_CASE("HostMemory: Partial sparse middle unmap and check bindings", "[common]") {
HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE); HostMemory mem(BACKING_SIZE, VIRTUAL_SIZE);
REQUIRE(mem.BackingBasePointer() != nullptr);
mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP); mem.Map(0x4000, 0x10000, 0x2000, PERMS, HEAP);
mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP); mem.Map(0x6000, 0x20000, 0x2000, PERMS, HEAP);
+39 -35
View File
@@ -20,54 +20,58 @@ Decoder::Decoder(Host1x::Host1x& host1x_, s32 id_, const Host1x::NvdecCommon::Nv
Decoder::~Decoder() = default; Decoder::~Decoder() = default;
void Decoder::SetFrameDimensions(s32 width, s32 height) {
if (width <= 0 || height <= 0) {
frame_dimensions.reset();
return;
}
frame_dimensions = FFmpeg::FrameDimensions{width, height};
}
void Decoder::Decode() { void Decoder::Decode() {
if (!initialized) { if (!initialized) {
return; return;
} }
const auto packet_data = ComposeFrame(); const auto packet_data = ComposeFrame();
FFmpeg::FrameOffsets offsets{};
offsets.hidden = vp9_hidden_frame;
offsets.interlaced = IsInterlaced();
if (offsets.interlaced) {
std::tie(offsets.luma, offsets.luma_bottom, std::ignore, std::ignore) =
GetInterlacedOffsets();
} else {
std::tie(offsets.luma, std::ignore) = GetProgressiveOffsets();
}
// Send assembled bitstream to decoder. // Send assembled bitstream to decoder.
if (!decode_api.SendPacket(packet_data, offsets, GetFrameDimensions())) { if (!decode_api.SendPacket(packet_data)) {
return; return;
} }
auto push = [&](u64 luma, std::shared_ptr<FFmpeg::Frame> frame) { // Only receive/store visible frames.
if (UsingDecodeOrder()) { if (vp9_hidden_frame) {
host1x.frame_queue.PushDecodeOrder(id, luma, std::move(frame)); return;
} else { }
host1x.frame_queue.PushPresentOrder(id, luma, std::move(frame));
}
};
while (auto result = decode_api.ReceiveFrame()) { // Receive output frames from decoder.
auto& [frame, frame_offsets] = *result; auto frame = decode_api.ReceiveFrame();
if (!frame) {
continue; if (!frame) {
return;
}
if (IsInterlaced()) {
auto [luma_top, luma_bottom, chroma_top, chroma_bottom] = GetInterlacedOffsets();
auto frame_copy = frame;
if (!frame.get()) {
LOG_ERROR(HW_GPU,
"Nvdec {} failed to decode interlaced frame for top {:#X} bottom 0x{:X}", id,
luma_top, luma_bottom);
} }
if (frame_offsets.interlaced) {
auto frame_copy = frame; if (UsingDecodeOrder()) {
push(frame_offsets.luma, std::move(frame)); host1x.frame_queue.PushDecodeOrder(id, luma_top, std::move(frame));
push(frame_offsets.luma_bottom, std::move(frame_copy)); host1x.frame_queue.PushDecodeOrder(id, luma_bottom, std::move(frame_copy));
} else { } else {
push(frame_offsets.luma, std::move(frame)); host1x.frame_queue.PushPresentOrder(id, luma_top, std::move(frame));
host1x.frame_queue.PushPresentOrder(id, luma_bottom, std::move(frame_copy));
}
} else {
auto [luma_offset, chroma_offset] = GetProgressiveOffsets();
if (!frame.get()) {
LOG_ERROR(HW_GPU, "Nvdec {} failed to decode progressive frame for luma {:#X}", id,
luma_offset);
}
if (UsingDecodeOrder()) {
host1x.frame_queue.PushDecodeOrder(id, luma_offset, std::move(frame));
} else {
host1x.frame_queue.PushPresentOrder(id, luma_offset, std::move(frame));
} }
} }
} }
-8
View File
@@ -10,7 +10,6 @@
#include <mutex> #include <mutex>
#include <optional> #include <optional>
#include <string_view> #include <string_view>
#include <tuple>
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
#include <queue> #include <queue>
@@ -47,19 +46,12 @@ protected:
virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0; virtual std::tuple<u64, u64, u64, u64> GetInterlacedOffsets() = 0;
virtual bool IsInterlaced() = 0; virtual bool IsInterlaced() = 0;
void SetFrameDimensions(s32 width, s32 height);
std::optional<FFmpeg::FrameDimensions> GetFrameDimensions() const {
return frame_dimensions;
}
FFmpeg::DecodeApi decode_api; FFmpeg::DecodeApi decode_api;
Host1x::Host1x& host1x; Host1x::Host1x& host1x;
const Host1x::NvdecCommon::NvdecRegisters& regs; const Host1x::NvdecCommon::NvdecRegisters& regs;
s32 id; s32 id;
bool initialized : 1 = false; bool initialized : 1 = false;
bool vp9_hidden_frame : 1 = false; bool vp9_hidden_frame : 1 = false;
std::optional<FFmpeg::FrameDimensions> frame_dimensions;
}; };
} // namespace Tegra } // namespace Tegra
-4
View File
@@ -52,10 +52,6 @@ bool H264::IsInterlaced() {
std::span<const u8> H264::ComposeFrame() { std::span<const u8> H264::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(H264DecoderContext)); host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(H264DecoderContext));
const auto& params = current_context.h264_parameter_set;
SetFrameDimensions(static_cast<s32>(params.pic_width_in_mbs) * 16,
static_cast<s32>(params.frame_height_in_mbs) * 16);
const s64 frame_number = current_context.h264_parameter_set.frame_number.Value(); const s64 frame_number = current_context.h264_parameter_set.frame_number.Value();
if (!is_first_frame && frame_number != 0) { if (!is_first_frame && frame_number != 0) {
frame_scratch.resize_destructive(current_context.stream_len); frame_scratch.resize_destructive(current_context.stream_len);
-1
View File
@@ -35,7 +35,6 @@ std::tuple<u64, u64, u64, u64> VP8::GetInterlacedOffsets() {
std::span<const u8> VP8::ComposeFrame() { std::span<const u8> VP8::ComposeFrame() {
host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(VP8PictureInfo)); host1x.gmmu_manager.ReadBlock(regs.picture_info_offset.Address(), &current_context, sizeof(VP8PictureInfo));
SetFrameDimensions(current_context.frame_width, current_context.frame_height);
const bool is_key_frame = current_context.key_frame == 1u; const bool is_key_frame = current_context.key_frame == 1u;
const auto bitstream_size = size_t(current_context.vld_buffer_size); const auto bitstream_size = size_t(current_context.vld_buffer_size);
-1
View File
@@ -841,7 +841,6 @@ std::span<const u8> VP9::ComposeFrame() {
{ {
Vp9FrameContainer curr_frame = GetCurrentFrame(); Vp9FrameContainer curr_frame = GetCurrentFrame();
current_frame_info = curr_frame.info; current_frame_info = curr_frame.info;
SetFrameDimensions(current_frame_info.frame_size.width, current_frame_info.frame_size.height);
bitstream = std::move(curr_frame.bit_stream); bitstream = std::move(curr_frame.bit_stream);
} }
// The uncompressed header routine sets PrevProb parameters needed for the compressed header // The uncompressed header routine sets PrevProb parameters needed for the compressed header
+10 -159
View File
@@ -4,10 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <cstring>
#include <string_view>
#include <vector>
#include "common/assert.h" #include "common/assert.h"
#include "common/logging.h" #include "common/logging.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
@@ -88,52 +84,6 @@ std::string AVError(int errnum) {
return errbuf; return errbuf;
} }
#if defined(__ANDROID__)
size_t FindNalStartCode(std::span<const u8> data, size_t i) {
const size_t n = data.size();
if (i + 3 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 0 && data[i + 3] == 1) {
return 4;
}
if (i + 2 < n && data[i] == 0 && data[i + 1] == 0 && data[i + 2] == 1) {
return 3;
}
return 0;
}
std::vector<u8> ExtractH264ParameterSetExtradata(std::span<const u8> packet) {
std::vector<u8> extradata;
const size_t size = packet.size();
size_t i = 0;
while (i < size) {
const size_t sc = FindNalStartCode(packet, i);
if (sc == 0) {
++i;
continue;
}
const size_t nal_start = i + sc;
if (nal_start >= size) {
break;
}
const u8 nal_type = packet[nal_start] & 0x1F;
size_t j = nal_start + 1;
while (j < size && FindNalStartCode(packet, j) == 0) {
++j;
}
if (nal_type == 7 || nal_type == 8) {
constexpr u8 start[4] = {0, 0, 0, 1};
extradata.insert(extradata.end(), start, start + sizeof(start));
extradata.insert(extradata.end(), packet.begin() + nal_start, packet.begin() + j);
} else if (nal_type == 1 || nal_type == 5) {
break;
}
i = j;
}
return extradata;
}
#endif
} }
Packet::Packet(std::span<const u8> data) { Packet::Packet(std::span<const u8> data) {
@@ -168,24 +118,7 @@ Decoder::Decoder(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
return AV_CODEC_ID_NONE; return AV_CODEC_ID_NONE;
} }
}(); }();
m_codec = avcodec_find_decoder(av_codec);
#if defined(__ANDROID__)
if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
const char* mc_name = nullptr;
switch (av_codec) {
case AV_CODEC_ID_H264: mc_name = "h264_mediacodec"; break;
case AV_CODEC_ID_VP8: mc_name = "vp8_mediacodec"; break;
case AV_CODEC_ID_VP9: mc_name = "vp9_mediacodec"; break;
default: break;
}
if (mc_name) {
m_codec = avcodec_find_decoder_by_name(mc_name);
}
}
#endif
if (!m_codec) {
m_codec = avcodec_find_decoder(av_codec);
}
} }
bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const { bool Decoder::SupportsDecodingOnDevice(AVPixelFormat* out_pix_fmt, AVHWDeviceType type) const {
@@ -281,10 +214,6 @@ DecoderContext::DecoderContext(const Decoder& decoder) : m_decoder{decoder} {
av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0); av_opt_set(m_codec_context->priv_data, "tune", "zerolatency", 0);
m_codec_context->thread_count = 0; m_codec_context->thread_count = 0;
m_codec_context->thread_type &= ~FF_THREAD_FRAME; m_codec_context->thread_type &= ~FF_THREAD_FRAME;
#if defined(__ANDROID__)
m_codec_context->flags |= AV_CODEC_FLAG_LOW_DELAY;
m_codec_context->flags2 |= AV_CODEC_FLAG2_FAST;
#endif
} }
DecoderContext::~DecoderContext() { DecoderContext::~DecoderContext() {
@@ -298,25 +227,15 @@ void DecoderContext::InitializeHardwareDecoder(const HardwareContext& context, A
m_codec_context->pix_fmt = hw_pix_fmt; m_codec_context->pix_fmt = hw_pix_fmt;
} }
bool DecoderContext::OpenContext(const Decoder& decoder, std::span<const u8> extradata) { bool DecoderContext::OpenContext(const Decoder& decoder) {
if (!extradata.empty()) {
av_freep(&m_codec_context->extradata);
m_codec_context->extradata = static_cast<u8*>(
av_mallocz(extradata.size() + AV_INPUT_BUFFER_PADDING_SIZE));
if (!m_codec_context->extradata) {
LOG_ERROR(HW_GPU, "Failed to allocate extradata");
return false;
}
std::memcpy(m_codec_context->extradata, extradata.data(), extradata.size());
m_codec_context->extradata_size = static_cast<int>(extradata.size());
}
if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) { if (const int ret = avcodec_open2(m_codec_context, decoder.GetCodec(), nullptr); ret < 0) {
LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret)); LOG_ERROR(HW_GPU, "avcodec_open2 error: {}", AVError(ret));
return false; return false;
} }
LOG_INFO(HW_GPU, "Using decoder {}", decoder.GetCodec()->name); if (!m_codec_context->hw_device_ctx) {
LOG_INFO(HW_GPU, "Using FFmpeg CPU decoder");
}
return true; return true;
} }
@@ -362,13 +281,6 @@ void DecodeApi::Reset() {
m_hardware_context.reset(); m_hardware_context.reset();
m_decoder_context.reset(); m_decoder_context.reset();
m_decoder.reset(); m_decoder.reset();
m_opened = false;
m_defer_android_mediacodec_open = false;
m_needs_h264_extradata = false;
m_next_pts = 0;
while (!m_pending_offsets.empty()) {
m_pending_offsets.pop();
}
} }
bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) { bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
@@ -376,90 +288,29 @@ bool DecodeApi::Initialize(Tegra::Host1x::NvdecCommon::VideoCodec codec) {
m_decoder.emplace(codec); m_decoder.emplace(codec);
m_decoder_context.emplace(*m_decoder); m_decoder_context.emplace(*m_decoder);
bool is_mediacodec = false;
#if defined(__ANDROID__)
const std::string_view decoder_name = m_decoder->GetCodec() ? m_decoder->GetCodec()->name : "";
is_mediacodec = decoder_name == "h264_mediacodec" ||
decoder_name == "vp8_mediacodec" ||
decoder_name == "vp9_mediacodec";
#endif
// Enable GPU decoding if requested. // Enable GPU decoding if requested.
if (!is_mediacodec && if (Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
Settings::values.nvdec_emulation.GetValue() == Settings::NvdecEmulation::Gpu) {
m_hardware_context.emplace(); m_hardware_context.emplace();
m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder); m_hardware_context->InitializeForDecoder(*m_decoder_context, *m_decoder);
} }
#if defined(__ANDROID__)
m_defer_android_mediacodec_open = is_mediacodec;
m_needs_h264_extradata = decoder_name == "h264_mediacodec";
if (m_defer_android_mediacodec_open) {
return true;
}
#endif
// Open the decoder context. // Open the decoder context.
if (!m_decoder_context->OpenContext(*m_decoder)) { if (!m_decoder_context->OpenContext(*m_decoder)) {
this->Reset(); this->Reset();
return false; return false;
} }
m_opened = true;
return true; return true;
} }
bool DecodeApi::SendPacket(std::span<const u8> packet_data, const FrameOffsets& offsets, bool DecodeApi::SendPacket(std::span<const u8> packet_data) {
std::optional<FrameDimensions> dimensions) {
if (!m_opened) {
std::vector<u8> extradata;
#if defined(__ANDROID__)
if (m_defer_android_mediacodec_open) {
if (!dimensions) {
return true;
}
auto* ctx = m_decoder_context->GetCodecContext();
ctx->width = dimensions->width;
ctx->height = dimensions->height;
ctx->coded_width = dimensions->width;
ctx->coded_height = dimensions->height;
}
if (m_needs_h264_extradata) {
extradata = ExtractH264ParameterSetExtradata(packet_data);
if (extradata.empty()) {
return true;
}
}
#endif
if (!m_decoder_context->OpenContext(*m_decoder, extradata)) {
this->Reset();
return false;
}
m_opened = true;
}
if (!offsets.hidden) {
m_pending_offsets.push(offsets);
}
FFmpeg::Packet packet(packet_data); FFmpeg::Packet packet(packet_data);
packet.GetPacket()->pts = m_next_pts;
packet.GetPacket()->dts = m_next_pts;
++m_next_pts;
return m_decoder_context->SendPacket(packet); return m_decoder_context->SendPacket(packet);
} }
std::optional<DecodeApi::DecodedFrame> DecodeApi::ReceiveFrame() { std::shared_ptr<Frame> DecodeApi::ReceiveFrame() {
auto frame = m_decoder_context->ReceiveFrame(); // Receive raw frame from decoder.
if (!frame) { return m_decoder_context->ReceiveFrame();
return std::nullopt;
}
FrameOffsets offsets{};
if (!m_pending_offsets.empty()) {
offsets = m_pending_offsets.front();
m_pending_offsets.pop();
}
return DecodedFrame{std::move(frame), offsets};
} }
} }
+3 -26
View File
@@ -179,7 +179,7 @@ public:
~DecoderContext(); ~DecoderContext();
void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt); void InitializeHardwareDecoder(const HardwareContext& context, AVPixelFormat hw_pix_fmt);
bool OpenContext(const Decoder& decoder, std::span<const u8> extradata = {}); bool OpenContext(const Decoder& decoder);
bool SendPacket(const Packet& packet); bool SendPacket(const Packet& packet);
std::shared_ptr<Frame> ReceiveFrame(); std::shared_ptr<Frame> ReceiveFrame();
@@ -198,18 +198,6 @@ private:
bool m_decode_order{}; bool m_decode_order{};
}; };
struct FrameOffsets {
bool interlaced{};
bool hidden{};
u64 luma{};
u64 luma_bottom{};
};
struct FrameDimensions {
s32 width{};
s32 height{};
};
class DecodeApi { class DecodeApi {
public: public:
YUZU_NON_COPYABLE(DecodeApi); YUZU_NON_COPYABLE(DecodeApi);
@@ -225,24 +213,13 @@ public:
return m_decoder_context->UsingDecodeOrder(); return m_decoder_context->UsingDecodeOrder();
} }
bool SendPacket(std::span<const u8> packet_data, const FrameOffsets& offsets, bool SendPacket(std::span<const u8> packet_data);
std::optional<FrameDimensions> dimensions = std::nullopt); std::shared_ptr<Frame> ReceiveFrame();
struct DecodedFrame {
std::shared_ptr<Frame> frame;
FrameOffsets offsets;
};
std::optional<DecodedFrame> ReceiveFrame();
private: private:
std::optional<FFmpeg::Decoder> m_decoder; std::optional<FFmpeg::Decoder> m_decoder;
std::optional<FFmpeg::DecoderContext> m_decoder_context; std::optional<FFmpeg::DecoderContext> m_decoder_context;
std::optional<FFmpeg::HardwareContext> m_hardware_context; std::optional<FFmpeg::HardwareContext> m_hardware_context;
bool m_opened{};
bool m_defer_android_mediacodec_open{};
bool m_needs_h264_extradata{};
s64 m_next_pts{};
std::queue<FrameOffsets> m_pending_offsets;
}; };
} // namespace FFmpeg } // namespace FFmpeg
-1
View File
@@ -31,7 +31,6 @@ Nvdec::Nvdec(Host1x& host1x_, s32 id_, u32 syncpt)
Nvdec::~Nvdec() { Nvdec::~Nvdec() {
LOG_INFO(HW_GPU, "Destroying nvdec {}", id); LOG_INFO(HW_GPU, "Destroying nvdec {}", id);
host1x.frame_queue.Close(id);
} }
void Nvdec::ProcessMethod(u32 method, u32 argument) { void Nvdec::ProcessMethod(u32 method, u32 argument) {
+1 -8
View File
@@ -118,7 +118,6 @@ void Vic::Execute() noexcept {
output_surface.resize(output_width * output_height); output_surface.resize(output_width * output_height);
if (Settings::values.nvdec_emulation.GetValue() != Settings::NvdecEmulation::Off) { if (Settings::values.nvdec_emulation.GetValue() != Settings::NvdecEmulation::Off) {
bool decoded_frame = false;
for (size_t i = 0; i < config.slot_structs.size(); i++) { for (size_t i = 0; i < config.slot_structs.size(); i++) {
if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) { if (auto& slot_config = config.slot_structs[i]; slot_config.config.slot_enable) {
auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address(); auto const luma_offset = regs.surfaces[i][SurfaceIndex::Current].luma.Address();
@@ -137,17 +136,11 @@ void Vic::Execute() noexcept {
break; break;
} }
Blend(config, slot_config, config.output_surface_config.out_pixel_format); Blend(config, slot_config, config.output_surface_config.out_pixel_format);
decoded_frame = true;
} else { } else {
LOG_TRACE(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset); LOG_ERROR(HW_GPU, "Vic {} failed to get frame with offset {:#X}", id, luma_offset);
} }
} }
} }
if (decoded_frame) {
has_decoded_frame = true;
} else if (!has_decoded_frame) {
return;
}
} else { } else {
// Fill the frame with black, as otherwise they can have random data and be very glitchy. // Fill the frame with black, as otherwise they can have random data and be very glitchy.
std::fill(output_surface.begin(), output_surface.end(), Pixel{}); std::fill(output_surface.begin(), output_surface.end(), Pixel{});
-1
View File
@@ -628,7 +628,6 @@ private:
s32 id; s32 id;
s32 nvdec_id{-1}; s32 nvdec_id{-1};
bool has_decoded_frame{};
u32 syncpoint; u32 syncpoint;
}; };
+6 -288
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -43,11 +40,7 @@ layout(binding = BINDING_INPUT_BUFFER, std430) readonly restrict buffer InputBuf
uvec4 astc_data[]; uvec4 astc_data[];
}; };
#ifdef VULKAN
layout(binding = BINDING_OUTPUT_IMAGE) uniform writeonly restrict image2DArray dest_image;
#else
layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly restrict image2DArray dest_image; layout(binding = BINDING_OUTPUT_IMAGE, rgba8) uniform writeonly restrict image2DArray dest_image;
#endif
const uint GOB_SIZE_X_SHIFT = 6; const uint GOB_SIZE_X_SHIFT = 6;
const uint GOB_SIZE_Y_SHIFT = 3; const uint GOB_SIZE_Y_SHIFT = 3;
@@ -596,163 +589,6 @@ ivec4 BlueContract(int a, int r, int g, int b) {
return ivec4(a, (r + b) >> 1, (g + b) >> 1, b); return ivec4(a, (r + b) >> 1, (g + b) >> 1, b);
} }
bool IsHDRColorEndpointMode(uint cem) {
return cem == 2u || cem == 3u || cem == 7u || cem == 11u || cem == 14u || cem == 15u;
}
// Sign-extends the low nbits of value (a 2's complement field packed into the bottom of an
// otherwise-unsigned integer), per C.2.15's HDR endpoint bitfield unpacking.
int SignExtend(int value, uint nbits) {
int sign_bit = 1 << (nbits - 1u);
return (value ^ sign_bit) - sign_bit;
}
// HDR Endpoint Mode 7 (C.2.15): base RGB + scale factor.
void DecodeHDREndpointMode7(uint v0, uint v1, uint v2, uint v3, out ivec3 e0, out ivec3 e1) {
uint modeval = ((v0 & 0xC0u) >> 6u) | ((v1 & 0x80u) >> 5u) | ((v2 & 0x80u) >> 4u);
uint majcomp;
uint mode;
if ((modeval & 0xCu) != 0xCu) {
majcomp = modeval >> 2u;
mode = modeval & 3u;
} else if (modeval != 0xFu) {
majcomp = modeval & 3u;
mode = 4u;
} else {
majcomp = 0u;
mode = 5u;
}
int red = int(v0 & 0x3Fu);
int green = int(v1 & 0x1Fu);
int blue = int(v2 & 0x1Fu);
int scale = int(v3 & 0x1Fu);
uint x0 = (v1 >> 6u) & 1u;
uint x1 = (v1 >> 5u) & 1u;
uint x2 = (v2 >> 6u) & 1u;
uint x3 = (v2 >> 5u) & 1u;
uint x4 = (v3 >> 7u) & 1u;
uint x5 = (v3 >> 6u) & 1u;
uint x6 = (v3 >> 5u) & 1u;
uint ohm = 1u << mode;
if ((ohm & 0x30u) != 0u) green |= int(x0 << 6u);
if ((ohm & 0x3Au) != 0u) green |= int(x1 << 5u);
if ((ohm & 0x30u) != 0u) blue |= int(x2 << 6u);
if ((ohm & 0x3Au) != 0u) blue |= int(x3 << 5u);
if ((ohm & 0x3Du) != 0u) scale |= int(x6 << 5u);
if ((ohm & 0x2Du) != 0u) scale |= int(x5 << 6u);
if ((ohm & 0x04u) != 0u) scale |= int(x4 << 7u);
if ((ohm & 0x3Bu) != 0u) red |= int(x4 << 6u);
if ((ohm & 0x04u) != 0u) red |= int(x3 << 6u);
if ((ohm & 0x10u) != 0u) red |= int(x5 << 7u);
if ((ohm & 0x0Fu) != 0u) red |= int(x2 << 7u);
if ((ohm & 0x05u) != 0u) red |= int(x1 << 8u);
if ((ohm & 0x0Au) != 0u) red |= int(x0 << 8u);
if ((ohm & 0x05u) != 0u) red |= int(x0 << 9u);
if ((ohm & 0x02u) != 0u) red |= int(x6 << 9u);
if ((ohm & 0x01u) != 0u) red |= int(x3 << 10u);
if ((ohm & 0x02u) != 0u) red |= int(x5 << 10u);
int shamts[6] = int[](1, 1, 2, 3, 4, 5);
int shamt = shamts[mode];
red <<= shamt;
green <<= shamt;
blue <<= shamt;
scale <<= shamt;
if (mode != 5u) {
green = red - green;
blue = red - blue;
}
if (majcomp == 1u) {
int t = red; red = green; green = t;
}
if (majcomp == 2u) {
int t = red; red = blue; blue = t;
}
e1 = ivec3(clamp(red, 0, 0xFFF), clamp(green, 0, 0xFFF), clamp(blue, 0, 0xFFF));
e0 = ivec3(clamp(red - scale, 0, 0xFFF), clamp(green - scale, 0, 0xFFF),
clamp(blue - scale, 0, 0xFFF));
}
// HDR Endpoint Mode 11 (C.2.15): direct RGB pair. Shared by modes 11, 14 and 15, which all
// decode their RGB the same way and only differ in how alpha is filled in.
void DecodeHDREndpointMode11(uint v0, uint v1, uint v2, uint v3, uint v4, uint v5, out ivec3 e0,
out ivec3 e1) {
uint majcomp = ((v4 & 0x80u) >> 7u) | ((v5 & 0x80u) >> 6u);
if (majcomp == 3u) {
e0 = ivec3(int(v0 << 4u), int(v2 << 4u), int((v4 & 0x7Fu) << 5u));
e1 = ivec3(int(v1 << 4u), int(v3 << 4u), int((v5 & 0x7Fu) << 5u));
return;
}
uint mode = ((v1 & 0x80u) >> 7u) | ((v2 & 0x80u) >> 6u) | ((v3 & 0x80u) >> 5u);
int va = int(v0 | ((v1 & 0x40u) << 2u));
int vb0 = int(v2 & 0x3Fu);
int vb1 = int(v3 & 0x3Fu);
int vc = int(v1 & 0x3Fu);
int vd0 = int(v4 & 0x7Fu);
int vd1 = int(v5 & 0x7Fu);
int dbitstab[8] = int[](7, 6, 7, 6, 5, 6, 5, 6);
vd0 = SignExtend(vd0, uint(dbitstab[mode]));
vd1 = SignExtend(vd1, uint(dbitstab[mode]));
uint x0 = (v2 >> 6u) & 1u;
uint x1 = (v3 >> 6u) & 1u;
uint x2 = (v4 >> 6u) & 1u;
uint x3 = (v5 >> 6u) & 1u;
uint x4 = (v4 >> 5u) & 1u;
uint x5 = (v5 >> 5u) & 1u;
uint ohm = 1u << mode;
if ((ohm & 0xA4u) != 0u) va |= int(x0 << 9u);
if ((ohm & 0x08u) != 0u) va |= int(x2 << 9u);
if ((ohm & 0x50u) != 0u) va |= int(x4 << 9u);
if ((ohm & 0x50u) != 0u) va |= int(x5 << 10u);
if ((ohm & 0xA0u) != 0u) va |= int(x1 << 10u);
if ((ohm & 0xC0u) != 0u) va |= int(x2 << 11u);
if ((ohm & 0x04u) != 0u) vc |= int(x1 << 6u);
if ((ohm & 0xE8u) != 0u) vc |= int(x3 << 6u);
if ((ohm & 0x20u) != 0u) vc |= int(x2 << 7u);
if ((ohm & 0x5Bu) != 0u) vb0 |= int(x0 << 6u);
if ((ohm & 0x5Bu) != 0u) vb1 |= int(x1 << 6u);
if ((ohm & 0x12u) != 0u) vb0 |= int(x2 << 7u);
if ((ohm & 0x12u) != 0u) vb1 |= int(x3 << 7u);
int shamt = (int(mode) >> 1) ^ 3;
va <<= shamt;
vb0 <<= shamt;
vb1 <<= shamt;
vc <<= shamt;
vd0 <<= shamt;
vd1 <<= shamt;
int r1 = clamp(va, 0, 0xFFF);
int g1 = clamp(va - vb0, 0, 0xFFF);
int b1 = clamp(va - vb1, 0, 0xFFF);
int r0 = clamp(va - vc, 0, 0xFFF);
int g0 = clamp(va - vb0 - vc - vd0, 0, 0xFFF);
int b0 = clamp(va - vb1 - vc - vd1, 0, 0xFFF);
if (majcomp == 1u) {
int t;
t = r0; r0 = g0; g0 = t;
t = r1; r1 = g1; g1 = t;
} else if (majcomp == 2u) {
int t;
t = r0; r0 = b0; b0 = t;
t = r1; r1 = b1; b1 = t;
}
e0 = ivec3(r0, g0, b0);
e1 = ivec3(r1, g1, b1);
}
void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, uint color_values[32], void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, uint color_values[32],
inout uint colvals_index) { inout uint colvals_index) {
#define READ_UINT_VALUES(N) \ #define READ_UINT_VALUES(N) \
@@ -879,88 +715,8 @@ void ComputeEndpoints(out uvec4 ep1, out uvec4 ep2, uint color_endpoint_mode, ui
} }
break; break;
} }
case 2: {
READ_UINT_VALUES(2)
uint y0, y1;
if (V[0].y >= V[0].x) {
y0 = V[0].x << 4u;
y1 = V[0].y << 4u;
} else {
y0 = (V[0].y << 4u) + 8u;
y1 = (V[0].x << 4u) - 8u;
}
ep1 = uvec4(0x780u, y0, y0, y0);
ep2 = uvec4(0x780u, y1, y1, y1);
break;
}
case 3: {
READ_UINT_VALUES(2)
uint y0, d;
if ((V[0].x & 0x80u) != 0u) {
y0 = ((V[0].y & 0xE0u) << 4u) | ((V[0].x & 0x7Fu) << 2u);
d = (V[0].y & 0x1Fu) << 2u;
} else {
y0 = ((V[0].y & 0xF0u) << 4u) | ((V[0].x & 0x7Fu) << 1u);
d = (V[0].y & 0x0Fu) << 1u;
}
const uint y1 = min(y0 + d, 0xFFFu);
ep1 = uvec4(0x780u, y0, y0, y0);
ep2 = uvec4(0x780u, y1, y1, y1);
break;
}
case 7: {
READ_UINT_VALUES(4)
ivec3 e0, e1;
DecodeHDREndpointMode7(V[0].x, V[0].y, V[0].z, V[0].w, e0, e1);
ep1 = uvec4(0x780u, uint(e0.x), uint(e0.y), uint(e0.z));
ep2 = uvec4(0x780u, uint(e1.x), uint(e1.y), uint(e1.z));
break;
}
case 11: {
READ_UINT_VALUES(6)
ivec3 e0, e1;
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
ep1 = uvec4(0x780u, uint(e0.x), uint(e0.y), uint(e0.z));
ep2 = uvec4(0x780u, uint(e1.x), uint(e1.y), uint(e1.z));
break;
}
case 14: {
READ_UINT_VALUES(8)
ivec3 e0, e1;
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
// Only HDR mode with LDR (8-bit UNORM)-interpreted alpha; left as-is (0-255).
ep1 = uvec4(V[1].z, uint(e0.x), uint(e0.y), uint(e0.z));
ep2 = uvec4(V[1].w, uint(e1.x), uint(e1.y), uint(e1.z));
break;
}
case 15: {
READ_UINT_VALUES(8)
ivec3 e0, e1;
DecodeHDREndpointMode11(V[0].x, V[0].y, V[0].z, V[0].w, V[1].x, V[1].y, e0, e1);
const uint mode = ((V[1].z >> 7u) & 1u) | ((V[1].w >> 6u) & 2u);
int a6 = int(V[1].z & 0x7Fu);
int a7 = int(V[1].w & 0x7Fu);
int alpha0, alpha1;
if (mode == 3u) {
alpha0 = a6 << 5;
alpha1 = a7 << 5;
} else {
a6 |= (a7 << int(mode + 1u)) & 0x780;
a7 &= int(0x3Fu >> mode);
a7 ^= int(0x20u >> mode);
a7 -= int(0x20u >> mode);
a6 <<= int(4u - mode);
a7 <<= int(4u - mode);
a7 += a6;
alpha0 = a6;
alpha1 = clamp(a7, 0, 0xFFF);
}
ep1 = uvec4(uint(alpha0), uint(e0.x), uint(e0.y), uint(e0.z));
ep2 = uvec4(uint(alpha1), uint(e1.x), uint(e1.y), uint(e1.z));
break;
}
default: { default: {
// Not a valid CEM at all (all 16 values are now handled above). // HDR mode, or more likely a bug computing the color_endpoint_mode
ep1 = uvec4(0xFF, 0xFF, 0, 0); ep1 = uvec4(0xFF, 0xFF, 0, 0);
ep2 = uvec4(0xFF, 0xFF, 0, 0); ep2 = uvec4(0xFF, 0xFF, 0, 0);
break; break;
@@ -1356,51 +1112,13 @@ void DecompressBlock(ivec3 coord) {
if (num_partitions > 1) { if (num_partitions > 1) {
local_partition = Select2DPartition(partition_index, i, j, num_partitions); local_partition = Select2DPartition(partition_index, i, j, num_partitions);
} }
const uint local_cem = color_endpoint_mode[local_partition]; const uvec4 C0 = ReplicateByteTo16(endpoints0[local_partition]);
const uvec4 C1 = ReplicateByteTo16(endpoints1[local_partition]);
const uvec4 weight_vec = GetUnquantizedWeightVector(j, i, size_params, plane_index, dual_plane); const uvec4 weight_vec = GetUnquantizedWeightVector(j, i, size_params, plane_index, dual_plane);
const vec4 Cf =
vec4 p; vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64);
if (IsHDRColorEndpointMode(local_cem)) { const vec4 p = (Cf / 65535.0f);
// Endpoints are raw 12-bit pseudo-logarithmic values; shift left 4 bits to
// become 16-bit before interpolating, per C.2.19.
const uvec4 C0 = endpoints0[local_partition] << 4u;
const uvec4 C1 = endpoints1[local_partition] << 4u;
const uvec4 C = (C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64u;
const uvec4 E = (C & uvec4(0xF800u)) >> 11u;
const uvec4 M = C & uvec4(0x7FFu);
const uvec4 Mt_lo = 3u * M;
const uvec4 Mt_mid = 4u * M - 512u;
const uvec4 Mt_hi = 5u * M - 2048u;
const uvec4 Mt =
mix(Mt_lo, mix(Mt_mid, Mt_hi, greaterThanEqual(M, uvec4(1536u))),
greaterThanEqual(M, uvec4(512u)));
const uvec4 Cf = (E << 10u) + (Mt >> 3u);
// +Inf/NaN clamps to the largest finite FP16 value (0x7BFF).
const uvec4 half_bits = mix(Cf, uvec4(0x7BFFu), greaterThanEqual(Cf, uvec4(0x7C00u)));
p = vec4(unpackHalf2x16(half_bits.x).x, unpackHalf2x16(half_bits.y).x,
unpackHalf2x16(half_bits.z).x, unpackHalf2x16(half_bits.w).x);
// Mode 14 keeps an LDR (8-bit UNORM)-interpreted alpha; component 0 here (A,
// see the ep1/ep2 layout used throughout this file).
if (local_cem == 14u) {
const uint a0 = ReplicateByteTo16(uvec4(endpoints0[local_partition].x)).x;
const uint a1 = ReplicateByteTo16(uvec4(endpoints1[local_partition].x)).x;
const uint Ca = (a0 * (64u - weight_vec.x) + a1 * weight_vec.x + 32u) / 64u;
p.x = float(Ca) / 65535.0f;
}
} else {
const uvec4 C0 = ReplicateByteTo16(endpoints0[local_partition]);
const uvec4 C1 = ReplicateByteTo16(endpoints1[local_partition]);
const vec4 Cf =
vec4((C0 * (uvec4(64) - weight_vec) + C1 * weight_vec + uvec4(32)) / 64);
p = Cf / 65535.0f;
}
#ifdef VULKAN
imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar); imageStore(dest_image, coord + ivec3(i, j, 0), p.gbar);
#else
imageStore(dest_image, coord + ivec3(i, j, 0), clamp(p, 0.0f, 1.0f).gbar);
#endif
} }
} }
} }
@@ -11,8 +11,9 @@
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS }; #define END_PUSH_CONSTANTS };
#define UNIFORM(n) #define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0 #define BINDING_SWIZZLE_BUFFER 0
#define BINDING_OUTPUT_IMAGE 1 #define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 2
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
@@ -25,7 +26,8 @@
#define BEGIN_PUSH_CONSTANTS #define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS #define END_PUSH_CONSTANTS
#define UNIFORM(n) layout (location = n) uniform #define UNIFORM(n) layout (location = n) uniform
#define BINDING_INPUT_BUFFER 0 #define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 0 #define BINDING_OUTPUT_IMAGE 0
#endif #endif
@@ -41,6 +43,10 @@ UNIFORM(6) uint block_height;
UNIFORM(7) uint block_height_mask; UNIFORM(7) uint block_height_mask;
END_PUSH_CONSTANTS END_PUSH_CONSTANTS
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
#if HAS_EXTENDED_TYPES #if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
@@ -65,19 +71,9 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1); const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) { uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK; pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64 + pos.x); return swizzle_table[pos.y * 64 + pos.x];
} }
uvec4 ReadTexel(uint offset) { uvec4 ReadTexel(uint offset) {
@@ -11,8 +11,9 @@
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS }; #define END_PUSH_CONSTANTS };
#define UNIFORM(n) #define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0 #define BINDING_SWIZZLE_BUFFER 0
#define BINDING_OUTPUT_IMAGE 1 #define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 2
#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv
@@ -25,7 +26,8 @@
#define BEGIN_PUSH_CONSTANTS #define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS #define END_PUSH_CONSTANTS
#define UNIFORM(n) layout (location = n) uniform #define UNIFORM(n) layout (location = n) uniform
#define BINDING_INPUT_BUFFER 0 #define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_IMAGE 0 #define BINDING_OUTPUT_IMAGE 0
#endif #endif
@@ -43,6 +45,10 @@ UNIFORM(8) uint block_depth;
UNIFORM(9) uint block_depth_mask; UNIFORM(9) uint block_depth_mask;
END_PUSH_CONSTANTS END_PUSH_CONSTANTS
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
#if HAS_EXTENDED_TYPES #if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
@@ -67,19 +73,9 @@ const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHI
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1); const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1, GOB_SIZE_Y - 1);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
uint SwizzleOffset(uvec2 pos) { uint SwizzleOffset(uvec2 pos) {
pos = pos & SWIZZLE_MASK; pos = pos & SWIZZLE_MASK;
return SwizzleTable(pos.y * 64 + pos.x); return swizzle_table[pos.y * 64 + pos.x];
} }
uvec4 ReadTexel(uint offset) { uvec4 ReadTexel(uint offset) {
@@ -10,8 +10,9 @@
#define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants { #define BEGIN_PUSH_CONSTANTS layout(push_constant) uniform PushConstants {
#define END_PUSH_CONSTANTS }; #define END_PUSH_CONSTANTS };
#define UNIFORM(n) #define UNIFORM(n)
#define BINDING_INPUT_BUFFER 0 #define BINDING_SWIZZLE_BUFFER 0
#define BINDING_OUTPUT_BUFFER 1 #define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_BUFFER 2
#else #else
#extension GL_NV_gpu_shader5 : enable #extension GL_NV_gpu_shader5 : enable
#ifdef GL_NV_gpu_shader5 #ifdef GL_NV_gpu_shader5
@@ -22,6 +23,7 @@
#define BEGIN_PUSH_CONSTANTS #define BEGIN_PUSH_CONSTANTS
#define END_PUSH_CONSTANTS #define END_PUSH_CONSTANTS
#define UNIFORM(n) layout(location = n) uniform #define UNIFORM(n) layout(location = n) uniform
#define BINDING_SWIZZLE_BUFFER 0
#define BINDING_INPUT_BUFFER 1 #define BINDING_INPUT_BUFFER 1
#define BINDING_OUTPUT_BUFFER 0 #define BINDING_OUTPUT_BUFFER 0
#endif #endif
@@ -64,9 +66,13 @@ END_PUSH_CONSTANTS
#endif #endif
// --- Buffers --- // --- Buffers ---
layout(binding = BINDING_SWIZZLE_BUFFER, std430) readonly buffer SwizzleTable {
uint swizzle_table[];
};
#if HAS_EXTENDED_TYPES #if HAS_EXTENDED_TYPES
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU8 { uint8_t u8data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU16 { uint16_t u16data[]; };
#endif #endif
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU32 { uint u32data[]; };
layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; }; layout(binding = BINDING_INPUT_BUFFER, std430) buffer InputBufferU64 { uvec2 u64data[]; };
@@ -90,20 +96,10 @@ const uint GOB_SIZE_Z_SHIFT = 0;
const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT; const uint GOB_SIZE_SHIFT = GOB_SIZE_X_SHIFT + GOB_SIZE_Y_SHIFT + GOB_SIZE_Z_SHIFT;
const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u); const uvec2 SWIZZLE_MASK = uvec2(GOB_SIZE_X - 1u, GOB_SIZE_Y - 1u);
uint SwizzleTable(uint pos) {
const uint t[8] = uint[](
0x12100200, 0x13110301, 0x16140604, 0x17150705,
0x1a180a08, 0x1b190b09, 0x1e1c0e0c, 0x1f1d0f0d
);
const uint i = pos >> 4;
const uint h = (t[i / 4] >> ((i % 4) * 8)) & 0xff;
return (h << 4) | (pos & 0xf);
}
// --- Helpers --- // --- Helpers ---
uint SwizzleOffset(uvec2 pos) { uint SwizzleOffset(uvec2 pos) {
pos &= SWIZZLE_MASK; pos &= SWIZZLE_MASK;
return SwizzleTable(pos.y * 64u + pos.x); return swizzle_table[pos.y * 64u + pos.x];
} }
uvec4 ReadTexel(uint offset) { uvec4 ReadTexel(uint offset) {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -8,11 +5,11 @@
#extension GL_ARB_shader_stencil_export : require #extension GL_ARB_shader_stencil_export : require
layout(binding = 0) uniform sampler2D depth_tex; layout(binding = 0) uniform sampler2D depth_tex;
layout(binding = 1) uniform usampler2D stencil_tex; layout(binding = 1) uniform isampler2D stencil_tex;
layout(location = 0) in vec2 texcoord; layout(location = 0) in vec2 texcoord;
void main() { void main() {
gl_FragDepth = textureLod(depth_tex, texcoord, 0).r; gl_FragDepth = textureLod(depth_tex, texcoord, 0).r;
gl_FragStencilRefARB = int(textureLod(stencil_tex, texcoord, 0).r); gl_FragStencilRefARB = textureLod(stencil_tex, texcoord, 0).r;
} }
@@ -54,7 +54,7 @@ using VideoCommon::LoadPipelines;
using VideoCommon::SerializePipeline; using VideoCommon::SerializePipeline;
using Context = ShaderContext::Context; using Context = ShaderContext::Context;
constexpr u32 CACHE_VERSION = 15; constexpr u32 CACHE_VERSION = 16;
template <typename Container> template <typename Container>
auto MakeSpan(Container& container) { auto MakeSpan(Container& container) {
@@ -245,31 +245,16 @@ ShaderCache::ShaderCache(Tegra::MaxwellDeviceMemoryManager& device_memory_,
std::min<u32>(device.GetMaxUserClipDistances(), Maxwell::Regs::NumClipDistances), std::min<u32>(device.GetMaxUserClipDistances(), Maxwell::Regs::NumClipDistances),
}, },
host_info{ host_info{
.min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()), .support_float64 = true,
.max_per_stage_descriptor_sampled_images = .support_float16 = false,
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT, .support_int64 = device.HasShaderInt64(),
.max_per_stage_resources = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT, .needs_demote_reorder = device.IsAmd(),
.max_descriptor_set_samplers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT, .support_snorm_render_buffer = false,
.max_descriptor_set_uniform_buffers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT, .support_viewport_index_layer = device.HasVertexViewportLayer(),
.max_descriptor_set_uniform_buffers_dynamic = .min_ssbo_alignment = static_cast<u32>(device.GetShaderStorageBufferAlignment()),
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT, .support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
.max_descriptor_set_storage_buffers = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT, .support_conditional_barrier = device.SupportsConditionalBarriers(),
.max_descriptor_set_storage_buffers_dynamic =
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
.max_descriptor_set_sampled_images = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
.max_descriptor_set_storage_images = Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
.max_descriptor_set_input_attachements =
Shader::HostTranslateInfo::DEFAULT_DESCRIPTOR_LIMIT,
.support_float64 = true,
.support_float16 = false,
.support_int64 = device.HasShaderInt64(),
.needs_demote_reorder = device.IsAmd(),
.support_snorm_render_buffer = false,
.support_viewport_index_layer = device.HasVertexViewportLayer(),
.support_geometry_shader_passthrough = device.HasGeometryShaderPassthrough(),
.support_conditional_barrier = device.SupportsConditionalBarriers(),
} { } {
host_info.ApplyDescriptorLimitPolicy();
if (use_asynchronous_shaders) { if (use_asynchronous_shaders) {
workers = CreateWorkers(); workers = CreateWorkers();
} }
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -59,8 +56,10 @@ UtilShaders::UtilShaders(ProgramManager& program_manager_)
copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)), copy_bc4_program(MakeProgram(OPENGL_COPY_BC4_COMP)),
convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)), convert_s8d24_program(MakeProgram(OPENGL_CONVERT_S8D24_COMP)),
convert_ms_to_nonms_program(MakeProgram(CONVERT_MSAA_TO_NON_MSAA_COMP)), convert_ms_to_nonms_program(MakeProgram(CONVERT_MSAA_TO_NON_MSAA_COMP)),
convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP)) convert_nonms_to_ms_program(MakeProgram(CONVERT_NON_MSAA_TO_MSAA_COMP)) {
{ const auto swizzle_table = Tegra::Texture::MakeSwizzleTable();
swizzle_table_buffer.Create();
glNamedBufferStorage(swizzle_table_buffer.handle, sizeof(swizzle_table), &swizzle_table, 0);
} }
UtilShaders::~UtilShaders() = default; UtilShaders::~UtilShaders() = default;
@@ -117,11 +116,13 @@ void UtilShaders::ASTCDecode(Image& image, const StagingBufferMap& map,
void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map, void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
std::span<const SwizzleParameters> swizzles) { std::span<const SwizzleParameters> swizzles) {
static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1}; static constexpr Extent3D WORKGROUP_SIZE{32, 32, 1};
static constexpr GLuint BINDING_INPUT_BUFFER = 0; static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
program_manager.BindComputeProgram(block_linear_unswizzle_2d_program.handle); program_manager.BindComputeProgram(block_linear_unswizzle_2d_program.handle);
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes); glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format)); const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
for (const SwizzleParameters& swizzle : swizzles) { for (const SwizzleParameters& swizzle : swizzles) {
@@ -152,11 +153,14 @@ void UtilShaders::BlockLinearUpload2D(Image& image, const StagingBufferMap& map,
void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map, void UtilShaders::BlockLinearUpload3D(Image& image, const StagingBufferMap& map,
std::span<const SwizzleParameters> swizzles) { std::span<const SwizzleParameters> swizzles) {
static constexpr Extent3D WORKGROUP_SIZE{16, 8, 8}; static constexpr Extent3D WORKGROUP_SIZE{16, 8, 8};
static constexpr GLuint BINDING_INPUT_BUFFER = 0;
static constexpr GLuint BINDING_SWIZZLE_BUFFER = 0;
static constexpr GLuint BINDING_INPUT_BUFFER = 1;
static constexpr GLuint BINDING_OUTPUT_IMAGE = 0; static constexpr GLuint BINDING_OUTPUT_IMAGE = 0;
glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes); glFlushMappedNamedBufferRange(map.buffer, map.offset, image.guest_size_bytes);
program_manager.BindComputeProgram(block_linear_unswizzle_3d_program.handle); program_manager.BindComputeProgram(block_linear_unswizzle_3d_program.handle);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, BINDING_SWIZZLE_BUFFER, swizzle_table_buffer.handle);
const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format)); const GLenum store_format = StoreFormat(BytesPerBlock(image.info.format));
for (const SwizzleParameters& swizzle : swizzles) { for (const SwizzleParameters& swizzle : swizzles) {
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -48,6 +45,9 @@ public:
private: private:
ProgramManager& program_manager; ProgramManager& program_manager;
OGLBuffer swizzle_table_buffer;
OGLProgram astc_decoder_program; OGLProgram astc_decoder_program;
OGLProgram block_linear_unswizzle_2d_program; OGLProgram block_linear_unswizzle_2d_program;
OGLProgram block_linear_unswizzle_3d_program; OGLProgram block_linear_unswizzle_3d_program;
@@ -39,55 +39,6 @@ constexpr std::array POLYGON_OFFSET_ENABLE_LUT = {
POLYGON, // Patches POLYGON, // Patches
}; };
constexpr std::array TOPOLOGY_CLASS_REPRESENTATIVE_LUT = {
Maxwell::PrimitiveTopology::Points, // Points
Maxwell::PrimitiveTopology::Lines, // Lines
Maxwell::PrimitiveTopology::LineLoop, // LineLoop
Maxwell::PrimitiveTopology::LineStrip, // LineStrip
Maxwell::PrimitiveTopology::Triangles, // Triangles
Maxwell::PrimitiveTopology::Triangles, // TriangleStrip
Maxwell::PrimitiveTopology::Triangles, // TriangleFan
Maxwell::PrimitiveTopology::Triangles, // Quads
Maxwell::PrimitiveTopology::Triangles, // QuadStrip
Maxwell::PrimitiveTopology::Triangles, // Polygon
Maxwell::PrimitiveTopology::LinesAdjacency, // LinesAdjacency
Maxwell::PrimitiveTopology::LinesAdjacency, // LineStripAdjacency
Maxwell::PrimitiveTopology::TrianglesAdjacency, // TrianglesAdjacency
Maxwell::PrimitiveTopology::TrianglesAdjacency, // TriangleStripAdjacency
Maxwell::PrimitiveTopology::Patches, // Patches
};
bool IsDualSourceBlendFactor(Maxwell::Blend::Factor factor) {
using F = Maxwell::Blend::Factor;
switch (factor) {
case F::Source1Color_D3D:
case F::OneMinusSource1Color_D3D:
case F::Source1Alpha_D3D:
case F::OneMinusSource1Alpha_D3D:
case F::Source1Color_GL:
case F::OneMinusSource1Color_GL:
case F::Source1Alpha_GL:
case F::OneMinusSource1Alpha_GL:
return true;
default:
return false;
}
}
bool ComputeAttachment0DualSourceBlend(const Maxwell& regs) {
if (!regs.blend.enable[0]) {
return false;
}
const auto uses_dual_source = [](const auto& blend) {
return IsDualSourceBlendFactor(blend.color_source) ||
IsDualSourceBlendFactor(blend.color_dest) ||
IsDualSourceBlendFactor(blend.alpha_source) ||
IsDualSourceBlendFactor(blend.alpha_dest);
};
return regs.blend_per_target_enabled ? uses_dual_source(regs.blend_per_target[0])
: uses_dual_source(regs.blend);
}
void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) { void RefreshXfbState(VideoCommon::TransformFeedbackState& state, const Maxwell& regs) {
std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(), std::ranges::transform(regs.transform_feedback.controls, state.layouts.begin(),
[](const auto& layout) { [](const auto& layout) {
@@ -111,7 +62,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0); extended_dynamic_state_2_logic_op.Assign(features.has_extended_dynamic_state_2_logic_op ? 1 : 0);
extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0); extended_dynamic_state_3_blend.Assign(features.has_extended_dynamic_state_3_blend ? 1 : 0);
extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0); extended_dynamic_state_3_enables.Assign(features.has_extended_dynamic_state_3_enables ? 1 : 0);
color_write_enable_dynamic.Assign(features.has_color_write_enable ? 1 : 0);
dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0); dynamic_vertex_input.Assign(features.has_dynamic_vertex_input ? 1 : 0);
xfb_enabled.Assign(regs.transform_feedback_enabled != 0); xfb_enabled.Assign(regs.transform_feedback_enabled != 0);
ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0); ndc_minus_one_to_one.Assign(regs.depth_mode == Maxwell::DepthMode::MinusOneToOne ? 1 : 0);
@@ -121,13 +71,8 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() == tessellation_clockwise.Assign(regs.tessellation.params.output_primitives.Value() ==
Maxwell::Tessellation::OutputPrimitives::Triangles_CW); Maxwell::Tessellation::OutputPrimitives::Triangles_CW);
patch_control_points_minus_one.Assign(regs.patch_vertices - 1); patch_control_points_minus_one.Assign(regs.patch_vertices - 1);
const bool can_collapse_topology_class = topology.Assign(topology_);
features.has_extended_dynamic_state && features.has_extended_dynamic_state_2;
topology.Assign(can_collapse_topology_class
? TOPOLOGY_CLASS_REPRESENTATIVE_LUT[static_cast<size_t>(topology_)]
: topology_);
msaa_mode.Assign(regs.anti_alias_samples_mode); msaa_mode.Assign(regs.anti_alias_samples_mode);
attachment0_dual_source_blend.Assign(ComputeAttachment0DualSourceBlend(regs) ? 1 : 0);
raw2 = 0; raw2 = 0;
@@ -242,15 +187,6 @@ void FixedPipelineState::Refresh(Tegra::Engines::Maxwell3D& maxwell3d, DynamicFe
maxwell3d.dirty.flags[Dirty::Blending] = false; maxwell3d.dirty.flags[Dirty::Blending] = false;
for (size_t index = 0; index < attachments.size(); ++index) { for (size_t index = 0; index < attachments.size(); ++index) {
attachments[index].Refresh(regs, index); attachments[index].Refresh(regs, index);
auto& attachment = attachments[index];
if (color_write_enable_dynamic && attachment.mask_r == 0 &&
attachment.mask_g == 0 && attachment.mask_b == 0 &&
attachment.mask_a == 0) {
attachment.mask_r.Assign(1);
attachment.mask_g.Assign(1);
attachment.mask_b.Assign(1);
attachment.mask_a.Assign(1);
}
} }
} }
} }
@@ -31,7 +31,6 @@ struct DynamicFeatures {
bool has_dynamic_state3_logic_op_enable; bool has_dynamic_state3_logic_op_enable;
bool has_dynamic_state3_line_stipple_enable; bool has_dynamic_state3_line_stipple_enable;
bool has_dynamic_vertex_input; bool has_dynamic_vertex_input;
bool has_color_write_enable;
bool has_provoking_vertex; bool has_provoking_vertex;
bool has_provoking_vertex_first_mode; bool has_provoking_vertex_first_mode;
bool has_provoking_vertex_last_mode; bool has_provoking_vertex_last_mode;
@@ -209,9 +208,6 @@ struct FixedPipelineState {
BitField<12, 2, u32> tessellation_spacing; BitField<12, 2, u32> tessellation_spacing;
BitField<14, 1, u32> tessellation_clockwise; BitField<14, 1, u32> tessellation_clockwise;
BitField<15, 5, u32> patch_control_points_minus_one; BitField<15, 5, u32> patch_control_points_minus_one;
BitField<20, 1, u32> color_write_enable_dynamic;
BitField<21, 1, u32> attachment0_dual_source_blend;
BitField<24, 4, Maxwell::PrimitiveTopology> topology; BitField<24, 4, Maxwell::PrimitiveTopology> topology;
BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode; BitField<28, 4, Tegra::Texture::MsaaMode> msaa_mode;
@@ -223,10 +223,6 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \ SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \ SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \ SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11_UNORM_BLOCK, 0, EAC_R11_UNORM) \
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11_SNORM_BLOCK, 0, EAC_R11_SNORM) \
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11G11_UNORM_BLOCK, 0, EAC_R11G11_UNORM) \
SURFACE_FORMAT_ELEM(VK_FORMAT_EAC_R11G11_SNORM_BLOCK, 0, EAC_R11G11_SNORM) \
/* Depth formats */ \ /* Depth formats */ \
SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \ SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \
SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \ SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \
@@ -282,17 +278,7 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
} }
} else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) { } else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) {
// Transcode on hardware that doesn't support ETC2 natively // Transcode on hardware that doesn't support ETC2 natively
if (pixel_format == PixelFormat::EAC_R11_SNORM) { tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
tuple.format = VK_FORMAT_R8_SNORM;
} else if (pixel_format == PixelFormat::EAC_R11_UNORM) {
tuple.format = VK_FORMAT_R8_UNORM;
} else if (pixel_format == PixelFormat::EAC_R11G11_SNORM) {
tuple.format = VK_FORMAT_R8G8_SNORM;
} else if (pixel_format == PixelFormat::EAC_R11G11_UNORM) {
tuple.format = VK_FORMAT_R8G8_UNORM;
} else {
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
}
} }
bool const attachable = (tuple.usage & usage_attachable) != 0; bool const attachable = (tuple.usage & usage_attachable) != 0;
bool const storage = (tuple.usage & usage_storage) != 0; bool const storage = (tuple.usage & usage_storage) != 0;
@@ -7,7 +7,6 @@
#pragma once #pragma once
#include <cstddef> #include <cstddef>
#include <optional>
#include <boost/container/small_vector.hpp> #include <boost/container/small_vector.hpp>
@@ -16,7 +15,6 @@
#include "shader_recompiler/shader_info.h" #include "shader_recompiler/shader_info.h"
#include "video_core/renderer_vulkan/vk_texture_cache.h" #include "video_core/renderer_vulkan/vk_texture_cache.h"
#include "video_core/renderer_vulkan/vk_update_descriptor.h" #include "video_core/renderer_vulkan/vk_update_descriptor.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/types.h" #include "video_core/texture_cache/types.h"
#include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_device.h"
@@ -24,38 +22,6 @@ namespace Vulkan {
using Shader::Backend::SPIRV::NUM_TEXTURE_AND_IMAGE_SCALING_WORDS; using Shader::Backend::SPIRV::NUM_TEXTURE_AND_IMAGE_SCALING_WORDS;
[[nodiscard]] inline std::optional<PixelFormat> PixelFormatFromImageFormat(
Shader::ImageFormat format) {
switch (format) {
case Shader::ImageFormat::Typeless:
return std::nullopt;
case Shader::ImageFormat::R8_UINT:
return PixelFormat::R8_UINT;
case Shader::ImageFormat::R8_SINT:
return PixelFormat::R8_SINT;
case Shader::ImageFormat::R16_UINT:
return PixelFormat::R16_UINT;
case Shader::ImageFormat::R16_SINT:
return PixelFormat::R16_SINT;
case Shader::ImageFormat::R32_UINT:
return PixelFormat::R32_UINT;
case Shader::ImageFormat::R32G32_UINT:
return PixelFormat::R32G32_UINT;
case Shader::ImageFormat::R32G32B32A32_UINT:
return PixelFormat::R32G32B32A32_UINT;
}
return std::nullopt;
}
[[nodiscard]] inline u32 NumDescriptorEntries(const Shader::Info& info) {
return Shader::NumDescriptors(info.constant_buffer_descriptors) +
Shader::NumDescriptors(info.storage_buffers_descriptors) +
Shader::NumDescriptors(info.texture_buffer_descriptors) +
Shader::NumDescriptors(info.image_buffer_descriptors) +
Shader::NumDescriptors(info.texture_descriptors) +
Shader::NumDescriptors(info.image_descriptors);
}
class DescriptorLayoutBuilder { class DescriptorLayoutBuilder {
public: public:
DescriptorLayoutBuilder(const Device& device_) : device{&device_} {} DescriptorLayoutBuilder(const Device& device_) : device{&device_} {}
@@ -228,20 +194,12 @@ inline void PushImageDescriptors(TextureCache& texture_cache,
const VideoCommon::ImageViewId image_view_id{(views++)->id}; const VideoCommon::ImageViewId image_view_id{(views++)->id};
const VideoCommon::SamplerId sampler_id{*(samplers++)}; const VideoCommon::SamplerId sampler_id{*(samplers++)};
ImageView& image_view{texture_cache.GetImageView(image_view_id)}; ImageView& image_view{texture_cache.GetImageView(image_view_id)};
VkImageView vk_image_view{image_view.Handle(desc.type)}; const VkImageView vk_image_view{image_view.Handle(desc.type)};
if (vk_image_view == VK_NULL_HANDLE) {
const VkImageView null_image_view{texture_cache.GetImageView(VideoCommon::NULL_IMAGE_VIEW_ID).Handle(desc.type)};
if (null_image_view != VK_NULL_HANDLE) vk_image_view = null_image_view;
}
const Sampler& sampler{texture_cache.GetSampler(sampler_id)}; const Sampler& sampler{texture_cache.GetSampler(sampler_id)};
const bool use_fallback_sampler{sampler.HasAddedAnisotropy() && const bool use_fallback_sampler{sampler.HasAddedAnisotropy() &&
!image_view.SupportsAnisotropy()}; !image_view.SupportsAnisotropy()};
VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy() const VkSampler vk_sampler{use_fallback_sampler ? sampler.HandleWithDefaultAnisotropy()
: sampler.Handle()}; : sampler.Handle()};
if (sampler.HasLinearFiltering() &&
VideoCore::Surface::IsPixelFormatInteger(image_view.format)) {
vk_sampler = sampler.HandleWithNearestFilter();
}
guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler); guest_descriptor_queue.AddSampledImage(vk_image_view, vk_sampler);
const bool element_rescaled{texture_cache.IsRescaling(image_view)}; const bool element_rescaled{texture_cache.IsRescaling(image_view)};
is_rescaled |= element_rescaled; is_rescaled |= element_rescaled;
@@ -84,7 +84,7 @@ vk::Buffer CreateBuffer(const Device& device, const MemoryAllocator& memory_allo
} // Anonymous namespace } // Anonymous namespace
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params) Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
: VideoCommon::BufferBase(null_params), scheduler{&runtime.scheduler}, tracker{4096} { : VideoCommon::BufferBase(null_params), tracker{4096} {
if (runtime.device.HasNullDescriptor()) { if (runtime.device.HasNullDescriptor()) {
return; return;
} }
@@ -95,18 +95,12 @@ Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_p
Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_) Buffer::Buffer(BufferCacheRuntime& runtime, DAddr cpu_addr_, u64 size_bytes_)
: VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device}, : VideoCommon::BufferBase(cpu_addr_, size_bytes_), device{&runtime.device},
scheduler{&runtime.scheduler},
buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, tracker{SizeBytes()} { buffer{CreateBuffer(*device, runtime.memory_allocator, SizeBytes())}, tracker{SizeBytes()} {
if (runtime.device.HasDebuggingToolAttached()) { if (runtime.device.HasDebuggingToolAttached()) {
buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str()); buffer.SetObjectNameEXT(fmt::format("Buffer 0x{:x}", CpuAddr()).c_str());
} }
} }
void Buffer::MarkUsage(u64 offset, u64 size) noexcept {
tracker.Track(offset, size);
last_usage_tick = scheduler->CurrentTick();
}
VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) { VkBufferView Buffer::View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format) {
if (!device) { if (!device) {
// Null buffer supported, return a null descriptor // Null buffer supported, return a null descriptor
@@ -390,9 +384,7 @@ u32 BufferCacheRuntime::GetStorageBufferAlignment() const {
void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept { void BufferCacheRuntime::TickFrame(Common::SlotVector<Buffer>& slot_buffers) noexcept {
for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) { for (auto it = slot_buffers.begin(); it != slot_buffers.end(); it++) {
if (scheduler.IsFree(it->LastUsageTick())) { it->ResetUsageTracking();
it->ResetUsageTracking();
}
} }
} }
@@ -564,7 +556,7 @@ void BufferCacheRuntime::BindVertexBuffer(u32 index, VkBuffer buffer, u32 offset
if (index >= device.GetMaxVertexInputBindings()) { if (index >= device.GetMaxVertexInputBindings()) {
return; return;
} }
if (device.IsExtExtendedDynamicStateSupported() && !vertex_input_dynamic_state_active) { if (device.IsExtExtendedDynamicStateSupported()) {
scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) { scheduler.Record([index, buffer, offset, size, stride](vk::CommandBuffer cmdbuf) {
const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0; const VkDeviceSize vk_offset = buffer != VK_NULL_HANDLE ? offset : 0;
const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE; const VkDeviceSize vk_size = buffer != VK_NULL_HANDLE ? size : VK_WHOLE_SIZE;
@@ -604,7 +596,7 @@ void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bi
if (binding_count == 0) { if (binding_count == 0) {
return; return;
} }
if (device.IsExtExtendedDynamicStateSupported() && !vertex_input_dynamic_state_active) { if (device.IsExtExtendedDynamicStateSupported()) {
scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles), binding_count](vk::CommandBuffer cmdbuf) { scheduler.Record([bindings_ = std::move(bindings), buffer_handles_ = std::move(buffer_handles), binding_count](vk::CommandBuffer cmdbuf) {
cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(), bindings_.offsets.data(), bindings_.sizes.data(), bindings_.strides.data()); cmdbuf.BindVertexBuffers2EXT(bindings_.min_index, binding_count, buffer_handles_.data(), bindings_.offsets.data(), bindings_.sizes.data(), bindings_.strides.data());
}); });
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -43,16 +43,14 @@ public:
return tracker.IsUsed(offset, size); return tracker.IsUsed(offset, size);
} }
void MarkUsage(u64 offset, u64 size) noexcept; void MarkUsage(u64 offset, u64 size) noexcept {
tracker.Track(offset, size);
}
void ResetUsageTracking() noexcept { void ResetUsageTracking() noexcept {
tracker.Reset(); tracker.Reset();
} }
[[nodiscard]] u64 LastUsageTick() const noexcept {
return last_usage_tick;
}
operator VkBuffer() const noexcept { operator VkBuffer() const noexcept {
return *buffer; return *buffer;
} }
@@ -66,11 +64,9 @@ private:
}; };
const Device* device{}; const Device* device{};
Scheduler* scheduler{};
vk::Buffer buffer; vk::Buffer buffer;
std::vector<BufferView> views; std::vector<BufferView> views;
VideoCommon::UsageTracker tracker; VideoCommon::UsageTracker tracker;
u64 last_usage_tick{};
bool is_null{}; bool is_null{};
}; };
@@ -131,10 +127,6 @@ public:
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings); void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
void SetVertexInputDynamicState(bool is_active) {
vertex_input_dynamic_state_active = is_active;
}
void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size); void BindTransformFeedbackBuffer(u32 index, VkBuffer buffer, u32 offset, u32 size);
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings); void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings);
@@ -171,11 +163,7 @@ public:
private: private:
void BindBuffer(VkBuffer buffer, u32 offset, u32 size) { void BindBuffer(VkBuffer buffer, u32 offset, u32 size) {
if (buffer == VK_NULL_HANDLE) { guest_descriptor_queue.AddBuffer(buffer, offset, size);
guest_descriptor_queue.AddBuffer(buffer, 0, VK_WHOLE_SIZE);
} else {
guest_descriptor_queue.AddBuffer(buffer, offset, size);
}
} }
void ReserveNullBuffer(); void ReserveNullBuffer();
@@ -197,8 +185,6 @@ private:
bool limit_dynamic_storage_buffers = false; bool limit_dynamic_storage_buffers = false;
u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)(); u32 max_dynamic_storage_buffers = (std::numeric_limits<u32>::max)();
bool vertex_input_dynamic_state_active = false;
}; };
struct BufferCacheParams { struct BufferCacheParams {
@@ -26,7 +26,6 @@
#include "video_core/host_shaders/vulkan_uint8_comp_spv.h" #include "video_core/host_shaders/vulkan_uint8_comp_spv.h"
#include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h" #include "video_core/host_shaders/block_linear_unswizzle_3d_bcn_comp_spv.h"
#include "video_core/renderer_vulkan/vk_compute_pass.h" #include "video_core/renderer_vulkan/vk_compute_pass.h"
#include "video_core/surface.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h" #include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h" #include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -327,7 +326,7 @@ std::pair<VkBuffer, VkDeviceSize> Uint8Pass::Assemble(u32 num_vertices, VkBuffer
const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16)); const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
compute_pass_descriptor_queue.Acquire(scheduler, 2); compute_pass_descriptor_queue.Acquire();
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices); compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
compute_pass_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size); compute_pass_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size);
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
@@ -385,7 +384,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadIndexedPass::Assemble(
const std::size_t staging_size = num_tri_vertices * sizeof(u32); const std::size_t staging_size = num_tri_vertices * sizeof(u32);
const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal); const auto staging = staging_buffer_pool.Request(staging_size, MemoryUsage::DeviceLocal);
compute_pass_descriptor_queue.Acquire(scheduler, 2); compute_pass_descriptor_queue.Acquire();
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size); compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
compute_pass_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size); compute_pass_descriptor_queue.AddBuffer(staging.buffer, staging.offset, staging_size);
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
@@ -430,7 +429,7 @@ void ConditionalRenderingResolvePass::Resolve(VkBuffer dst_buffer, VkBuffer src_
} }
const size_t compare_size = compare_to_zero ? 8 : 24; const size_t compare_size = compare_to_zero ? 8 : 24;
compute_pass_descriptor_queue.Acquire(scheduler, 2); compute_pass_descriptor_queue.Acquire();
compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, compare_size); compute_pass_descriptor_queue.AddBuffer(src_buffer, src_offset, compare_size);
compute_pass_descriptor_queue.AddBuffer(dst_buffer, 0, sizeof(u32)); compute_pass_descriptor_queue.AddBuffer(dst_buffer, 0, sizeof(u32));
const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()}; const void* const descriptor_data{compute_pass_descriptor_queue.UpdateData()};
@@ -499,7 +498,7 @@ void QueriesPrefixScanPass::Run(VkBuffer accumulation_buffer, VkBuffer dst_buffe
static constexpr size_t DISPATCH_SIZE = 2048U; static constexpr size_t DISPATCH_SIZE = 2048U;
size_t runs_to_do = std::min<size_t>(current_runs, DISPATCH_SIZE); size_t runs_to_do = std::min<size_t>(current_runs, DISPATCH_SIZE);
current_runs -= runs_to_do; current_runs -= runs_to_do;
compute_pass_descriptor_queue.Acquire(scheduler, 3); compute_pass_descriptor_queue.Acquire();
compute_pass_descriptor_queue.AddBuffer(src_buffer, 0, number_of_sums * sizeof(u64)); compute_pass_descriptor_queue.AddBuffer(src_buffer, 0, number_of_sums * sizeof(u64));
compute_pass_descriptor_queue.AddBuffer(dst_buffer, 0, number_of_sums * sizeof(u64)); compute_pass_descriptor_queue.AddBuffer(dst_buffer, 0, number_of_sums * sizeof(u64));
compute_pass_descriptor_queue.AddBuffer(accumulation_buffer, 0, sizeof(u64)); compute_pass_descriptor_queue.AddBuffer(accumulation_buffer, 0, sizeof(u64));
@@ -601,7 +600,7 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U); const u32 num_dispatches_y = Common::DivCeil(swizzle.num_tiles.height, 8U);
const u32 num_dispatches_z = image.info.resources.layers; const u32 num_dispatches_z = image.info.resources.layers;
compute_pass_descriptor_queue.Acquire(scheduler, 2); compute_pass_descriptor_queue.Acquire();
compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset, compute_pass_descriptor_queue.AddBuffer(map.buffer, input_offset,
image.guest_size_bytes - swizzle.buffer_offset); image.guest_size_bytes - swizzle.buffer_offset);
compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level)); compute_pass_descriptor_queue.AddImage(image.StorageImageView(swizzle.level));
@@ -654,8 +653,71 @@ void ASTCDecoderPass::Assemble(Image& image, const StagingBufferRef& map,
scheduler.Finish(); scheduler.Finish();
} }
constexpr u32 BL3D_BINDING_INPUT_BUFFER = 0; constexpr u32 BL3D_BINDING_SWIZZLE_TABLE = 0;
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 1; constexpr u32 BL3D_BINDING_INPUT_BUFFER = 1;
constexpr u32 BL3D_BINDING_OUTPUT_BUFFER = 2;
constexpr std::array<VkDescriptorSetLayoutBinding, 3> BL3D_DESCRIPTOR_SET_BINDINGS{{
{
.binding = BL3D_BINDING_SWIZZLE_TABLE,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // swizzle_table[]
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3D_BINDING_INPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // block-linear input
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3D_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}};
constexpr DescriptorBankInfo BL3D_BANK_INFO{
.uniform_buffers = 0,
.storage_buffers = 3,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 3,
};
constexpr std::array<VkDescriptorUpdateTemplateEntry, 3>
BL3D_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY{{
{
.dstBinding = BL3D_BINDING_SWIZZLE_TABLE,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_SWIZZLE_TABLE * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3D_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3D_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
}
}};
struct alignas(16) BlockLinearUnswizzle3DPushConstants { struct alignas(16) BlockLinearUnswizzle3DPushConstants {
u32 blocks_dim[3]; // Offset 0 u32 blocks_dim[3]; // Offset 0
@@ -683,50 +745,11 @@ BlockLinearUnswizzle3DPass::BlockLinearUnswizzle3DPass(
DescriptorPool& descriptor_pool_, DescriptorPool& descriptor_pool_,
StagingBufferPool& staging_buffer_pool_, StagingBufferPool& staging_buffer_pool_,
ComputePassDescriptorQueue& compute_pass_descriptor_queue_) ComputePassDescriptorQueue& compute_pass_descriptor_queue_)
: ComputePass(device_, scheduler_, descriptor_pool_, : ComputePass(
std::array<VkDescriptorSetLayoutBinding, 2>{{ device_, scheduler_, descriptor_pool_,
{ BL3D_DESCRIPTOR_SET_BINDINGS,
.binding = BL3D_BINDING_INPUT_BUFFER, BL3D_DESCRIPTOR_UPDATE_TEMPLATE_ENTRY,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, // block-linear input BL3D_BANK_INFO,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
{
.binding = BL3D_BINDING_OUTPUT_BUFFER,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.descriptorCount = 1,
.stageFlags = VK_SHADER_STAGE_COMPUTE_BIT,
.pImmutableSamplers = nullptr,
},
}},
std::array<VkDescriptorUpdateTemplateEntry, 2>{{
{
.dstBinding = BL3D_BINDING_INPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_INPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
},
{
.dstBinding = BL3D_BINDING_OUTPUT_BUFFER,
.dstArrayElement = 0,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
.offset = BL3D_BINDING_OUTPUT_BUFFER * sizeof(DescriptorUpdateEntry),
.stride = sizeof(DescriptorUpdateEntry),
}
}},
DescriptorBankInfo{
.uniform_buffers = 0,
.storage_buffers = 2,
.texture_buffers = 0,
.image_buffers = 0,
.textures = 0,
.images = 0,
.score = 2,
},
COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DPushConstants)>, COMPUTE_PUSH_CONSTANT_RANGE<sizeof(BlockLinearUnswizzle3DPushConstants)>,
BLOCK_LINEAR_UNSWIZZLE_3D_BCN_COMP_SPV), BLOCK_LINEAR_UNSWIZZLE_3D_BCN_COMP_SPV),
scheduler{scheduler_}, scheduler{scheduler_},
@@ -746,8 +769,10 @@ void BlockLinearUnswizzle3DPass::Unswizzle(
const u32 MAX_BATCH_SLICES = (std::min)(z_count, image.info.size.depth); const u32 MAX_BATCH_SLICES = (std::min)(z_count, image.info.size.depth);
// Allocate or grow to cover this batch's slice count if (!image.has_compute_unswizzle_buffer) {
image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES); // Allocate exactly what this batch needs
image.AllocateComputeUnswizzleBuffer(MAX_BATCH_SLICES);
}
ASSERT(swizzles.size() == 1); ASSERT(swizzles.size() == 1);
const auto& sw = swizzles[0]; const auto& sw = swizzles[0];
@@ -796,7 +821,9 @@ void BlockLinearUnswizzle3DPass::UnswizzleChunk(
pc.blocks_dim[1] = blocks_y; pc.blocks_dim[1] = blocks_y;
pc.blocks_dim[2] = z_count; // Only process the count pc.blocks_dim[2] = z_count; // Only process the count
compute_pass_descriptor_queue.Acquire(scheduler, 3); compute_pass_descriptor_queue.Acquire();
compute_pass_descriptor_queue.AddBuffer(*image.runtime->swizzle_table_buffer, 0,
image.runtime->swizzle_table_size);
compute_pass_descriptor_queue.AddBuffer(swizzled.buffer, compute_pass_descriptor_queue.AddBuffer(swizzled.buffer,
sw.buffer_offset + swizzled.offset, sw.buffer_offset + swizzled.offset,
image.guest_size_bytes - sw.buffer_offset); image.guest_size_bytes - sw.buffer_offset);
@@ -962,7 +989,7 @@ void MSAACopyPass::CopyImage(Image& dst_image, Image& src_image,
ASSERT(copy.dst_subresource.base_layer == 0); ASSERT(copy.dst_subresource.base_layer == 0);
ASSERT(copy.dst_subresource.num_layers == 1); ASSERT(copy.dst_subresource.num_layers == 1);
compute_pass_descriptor_queue.Acquire(scheduler, 2); compute_pass_descriptor_queue.Acquire();
compute_pass_descriptor_queue.AddImage( compute_pass_descriptor_queue.AddImage(
src_image.StorageImageView(copy.src_subresource.base_level)); src_image.StorageImageView(copy.src_subresource.base_level));
compute_pass_descriptor_queue.AddImage( compute_pass_descriptor_queue.AddImage(

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