[cmake] Allow proper unity builds

Signed-off-by: lizzie <lizzie@eden-emu.dev>
This commit is contained in:
lizzie 2026-03-17 08:42:34 +00:00
parent 860acb4faf
commit 73608d945f
73 changed files with 860 additions and 1012 deletions

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// 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
@ -5,17 +8,11 @@
#include "common/assert.h" #include "common/assert.h"
namespace AudioCore::ADSP::OpusDecoder { namespace AudioCore::ADSP::OpusDecoder {
namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
} // namespace
u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) { u32 OpusDecodeObject::GetWorkBufferSize(u32 channel_count) {
if (!IsValidChannelCount(channel_count)) { if (channel_count == 1 || channel_count == 2)
return 0; return 0;
} return u32(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
return static_cast<u32>(sizeof(OpusDecodeObject)) + opus_decoder_get_size(channel_count);
} }
OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) { OpusDecodeObject& OpusDecodeObject::Initialize(u64 buffer, u64 buffer2) {

View file

@ -22,10 +22,6 @@ namespace AudioCore::ADSP::OpusDecoder {
namespace { namespace {
constexpr size_t OpusStreamCountMax = 255; constexpr size_t OpusStreamCountMax = 255;
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
bool IsValidMultiStreamChannelCount(u32 channel_count) { bool IsValidMultiStreamChannelCount(u32 channel_count) {
return channel_count <= OpusStreamCountMax; return channel_count <= OpusStreamCountMax;
} }
@ -90,7 +86,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
case GetWorkBufferSize: { case GetWorkBufferSize: {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]); auto channel_count = static_cast<s32>(shared_memory->host_send_data[0]);
ASSERT(IsValidChannelCount(channel_count)); ASSERT(channel_count == 1 || channel_count == 2);
shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count); shared_memory->dsp_return_data[0] = OpusDecodeObject::GetWorkBufferSize(channel_count);
Send(Direction::Host, Message::GetWorkBufferSizeOK); Send(Direction::Host, Message::GetWorkBufferSizeOK);
@ -103,7 +99,7 @@ void OpusDecoder::Main(std::stop_token stop_token) {
auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]); auto channel_count = static_cast<s32>(shared_memory->host_send_data[3]);
ASSERT(sample_rate >= 0); ASSERT(sample_rate >= 0);
ASSERT(IsValidChannelCount(channel_count)); ASSERT(channel_count == 1 || channel_count == 2);
ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count)); ASSERT(buffer_size >= OpusDecodeObject::GetWorkBufferSize(channel_count));
auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer); auto& decoder_object = OpusDecodeObject::Initialize(buffer, buffer);

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// 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
@ -7,13 +10,10 @@
namespace AudioCore::ADSP::OpusDecoder { namespace AudioCore::ADSP::OpusDecoder {
namespace { namespace {
bool IsValidChannelCount(u32 channel_count) {
return channel_count == 1 || channel_count == 2;
}
bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) { bool IsValidStreamCounts(u32 total_stream_count, u32 stereo_stream_count) {
return total_stream_count > 0 && static_cast<s32>(stereo_stream_count) >= 0 && return total_stream_count > 0 && s32(stereo_stream_count) >= 0
stereo_stream_count <= total_stream_count && IsValidChannelCount(total_stream_count); && stereo_stream_count <= total_stream_count
&& (total_stream_count == 1 || total_stream_count == 2);
} }
} // namespace } // namespace

View file

@ -1,3 +1,6 @@
// 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-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@ -16,7 +16,7 @@ namespace AudioCore::Renderer {
* @param memory - Core memory for writing. * @param memory - Core memory for writing.
* @param aux_info - Memory address pointing to the AuxInfo to reset. * @param aux_info - Memory address pointing to the AuxInfo to reset.
*/ */
static void ResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) { static void CaptureResetAuxBufferDsp(Core::Memory::Memory& memory, const CpuAddr aux_info) {
if (aux_info == 0) { if (aux_info == 0) {
LOG_ERROR(Service_Audio, "Aux info is 0!"); LOG_ERROR(Service_Audio, "Aux info is 0!");
return; return;
@ -134,7 +134,7 @@ void CaptureCommand::Process(const AudioRenderer::CommandListProcessor& processo
WriteAuxBufferDsp(*processor.memory, send_buffer_info, send_buffer, count_max, input_buffer, WriteAuxBufferDsp(*processor.memory, send_buffer_info, send_buffer, count_max, input_buffer,
processor.sample_count, write_offset, update_count); processor.sample_count, write_offset, update_count);
} else { } else {
ResetAuxBufferDsp(*processor.memory, send_buffer_info); CaptureResetAuxBufferDsp(*processor.memory, send_buffer_info);
} }
} }

View file

@ -1,23 +1,24 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// 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 <dynarmic/interface/halt_reason.h> #pragma once
#include <dynarmic/interface/halt_reason.h>
#include "core/arm/arm_interface.h" #include "core/arm/arm_interface.h"
namespace Core { namespace Core {
constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step; inline constexpr Dynarmic::HaltReason StepThread = Dynarmic::HaltReason::Step;
constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort; inline constexpr Dynarmic::HaltReason DataAbort = Dynarmic::HaltReason::MemoryAbort;
constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2; inline constexpr Dynarmic::HaltReason BreakLoop = Dynarmic::HaltReason::UserDefined2;
constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3; inline constexpr Dynarmic::HaltReason SupervisorCall = Dynarmic::HaltReason::UserDefined3;
constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4; inline constexpr Dynarmic::HaltReason InstructionBreakpoint = Dynarmic::HaltReason::UserDefined4;
constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6; inline constexpr Dynarmic::HaltReason PrefetchAbort = Dynarmic::HaltReason::UserDefined6;
constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) { [[nodiscard]] inline constexpr HaltReason TranslateHaltReason(Dynarmic::HaltReason hr) {
static_assert(u64(HaltReason::StepThread) == u64(StepThread)); static_assert(u64(HaltReason::StepThread) == u64(StepThread));
static_assert(u64(HaltReason::DataAbort) == u64(DataAbort)); static_assert(u64(HaltReason::DataAbort) == u64(DataAbort));
static_assert(u64(HaltReason::BreakLoop) == u64(BreakLoop)); static_assert(u64(HaltReason::BreakLoop) == u64(BreakLoop));

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@ -38,7 +38,7 @@ struct RomFSHeader {
}; };
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size."); static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
struct DirectoryEntry { struct RomFSDirectoryEntry {
u32_le parent; u32_le parent;
u32_le sibling; u32_le sibling;
u32_le child_dir; u32_le child_dir;
@ -46,9 +46,9 @@ struct DirectoryEntry {
u32_le hash; u32_le hash;
u32_le name_length; u32_le name_length;
}; };
static_assert(sizeof(DirectoryEntry) == 0x18, "DirectoryEntry has incorrect size."); static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
struct FileEntry { struct RomFSFileEntry {
u32_le parent; u32_le parent;
u32_le sibling; u32_le sibling;
u64_le offset; u64_le offset;
@ -56,7 +56,7 @@ struct FileEntry {
u32_le hash; u32_le hash;
u32_le name_length; u32_le name_length;
}; };
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size."); static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
struct RomFSTraversalContext { struct RomFSTraversalContext {
RomFSHeader header; RomFSHeader header;
@ -84,14 +84,14 @@ std::pair<EntryType, std::string> GetEntry(const RomFSTraversalContext& ctx, siz
return {entry, std::move(name)}; return {entry, std::move(name)};
} }
std::pair<DirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx, std::pair<RomFSDirectoryEntry, std::string> GetDirectoryEntry(const RomFSTraversalContext& ctx,
size_t directory_offset) { size_t directory_offset) {
return GetEntry<DirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset); return GetEntry<RomFSDirectoryEntry, &RomFSTraversalContext::directory_meta>(ctx, directory_offset);
} }
std::pair<FileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx, std::pair<RomFSFileEntry, std::string> GetFileEntry(const RomFSTraversalContext& ctx,
size_t file_offset) { size_t file_offset) {
return GetEntry<FileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset); return GetEntry<RomFSFileEntry, &RomFSTraversalContext::file_meta>(ctx, file_offset);
} }
void ProcessFile(const RomFSTraversalContext& ctx, u32 this_file_offset, void ProcessFile(const RomFSTraversalContext& ctx, u32 this_file_offset,

View file

@ -1,3 +1,6 @@
// 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
@ -6,13 +9,12 @@
#include "core/hle/service/ipc_helpers.h" #include "core/hle/service/ipc_helpers.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::AudioIn;
IAudioIn::IAudioIn(Core::System& system_, Manager& manager, size_t session_id, IAudioIn::IAudioIn(Core::System& system_, AudioCore::AudioIn::Manager& manager, size_t session_id,
const std::string& device_name, const AudioInParameter& in_params, const std::string& device_name, const AudioCore::AudioIn::AudioInParameter& in_params,
Kernel::KProcess* handle, u64 applet_resource_user_id) Kernel::KProcess* handle, u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioIn"}, process{handle}, service_context{system_, "IAudioIn"}, : ServiceFramework{system_, "IAudioIn"}, process{handle}, service_context{system_, "IAudioIn"},
event{service_context.CreateEvent("AudioInEvent")}, impl{std::make_shared<In>(system_, event{service_context.CreateEvent("AudioInEvent")}, impl{std::make_shared<AudioCore::AudioIn::In>(system_,
manager, event, manager, event,
session_id)} { session_id)} {
// clang-format off // clang-format off
@ -68,12 +70,12 @@ Result IAudioIn::Stop() {
R_RETURN(impl->StopSystem()); R_RETURN(impl->StopSystem());
} }
Result IAudioIn::AppendAudioInBuffer(InArray<AudioInBuffer, BufferAttr_HipcMapAlias> buffer, Result IAudioIn::AppendAudioInBuffer(InArray<AudioCore::AudioIn::AudioInBuffer, BufferAttr_HipcMapAlias> buffer,
u64 buffer_client_ptr) { u64 buffer_client_ptr) {
R_RETURN(this->AppendAudioInBufferAuto(buffer, buffer_client_ptr)); R_RETURN(this->AppendAudioInBufferAuto(buffer, buffer_client_ptr));
} }
Result IAudioIn::AppendAudioInBufferAuto(InArray<AudioInBuffer, BufferAttr_HipcAutoSelect> buffer, Result IAudioIn::AppendAudioInBufferAuto(InArray<AudioCore::AudioIn::AudioInBuffer, BufferAttr_HipcAutoSelect> buffer,
u64 buffer_client_ptr) { u64 buffer_client_ptr) {
if (buffer.empty()) { if (buffer.empty()) {
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!"); LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioInBuffer!");

View file

@ -1,3 +1,6 @@
// 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
@ -7,7 +10,6 @@
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::AudioIn;
IAudioInManager::IAudioInManager(Core::System& system_) IAudioInManager::IAudioInManager(Core::System& system_)
: ServiceFramework{system_, "audin:u"}, impl{std::make_unique<AudioCore::AudioIn::Manager>( : ServiceFramework{system_, "audin:u"}, impl{std::make_unique<AudioCore::AudioIn::Manager>(
@ -34,11 +36,11 @@ Result IAudioInManager::ListAudioIns(
R_RETURN(this->ListAudioInsAutoFiltered(out_audio_ins, out_count)); R_RETURN(this->ListAudioInsAutoFiltered(out_audio_ins, out_count));
} }
Result IAudioInManager::OpenAudioIn(Out<AudioInParameterInternal> out_parameter_internal, Result IAudioInManager::OpenAudioIn(Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal,
Out<SharedPointer<IAudioIn>> out_audio_in, Out<SharedPointer<IAudioIn>> out_audio_in,
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name, OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name, InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
AudioInParameter parameter, AudioCore::AudioIn::AudioInParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle,
ClientAppletResourceUserId aruid) { ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
@ -53,9 +55,9 @@ Result IAudioInManager::ListAudioInsAuto(
} }
Result IAudioInManager::OpenAudioInAuto( Result IAudioInManager::OpenAudioInAuto(
Out<AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in, Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name, OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioInParameter parameter, InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioCore::AudioIn::AudioInParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) { InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
R_RETURN(this->OpenAudioInProtocolSpecified(out_parameter_internal, out_audio_in, out_name, R_RETURN(this->OpenAudioInProtocolSpecified(out_parameter_internal, out_audio_in, out_name,
@ -70,10 +72,10 @@ Result IAudioInManager::ListAudioInsAutoFiltered(
} }
Result IAudioInManager::OpenAudioInProtocolSpecified( Result IAudioInManager::OpenAudioInProtocolSpecified(
Out<AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in, Out<AudioCore::AudioIn::AudioInParameterInternal> out_parameter_internal, Out<SharedPointer<IAudioIn>> out_audio_in,
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name, OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, Protocol protocol, InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, Protocol protocol,
AudioInParameter parameter, InCopyHandle<Kernel::KProcess> process_handle, AudioCore::AudioIn::AudioInParameter parameter, InCopyHandle<Kernel::KProcess> process_handle,
ClientAppletResourceUserId aruid) { ClientAppletResourceUserId aruid) {
LOG_DEBUG(Service_Audio, "called"); LOG_DEBUG(Service_Audio, "called");
@ -104,7 +106,7 @@ Result IAudioInManager::OpenAudioInProtocolSpecified(
auto& out_system = impl->sessions[new_session_id]->GetSystem(); auto& out_system = impl->sessions[new_session_id]->GetSystem();
*out_parameter_internal = *out_parameter_internal =
AudioInParameterInternal{.sample_rate = out_system.GetSampleRate(), AudioCore::AudioIn::AudioInParameterInternal{.sample_rate = out_system.GetSampleRate(),
.channel_count = out_system.GetChannelCount(), .channel_count = out_system.GetChannelCount(),
.sample_format = static_cast<u32>(out_system.GetSampleFormat()), .sample_format = static_cast<u32>(out_system.GetSampleFormat()),
.state = static_cast<u32>(out_system.GetState())}; .state = static_cast<u32>(out_system.GetState())};

View file

@ -1,3 +1,6 @@
// 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
@ -10,10 +13,9 @@
#include "core/hle/service/service.h" #include "core/hle/service/service.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::AudioOut;
IAudioOut::IAudioOut(Core::System& system_, Manager& manager, size_t session_id, IAudioOut::IAudioOut(Core::System& system_, AudioCore::AudioOut::Manager& manager, size_t session_id,
const std::string& device_name, const AudioOutParameter& in_params, const std::string& device_name, const AudioCore::AudioOut::AudioOutParameter& in_params,
Kernel::KProcess* handle, u64 applet_resource_user_id) Kernel::KProcess* handle, u64 applet_resource_user_id)
: ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"}, : ServiceFramework{system_, "IAudioOut"}, service_context{system_, "IAudioOut"},
event{service_context.CreateEvent("AudioOutEvent")}, process{handle}, event{service_context.CreateEvent("AudioOutEvent")}, process{handle},
@ -65,12 +67,12 @@ Result IAudioOut::Stop() {
} }
Result IAudioOut::AppendAudioOutBuffer( Result IAudioOut::AppendAudioOutBuffer(
InArray<AudioOutBuffer, BufferAttr_HipcMapAlias> audio_out_buffer, u64 buffer_client_ptr) { InArray<AudioCore::AudioOut::AudioOutBuffer, BufferAttr_HipcMapAlias> audio_out_buffer, u64 buffer_client_ptr) {
R_RETURN(this->AppendAudioOutBufferAuto(audio_out_buffer, buffer_client_ptr)); R_RETURN(this->AppendAudioOutBufferAuto(audio_out_buffer, buffer_client_ptr));
} }
Result IAudioOut::AppendAudioOutBufferAuto( Result IAudioOut::AppendAudioOutBufferAuto(
InArray<AudioOutBuffer, BufferAttr_HipcAutoSelect> audio_out_buffer, u64 buffer_client_ptr) { InArray<AudioCore::AudioOut::AudioOutBuffer, BufferAttr_HipcAutoSelect> audio_out_buffer, u64 buffer_client_ptr) {
if (audio_out_buffer.empty()) { if (audio_out_buffer.empty()) {
LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!"); LOG_ERROR(Service_Audio, "Input buffer is too small for an AudioOutBuffer!");
R_THROW(Audio::ResultInsufficientBuffer); R_THROW(Audio::ResultInsufficientBuffer);

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
@ -11,10 +11,9 @@
#include "core/memory.h" #include "core/memory.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::AudioOut;
IAudioOutManager::IAudioOutManager(Core::System& system_) IAudioOutManager::IAudioOutManager(Core::System& system_)
: ServiceFramework{system_, "audout:u"}, impl{std::make_unique<Manager>(system_)} { : ServiceFramework{system_, "audout:u"}, impl{std::make_unique<AudioCore::AudioOut::Manager>(system_)} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"}, {0, D<&IAudioOutManager::ListAudioOuts>, "ListAudioOuts"},
@ -34,11 +33,11 @@ Result IAudioOutManager::ListAudioOuts(
R_RETURN(this->ListAudioOutsAuto(out_audio_outs, out_count)); R_RETURN(this->ListAudioOutsAuto(out_audio_outs, out_count));
} }
Result IAudioOutManager::OpenAudioOut(Out<AudioOutParameterInternal> out_parameter_internal, Result IAudioOutManager::OpenAudioOut(Out<AudioCore::AudioOut::AudioOutParameterInternal> out_parameter_internal,
Out<SharedPointer<IAudioOut>> out_audio_out, Out<SharedPointer<IAudioOut>> out_audio_out,
OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name, OutArray<AudioDeviceName, BufferAttr_HipcMapAlias> out_name,
InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name, InArray<AudioDeviceName, BufferAttr_HipcMapAlias> name,
AudioOutParameter parameter, AudioCore::AudioOut::AudioOutParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, InCopyHandle<Kernel::KProcess> process_handle,
ClientAppletResourceUserId aruid) { ClientAppletResourceUserId aruid) {
R_RETURN(this->OpenAudioOutAuto(out_parameter_internal, out_audio_out, out_name, name, R_RETURN(this->OpenAudioOutAuto(out_parameter_internal, out_audio_out, out_name, name,
@ -60,10 +59,10 @@ Result IAudioOutManager::ListAudioOutsAuto(
} }
Result IAudioOutManager::OpenAudioOutAuto( Result IAudioOutManager::OpenAudioOutAuto(
Out<AudioOutParameterInternal> out_parameter_internal, Out<AudioCore::AudioOut::AudioOutParameterInternal> out_parameter_internal,
Out<SharedPointer<IAudioOut>> out_audio_out, Out<SharedPointer<IAudioOut>> out_audio_out,
OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name, OutArray<AudioDeviceName, BufferAttr_HipcAutoSelect> out_name,
InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioOutParameter parameter, InArray<AudioDeviceName, BufferAttr_HipcAutoSelect> name, AudioCore::AudioOut::AudioOutParameter parameter,
InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) { InCopyHandle<Kernel::KProcess> process_handle, ClientAppletResourceUserId aruid) {
if (!process_handle) { if (!process_handle) {
LOG_ERROR(Service_Audio, "Failed to get process handle"); LOG_ERROR(Service_Audio, "Failed to get process handle");
@ -93,7 +92,7 @@ Result IAudioOutManager::OpenAudioOutAuto(
auto& out_system = impl->sessions[new_session_id]->GetSystem(); auto& out_system = impl->sessions[new_session_id]->GetSystem();
*out_parameter_internal = *out_parameter_internal =
AudioOutParameterInternal{.sample_rate = out_system.GetSampleRate(), AudioCore::AudioOut::AudioOutParameterInternal{.sample_rate = out_system.GetSampleRate(),
.channel_count = out_system.GetChannelCount(), .channel_count = out_system.GetChannelCount(),
.sample_format = static_cast<u32>(out_system.GetSampleFormat()), .sample_format = static_cast<u32>(out_system.GetSampleFormat()),
.state = static_cast<u32>(out_system.GetState())}; .state = static_cast<u32>(out_system.GetState())};

View file

@ -1,24 +1,23 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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 "audio_core/renderer/audio_renderer.h"
#include "core/hle/service/audio/audio_renderer.h" #include "core/hle/service/audio/audio_renderer.h"
#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/cmif_serialization.h"
namespace Service::Audio { namespace Service::Audio {
using namespace AudioCore::Renderer;
IAudioRenderer::IAudioRenderer(Core::System& system_, Manager& manager_, IAudioRenderer::IAudioRenderer(Core::System& system_, AudioCore::Renderer::Manager& manager_,
AudioCore::AudioRendererParameterInternal& params, AudioCore::AudioRendererParameterInternal& params,
Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size, Kernel::KTransferMemory* transfer_memory, u64 transfer_memory_size,
Kernel::KProcess* process_handle_, u64 applet_resource_user_id, Kernel::KProcess* process_handle_, u64 applet_resource_user_id,
s32 session_id) s32 session_id)
: ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"}, : ServiceFramework{system_, "IAudioRenderer"}, service_context{system_, "IAudioRenderer"},
rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_}, rendered_event{service_context.CreateEvent("IAudioRendererEvent")}, manager{manager_},
impl{std::make_unique<Renderer>(system_, manager, rendered_event)}, process_handle{ impl{std::make_unique<AudioCore::Renderer::Renderer>(system_, manager, rendered_event)}, process_handle{process_handle_} {
process_handle_} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IAudioRenderer::GetSampleRate>, "GetSampleRate"}, {0, D<&IAudioRenderer::GetSampleRate>, "GetSampleRate"},

View file

@ -1,3 +1,6 @@
// 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
@ -15,7 +18,7 @@ namespace Service::Audio {
using namespace AudioCore::Renderer; using namespace AudioCore::Renderer;
IAudioRendererManager::IAudioRendererManager(Core::System& system_) IAudioRendererManager::IAudioRendererManager(Core::System& system_)
: ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Manager>(system_)} { : ServiceFramework{system_, "audren:u"}, impl{std::make_unique<Service::Audio::Manager>(system_)} {
// clang-format off // clang-format off
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
{0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"}, {0, D<&IAudioRendererManager::OpenAudioRenderer>, "OpenAudioRenderer"},

View file

@ -14,16 +14,13 @@
#include <cstring> #include <cstring>
namespace Service::News { namespace Service::News {
namespace {
std::string_view ToStringView(std::span<const char> buf) { [[nodiscard]] inline std::string_view ToStringViewNDS(std::span<const char> buf) {
const std::string_view sv{buf.data(), buf.size()}; const std::string_view sv{buf.data(), buf.size()};
const auto nul = sv.find('\0'); const auto nul = sv.find('\0');
return nul == std::string_view::npos ? sv : sv.substr(0, nul); return nul == std::string_view::npos ? sv : sv.substr(0, nul);
} }
} // namespace
INewsDataService::INewsDataService(Core::System& system_) INewsDataService::INewsDataService(Core::System& system_)
: ServiceFramework{system_, "INewsDataService"} { : ServiceFramework{system_, "INewsDataService"} {
static const FunctionInfo functions[] = { static const FunctionInfo functions[] = {
@ -55,7 +52,7 @@ bool INewsDataService::TryOpen(std::string_view key, std::string_view user) {
const auto list = NewsStorage::Instance().ListAll(); const auto list = NewsStorage::Instance().ListAll();
if (!list.empty()) { if (!list.empty()) {
if (auto found = NewsStorage::Instance().FindByNewsId(ToStringView(list.front().news_id))) { if (auto found = NewsStorage::Instance().FindByNewsId(ToStringViewNDS(list.front().news_id))) {
opened_payload = std::move(found->payload); opened_payload = std::move(found->payload);
return true; return true;
} }
@ -67,7 +64,7 @@ bool INewsDataService::TryOpen(std::string_view key, std::string_view user) {
Result INewsDataService::Open(InBuffer<BufferAttr_HipcMapAlias> name) { Result INewsDataService::Open(InBuffer<BufferAttr_HipcMapAlias> name) {
EnsureBuiltinNewsLoaded(); EnsureBuiltinNewsLoaded();
const auto key = ToStringView({reinterpret_cast<const char*>(name.data()), name.size()}); const auto key = ToStringViewNDS({reinterpret_cast<const char*>(name.data()), name.size()});
if (TryOpen(key, {})) { if (TryOpen(key, {})) {
R_SUCCEED(); R_SUCCEED();
@ -79,8 +76,8 @@ Result INewsDataService::Open(InBuffer<BufferAttr_HipcMapAlias> name) {
Result INewsDataService::OpenWithNewsRecordV1(NewsRecordV1 record) { Result INewsDataService::OpenWithNewsRecordV1(NewsRecordV1 record) {
EnsureBuiltinNewsLoaded(); EnsureBuiltinNewsLoaded();
const auto key = ToStringView(record.news_id); const auto key = ToStringViewNDS(record.news_id);
const auto user = ToStringView(record.user_id); const auto user = ToStringViewNDS(record.user_id);
if (TryOpen(key, user)) { if (TryOpen(key, user)) {
R_SUCCEED(); R_SUCCEED();
@ -92,8 +89,8 @@ Result INewsDataService::OpenWithNewsRecordV1(NewsRecordV1 record) {
Result INewsDataService::OpenWithNewsRecord(NewsRecord record) { Result INewsDataService::OpenWithNewsRecord(NewsRecord record) {
EnsureBuiltinNewsLoaded(); EnsureBuiltinNewsLoaded();
const auto key = ToStringView(record.news_id); const auto key = ToStringViewNDS(record.news_id);
const auto user = ToStringView(record.user_id); const auto user = ToStringViewNDS(record.user_id);
if (TryOpen(key, user)) { if (TryOpen(key, user)) {
R_SUCCEED(); R_SUCCEED();

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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
@ -15,13 +15,13 @@
namespace Service::News { namespace Service::News {
namespace { namespace {
std::string_view ToStringView(std::span<const u8> buf) { [[nodiscard]] inline std::string_view ToStringView(std::span<const u8> buf) {
if (buf.empty()) return {}; if (buf.empty()) return {};
auto data = reinterpret_cast<const char*>(buf.data()); auto data = reinterpret_cast<const char*>(buf.data());
return {data, strnlen(data, buf.size())}; return {data, strnlen(data, buf.size())};
} }
std::string_view ToStringView(std::span<const char> buf) { [[nodiscard]] inline std::string_view ToStringView(std::span<const char> buf) {
if (buf.empty()) return {}; if (buf.empty()) return {};
return {buf.data(), strnlen(buf.data(), buf.size())}; return {buf.data(), strnlen(buf.data(), buf.size())};
} }

View file

@ -212,8 +212,9 @@ struct NifmNetworkProfileData {
NifmWirelessSettingData wireless_setting_data{}; NifmWirelessSettingData wireless_setting_data{};
IpSettingData ip_setting_data{}; IpSettingData ip_setting_data{};
}; };
static_assert(sizeof(NifmNetworkProfileData) == 0x18E,
"NifmNetworkProfileData has incorrect size.");
#pragma pack(pop) #pragma pack(pop)
static_assert(sizeof(NifmNetworkProfileData) == 0x18E, "NifmNetworkProfileData has incorrect size.");
struct PendingProfile { struct PendingProfile {
std::array<char, 0x21> ssid{}; std::array<char, 0x21> ssid{};

View file

@ -1,6 +1,11 @@
// 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
#pragma once
#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"

View file

@ -4,6 +4,8 @@
// 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
#pragma once
#include <ankerl/unordered_dense.h> #include <ankerl/unordered_dense.h>
#include "common/uuid.h" #include "common/uuid.h"

View file

@ -1,6 +1,11 @@
// 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
#pragma once
#include "core/hle/service/cmif_types.h" #include "core/hle/service/cmif_types.h"
#include "core/hle/service/ns/ns_types.h" #include "core/hle/service/ns/ns_types.h"
#include "core/hle/service/service.h" #include "core/hle/service/service.h"

View file

@ -4,6 +4,8 @@
// 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
#pragma once
#include <array> #include <array>
#include "common/uuid.h" #include "common/uuid.h"

View file

@ -4,6 +4,8 @@
// 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
#pragma once
#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"

View file

@ -1,6 +1,11 @@
// 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
#pragma once
#include <map> #include <map>
#include <set> #include <set>

View file

@ -17,11 +17,9 @@
namespace Loader { namespace Loader {
namespace { [[nodiscard]] inline constexpr u32 PageAlignSizeKIP(u32 size) {
constexpr u32 PageAlignSize(u32 size) { return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
} }
} // Anonymous namespace
AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_) AppLoader_KIP::AppLoader_KIP(FileSys::VirtualFile file_)
: AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {} : AppLoader(std::move(file_)), kip(std::make_unique<FileSys::KIP>(file)) {}
@ -76,11 +74,11 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process,
kip->GetKernelCapabilities()); kip->GetKernelCapabilities());
Kernel::CodeSet codeset; Kernel::CodeSet codeset;
codeset.memory.resize(PageAlignSize(kip->GetBSSOffset()) + kip->GetBSSSize()); codeset.memory.resize(PageAlignSizeKIP(kip->GetBSSOffset()) + kip->GetBSSSize());
const auto load_segment = [&codeset](Kernel::CodeSet::Segment& segment, std::span<const u8> data, u32 offset) { const auto load_segment = [&codeset](Kernel::CodeSet::Segment& segment, std::span<const u8> data, u32 offset) {
segment.addr = offset; segment.addr = offset;
segment.offset = offset; segment.offset = offset;
segment.size = PageAlignSize(u32(data.size())); segment.size = PageAlignSizeKIP(u32(data.size()));
std::memcpy(codeset.memory.data() + offset, data.data(), data.size()); std::memcpy(codeset.memory.data() + offset, data.data(), data.size());
}; };
load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset()); load_segment(codeset.CodeSegment(), kip->GetTextSection(), kip->GetTextOffset());

View file

@ -143,8 +143,8 @@ bool AppLoader_NRO::IsHomebrew() {
nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W'); nro_header.magic_ext2 == Common::MakeMagic('B', 'R', 'E', 'W');
} }
static constexpr u32 PageAlignSize(u32 size) { [[nodiscard]] inline constexpr u32 PageAlignSizeNRO(u32 size) {
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
} }
static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
@ -161,9 +161,9 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
} }
// Build program image // Build program image
std::vector<u8> program_image(PageAlignSize(nro_header.file_size)); std::vector<u8> program_image(PageAlignSizeNRO(nro_header.file_size));
std::memcpy(program_image.data(), data.data(), program_image.size()); std::memcpy(program_image.data(), data.data(), program_image.size());
if (program_image.size() != PageAlignSize(nro_header.file_size)) { if (program_image.size() != PageAlignSizeNRO(nro_header.file_size)) {
return {}; return {};
} }
@ -171,7 +171,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
for (std::size_t i = 0; i < nro_header.segments.size(); ++i) { for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
codeset.segments[i].addr = nro_header.segments[i].offset; codeset.segments[i].addr = nro_header.segments[i].offset;
codeset.segments[i].offset = nro_header.segments[i].offset; codeset.segments[i].offset = nro_header.segments[i].offset;
codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size); codeset.segments[i].size = PageAlignSizeNRO(nro_header.segments[i].size);
} }
if (!Settings::values.program_args.GetValue().empty()) { if (!Settings::values.program_args.GetValue().empty()) {
@ -188,7 +188,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
} }
// Default .bss to NRO header bss size if MOD0 section doesn't exist // Default .bss to NRO header bss size if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nro_header.bss_size)}; u32 bss_size{PageAlignSizeNRO(nro_header.bss_size)};
// Read MOD header // Read MOD header
ModHeader mod_header{}; ModHeader mod_header{};
@ -198,7 +198,7 @@ static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process,
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) { if (has_mod_header) {
// Resize program image to include .bss section and page align each section // Resize program image to include .bss section and page align each section
bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); bss_size = PageAlignSizeNRO(mod_header.bss_end_offset - mod_header.bss_start_offset);
} }
codeset.DataSegment().size += bss_size; codeset.DataSegment().size += bss_size;

View file

@ -41,8 +41,8 @@ struct MODHeader {
}; };
static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size."); static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
constexpr u32 PageAlignSize(u32 size) { [[nodiscard]] inline constexpr u32 PageAlignSizeNSO(u32 size) {
return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); return u32((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK);
} }
} // Anonymous namespace } // Anonymous namespace
@ -128,11 +128,11 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::
} }
codeset.DataSegment().size += nso_header.segments[2].bss_size; codeset.DataSegment().size += nso_header.segments[2].bss_size;
u32 image_size = PageAlignSize(u32(codeset.memory.size()) + nso_header.segments[2].bss_size); u32 image_size = PageAlignSizeNSO(u32(codeset.memory.size()) + nso_header.segments[2].bss_size);
codeset.memory.resize(image_size); codeset.memory.resize(image_size);
for (std::size_t i = 0; i < nso_header.segments.size(); ++i) { for (std::size_t i = 0; i < nso_header.segments.size(); ++i) {
codeset.segments[i].size = PageAlignSize(codeset.segments[i].size); codeset.segments[i].size = PageAlignSizeNSO(codeset.segments[i].size);
} }
// Apply patches if necessary // Apply patches if necessary

View file

@ -1,3 +1,6 @@
// 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
@ -6,7 +9,6 @@
#include "hid_core/hidbus/starlink.h" #include "hid_core/hidbus/starlink.h"
namespace Service::HID { namespace Service::HID {
constexpr u8 DEVICE_ID = 0x28;
Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_) Starlink::Starlink(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: HidbusBase(system_, service_context_) {} : HidbusBase(system_, service_context_) {}
@ -35,7 +37,7 @@ void Starlink::OnUpdate() {
} }
u8 Starlink::GetDeviceId() const { u8 Starlink::GetDeviceId() const {
return DEVICE_ID; return 0x28;
} }
u64 Starlink::GetReply(std::span<u8> out_data) const { u64 Starlink::GetReply(std::span<u8> out_data) const {

View file

@ -1,3 +1,6 @@
// 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
@ -6,7 +9,6 @@
#include "hid_core/hidbus/stubbed.h" #include "hid_core/hidbus/stubbed.h"
namespace Service::HID { namespace Service::HID {
constexpr u8 DEVICE_ID = 0xFF;
HidbusStubbed::HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_) HidbusStubbed::HidbusStubbed(Core::System& system_, KernelHelpers::ServiceContext& service_context_)
: HidbusBase(system_, service_context_) {} : HidbusBase(system_, service_context_) {}
@ -35,7 +37,7 @@ void HidbusStubbed::OnUpdate() {
} }
u8 HidbusStubbed::GetDeviceId() const { u8 HidbusStubbed::GetDeviceId() const {
return DEVICE_ID; return 0xFF;
} }
u64 HidbusStubbed::GetReply(std::span<u8> out_data) const { u64 HidbusStubbed::GetReply(std::span<u8> out_data) const {

View file

@ -1,3 +1,6 @@
// 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
@ -8,9 +11,6 @@
#include "hid_core/irsensor/moment_processor.h" #include "hid_core/irsensor/moment_processor.h"
namespace Service::IRS { namespace Service::IRS {
static constexpr auto format = Core::IrSensor::ImageTransferProcessorFormat::Size40x30;
static constexpr std::size_t ImageWidth = 40;
static constexpr std::size_t ImageHeight = 30;
MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format, MomentProcessor::MomentProcessor(Core::System& system_, Core::IrSensor::DeviceFormat& device_format,
std::size_t npad_index) std::size_t npad_index)
@ -80,9 +80,9 @@ void MomentProcessor::OnControllerUpdate(Core::HID::ControllerTriggerType type)
} }
u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const { u8 MomentProcessor::GetPixel(const std::vector<u8>& data, std::size_t x, std::size_t y) const {
if ((y * ImageWidth) + x >= data.size()) { constexpr std::size_t ImageWidth = 40;
if ((y * ImageWidth) + x >= data.size())
return 0; return 0;
}
return data[(y * ImageWidth) + x]; return data[(y * ImageWidth) + x];
} }
@ -92,9 +92,12 @@ MomentProcessor::MomentStatistic MomentProcessor::GetStatistic(const std::vector
std::size_t width, std::size_t width,
std::size_t height) const { std::size_t height) const {
// The actual implementation is always 320x240 // The actual implementation is always 320x240
static constexpr std::size_t RealWidth = 320; constexpr std::size_t RealWidth = 320;
static constexpr std::size_t RealHeight = 240; constexpr std::size_t RealHeight = 240;
static constexpr std::size_t Threshold = 30; constexpr std::size_t Threshold = 30;
constexpr std::size_t ImageWidth = 40;
constexpr std::size_t ImageHeight = 30;
MomentStatistic statistic{}; MomentStatistic statistic{};
std::size_t active_points{}; std::size_t active_points{};
@ -143,7 +146,7 @@ void MomentProcessor::SetConfig(Core::IrSensor::PackedMomentProcessorConfig conf
static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess); static_cast<Core::IrSensor::MomentProcessorPreprocess>(config.preprocess);
current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold; current_config.preprocess_intensity_threshold = config.preprocess_intensity_threshold;
npad_device->SetCameraFormat(format); npad_device->SetCameraFormat(Core::IrSensor::ImageTransferProcessorFormat::Size40x30);
} }
} // namespace Service::IRS } // namespace Service::IRS

View file

@ -1,3 +1,6 @@
// 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
@ -7,14 +10,14 @@
#include "input_common/drivers/camera.h" #include "input_common/drivers/camera.h"
namespace InputCommon { namespace InputCommon {
constexpr PadIdentifier identifier = { constexpr PadIdentifier camera_identifier = {
.guid = Common::UUID{}, .guid = Common::UUID{},
.port = 0, .port = 0,
.pad = 0, .pad = 0,
}; };
Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) { Camera::Camera(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(identifier); PreSetController(camera_identifier);
} }
void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) { void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<const u32> data) {
@ -33,7 +36,7 @@ void Camera::SetCameraData(std::size_t width, std::size_t height, std::span<cons
} }
} }
SetCamera(identifier, status); SetCamera(camera_identifier, status);
} }
std::size_t Camera::getImageWidth() const { std::size_t Camera::getImageWidth() const {

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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
@ -26,7 +26,7 @@ constexpr int mouse_axis_x = 0;
constexpr int mouse_axis_y = 1; constexpr int mouse_axis_y = 1;
constexpr int wheel_axis_x = 2; constexpr int wheel_axis_x = 2;
constexpr int wheel_axis_y = 3; constexpr int wheel_axis_y = 3;
constexpr PadIdentifier identifier = { constexpr PadIdentifier mouse_identifier = {
.guid = Common::UUID{}, .guid = Common::UUID{},
.port = 0, .port = 0,
.pad = 0, .pad = 0,
@ -51,16 +51,16 @@ constexpr PadIdentifier touch_identifier = {
}; };
Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) { Mouse::Mouse(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(identifier); PreSetController(mouse_identifier);
PreSetController(real_mouse_identifier); PreSetController(real_mouse_identifier);
PreSetController(touch_identifier); PreSetController(touch_identifier);
PreSetController(motion_identifier); PreSetController(motion_identifier);
// Initialize all mouse axis // Initialize all mouse axis
PreSetAxis(identifier, mouse_axis_x); PreSetAxis(mouse_identifier, mouse_axis_x);
PreSetAxis(identifier, mouse_axis_y); PreSetAxis(mouse_identifier, mouse_axis_y);
PreSetAxis(identifier, wheel_axis_x); PreSetAxis(mouse_identifier, wheel_axis_x);
PreSetAxis(identifier, wheel_axis_y); PreSetAxis(mouse_identifier, wheel_axis_y);
PreSetAxis(real_mouse_identifier, mouse_axis_x); PreSetAxis(real_mouse_identifier, mouse_axis_x);
PreSetAxis(real_mouse_identifier, mouse_axis_y); PreSetAxis(real_mouse_identifier, mouse_axis_y);
PreSetAxis(touch_identifier, mouse_axis_x); PreSetAxis(touch_identifier, mouse_axis_x);
@ -97,8 +97,8 @@ void Mouse::UpdateStickInput() {
last_mouse_change *= maximum_stick_range; last_mouse_change *= maximum_stick_range;
} }
SetAxis(identifier, mouse_axis_x, last_mouse_change.x); SetAxis(mouse_identifier, mouse_axis_x, last_mouse_change.x);
SetAxis(identifier, mouse_axis_y, -last_mouse_change.y); SetAxis(mouse_identifier, mouse_axis_y, -last_mouse_change.y);
// Decay input over time // Decay input over time
const float clamped_length = (std::min)(1.0f, length); const float clamped_length = (std::min)(1.0f, length);
@ -174,8 +174,8 @@ void Mouse::Move(int x, int y, int center_x, int center_y) {
Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity; Settings::values.mouse_panning_x_sensitivity.GetValue() * default_stick_sensitivity;
const float y_sensitivity = const float y_sensitivity =
Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity; Settings::values.mouse_panning_y_sensitivity.GetValue() * default_stick_sensitivity;
SetAxis(identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity); SetAxis(mouse_identifier, mouse_axis_x, static_cast<float>(mouse_move.x) * x_sensitivity);
SetAxis(identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity); SetAxis(mouse_identifier, mouse_axis_y, static_cast<float>(-mouse_move.y) * y_sensitivity);
last_motion_change = { last_motion_change = {
static_cast<float>(-mouse_move.y) * x_sensitivity, static_cast<float>(-mouse_move.y) * x_sensitivity,
@ -196,7 +196,7 @@ void Mouse::TouchMove(f32 touch_x, f32 touch_y) {
} }
void Mouse::PressButton(int x, int y, MouseButton button) { void Mouse::PressButton(int x, int y, MouseButton button) {
SetButton(identifier, static_cast<int>(button), true); SetButton(mouse_identifier, static_cast<int>(button), true);
// Set initial analog parameters // Set initial analog parameters
mouse_origin = {x, y}; mouse_origin = {x, y};
@ -215,13 +215,13 @@ void Mouse::PressTouchButton(f32 touch_x, f32 touch_y, MouseButton button) {
} }
void Mouse::ReleaseButton(MouseButton button) { void Mouse::ReleaseButton(MouseButton button) {
SetButton(identifier, static_cast<int>(button), false); SetButton(mouse_identifier, static_cast<int>(button), false);
SetButton(real_mouse_identifier, static_cast<int>(button), false); SetButton(real_mouse_identifier, static_cast<int>(button), false);
SetButton(touch_identifier, static_cast<int>(button), false); SetButton(touch_identifier, static_cast<int>(button), false);
if (!IsMousePanningEnabled()) { if (!IsMousePanningEnabled()) {
SetAxis(identifier, mouse_axis_x, 0); SetAxis(mouse_identifier, mouse_axis_x, 0);
SetAxis(identifier, mouse_axis_y, 0); SetAxis(mouse_identifier, mouse_axis_y, 0);
} }
last_motion_change.x = 0; last_motion_change.x = 0;
@ -234,8 +234,8 @@ void Mouse::MouseWheelChange(int x, int y) {
wheel_position.x += x; wheel_position.x += x;
wheel_position.y += y; wheel_position.y += y;
last_motion_change.z += static_cast<f32>(y); last_motion_change.z += static_cast<f32>(y);
SetAxis(identifier, wheel_axis_x, static_cast<f32>(wheel_position.x)); SetAxis(mouse_identifier, wheel_axis_x, static_cast<f32>(wheel_position.x));
SetAxis(identifier, wheel_axis_y, static_cast<f32>(wheel_position.y)); SetAxis(mouse_identifier, wheel_axis_y, static_cast<f32>(wheel_position.y));
} }
void Mouse::ReleaseAllButtons() { void Mouse::ReleaseAllButtons() {

View file

@ -1,3 +1,6 @@
// 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
@ -6,14 +9,14 @@
namespace InputCommon { namespace InputCommon {
constexpr PadIdentifier identifier = { constexpr PadIdentifier touch_screen_identifier = {
.guid = Common::UUID{}, .guid = Common::UUID{},
.port = 0, .port = 0,
.pad = 0, .pad = 0,
}; };
TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) { TouchScreen::TouchScreen(std::string input_engine_) : InputEngine(std::move(input_engine_)) {
PreSetController(identifier); PreSetController(touch_screen_identifier);
ReleaseAllTouch(); ReleaseAllTouch();
} }
@ -26,9 +29,9 @@ void TouchScreen::TouchMoved(float x, float y, std::size_t finger_id) {
} }
const auto i = index.value(); const auto i = index.value();
fingers[i].is_active = true; fingers[i].is_active = true;
SetButton(identifier, static_cast<int>(i), true); SetButton(touch_screen_identifier, static_cast<int>(i), true);
SetAxis(identifier, static_cast<int>(i * 2), x); SetAxis(touch_screen_identifier, static_cast<int>(i * 2), x);
SetAxis(identifier, static_cast<int>(i * 2 + 1), y); SetAxis(touch_screen_identifier, static_cast<int>(i * 2 + 1), y);
} }
void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) { void TouchScreen::TouchPressed(float x, float y, std::size_t finger_id) {
@ -55,9 +58,9 @@ void TouchScreen::TouchReleased(std::size_t finger_id) {
} }
const auto i = index.value(); const auto i = index.value();
fingers[i].is_enabled = false; fingers[i].is_enabled = false;
SetButton(identifier, static_cast<int>(i), false); SetButton(touch_screen_identifier, static_cast<int>(i), false);
SetAxis(identifier, static_cast<int>(i * 2), 0.0f); SetAxis(touch_screen_identifier, static_cast<int>(i * 2), 0.0f);
SetAxis(identifier, static_cast<int>(i * 2 + 1), 0.0f); SetAxis(touch_screen_identifier, static_cast<int>(i * 2 + 1), 0.0f);
} }
std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const { std::optional<std::size_t> TouchScreen::GetIndexFromFingerId(std::size_t finger_id) const {

View file

@ -15,7 +15,7 @@
#include "input_common/drivers/virtual_amiibo.h" #include "input_common/drivers/virtual_amiibo.h"
namespace InputCommon { namespace InputCommon {
constexpr PadIdentifier identifier = { constexpr PadIdentifier virtual_amiibo_identifier = {
.guid = Common::UUID{}, .guid = Common::UUID{},
.port = 0, .port = 0,
.pad = 0, .pad = 0,
@ -228,13 +228,13 @@ VirtualAmiibo::Info VirtualAmiibo::LoadAmiibo(std::span<u8> data) {
status.state = Common::Input::NfcState::NewAmiibo, status.state = Common::Input::NfcState::NewAmiibo,
memcpy(nfc_data.data(), data.data(), data.size_bytes()); memcpy(nfc_data.data(), data.data(), data.size_bytes());
memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length); memcpy(status.uuid.data(), nfc_data.data(), status.uuid_length);
SetNfc(identifier, status); SetNfc(virtual_amiibo_identifier, status);
return Info::Success; return Info::Success;
} }
VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() { VirtualAmiibo::Info VirtualAmiibo::ReloadAmiibo() {
if (state == State::TagNearby) { if (state == State::TagNearby) {
SetNfc(identifier, status); SetNfc(virtual_amiibo_identifier, status);
return Info::Success; return Info::Success;
} }
@ -248,7 +248,7 @@ VirtualAmiibo::Info VirtualAmiibo::CloseAmiibo() {
state = State::WaitingForAmiibo; state = State::WaitingForAmiibo;
status.state = Common::Input::NfcState::AmiiboRemoved; status.state = Common::Input::NfcState::AmiiboRemoved;
SetNfc(identifier, status); SetNfc(virtual_amiibo_identifier, status);
status.tag_type = 0; status.tag_type = 0;
return Info::Success; return Info::Success;
} }

View file

@ -1,3 +1,6 @@
// 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
@ -9,14 +12,13 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
constexpr std::string_view SWIZZLE{"xyzw"};
void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite, void CompositeInsert(EmitContext& ctx, std::string_view result, std::string_view composite,
std::string_view object, u32 index) { std::string_view object, u32 index) {
if (result == composite) { if (result == composite) {
// The result is aliased with the composite // The result is aliased with the composite
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object); ctx.Add("{}.{}={};", composite, "xyzw"[index], object);
} else { } else {
ctx.Add("{}={};{}.{}={};", result, composite, result, SWIZZLE[index], object); ctx.Add("{}={};{}.{}={};", result, composite, result, "xyzw"[index], object);
} }
} }
} // Anonymous namespace } // Anonymous namespace
@ -38,17 +40,17 @@ void EmitCompositeConstructU32x4(EmitContext& ctx, IR::Inst& inst, std::string_v
void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]); ctx.AddU32("{}={}.{};", inst, composite, "xyzw"[index]);
} }
void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractU32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]); ctx.AddU32("{}={}.{};", inst, composite, "xyzw"[index]);
} }
void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractU32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddU32("{}={}.{};", inst, composite, SWIZZLE[index]); ctx.AddU32("{}={}.{};", inst, composite, "xyzw"[index]);
} }
void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeInsertU32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
@ -146,17 +148,17 @@ void EmitCompositeConstructF32x4(EmitContext& ctx, IR::Inst& inst, std::string_v
void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]); ctx.AddF32("{}={}.{};", inst, composite, "xyzw"[index]);
} }
void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractF32x3(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]); ctx.AddF32("{}={}.{};", inst, composite, "xyzw"[index]);
} }
void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeExtractF32x4(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
u32 index) { u32 index) {
ctx.AddF32("{}={}.{};", inst, composite, SWIZZLE[index]); ctx.AddF32("{}={}.{};", inst, composite, "xyzw"[index]);
} }
void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite, void EmitCompositeInsertF32x2(EmitContext& ctx, IR::Inst& inst, std::string_view composite,
@ -203,16 +205,16 @@ void EmitCompositeExtractF64x4([[maybe_unused]] EmitContext& ctx) {
void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object, void EmitCompositeInsertF64x2(EmitContext& ctx, std::string_view composite, std::string_view object,
u32 index) { u32 index) {
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object); ctx.Add("{}.{}={};", composite, "xyzw"[index], object);
} }
void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object, void EmitCompositeInsertF64x3(EmitContext& ctx, std::string_view composite, std::string_view object,
u32 index) { u32 index) {
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object); ctx.Add("{}.{}={};", composite, "xyzw"[index], object);
} }
void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object, void EmitCompositeInsertF64x4(EmitContext& ctx, std::string_view composite, std::string_view object,
u32 index) { u32 index) {
ctx.Add("{}.{}={};", composite, SWIZZLE[index], object); ctx.Add("{}.{}={};", composite, "xyzw"[index], object);
} }
} // namespace Shader::Backend::GLSL } // namespace Shader::Backend::GLSL

View file

@ -1,3 +1,6 @@
// 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
@ -11,14 +14,13 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
constexpr char SWIZZLE[]{"xyzw"};
u32 CbufIndex(u32 offset) { u32 CbufIndex(u32 offset) {
return (offset / 4) % 4; return (offset / 4) % 4;
} }
char OffsetSwizzle(u32 offset) { char OffsetSwizzle(u32 offset) {
return SWIZZLE[CbufIndex(offset)]; return "xyzw"[CbufIndex(offset)];
} }
bool IsInputArray(Stage stage) { bool IsInputArray(Stage stage) {

View file

@ -1,3 +1,6 @@
// 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
@ -10,14 +13,13 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
"if(cas_result==old_value){{break;}}}}"};
void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var, void SsboWriteCas(EmitContext& ctx, const IR::Value& binding, std::string_view offset_var,
std::string_view value, std::string_view bit_offset, u32 num_bits) { std::string_view value, std::string_view bit_offset, u32 num_bits) {
const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)}; const auto ssbo{fmt::format("{}_ssbo{}[{}>>2]", ctx.stage_name, binding.U32(), offset_var)};
ctx.Add(cas_loop, ssbo, ssbo, ssbo, value, bit_offset, num_bits); ctx.Add(
"for(;;){{uint old_value={};uint "
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
"if(cas_result==old_value){{break;}}}}", ssbo, ssbo, ssbo, value, bit_offset, num_bits);
} }
} // Anonymous namespace } // Anonymous namespace

View file

@ -1,3 +1,6 @@
// 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
@ -7,14 +10,13 @@
namespace Shader::Backend::GLSL { namespace Shader::Backend::GLSL {
namespace { namespace {
constexpr char cas_loop[]{"for(;;){{uint old_value={};uint "
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
"if(cas_result==old_value){{break;}}}}"};
void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value, void SharedWriteCas(EmitContext& ctx, std::string_view offset, std::string_view value,
std::string_view bit_offset, u32 num_bits) { std::string_view bit_offset, u32 num_bits) {
const auto smem{fmt::format("smem[{}>>2]", offset)}; const auto smem{fmt::format("smem[{}>>2]", offset)};
ctx.Add(cas_loop, smem, smem, smem, value, bit_offset, num_bits); ctx.Add(
"for(;;){{uint old_value={};uint "
"cas_result=atomicCompSwap({},old_value,bitfieldInsert({},{},{},{}));"
"if(cas_result==old_value){{break;}}}}", smem, smem, smem, value, bit_offset, num_bits);
} }
} // Anonymous namespace } // Anonymous namespace

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@ -9,7 +9,7 @@
namespace Shader::Backend::SPIRV { namespace Shader::Backend::SPIRV {
namespace { namespace {
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id op) { Id DecorateNoContraction(EmitContext& ctx, IR::Inst* inst, Id op) {
const auto flags{inst->Flags<IR::FpControl>()}; const auto flags{inst->Flags<IR::FpControl>()};
if (flags.no_contraction) { if (flags.no_contraction) {
ctx.Decorate(op, spv::Decoration::NoContraction); ctx.Decorate(op, spv::Decoration::NoContraction);
@ -61,27 +61,27 @@ Id EmitFPAbs64(EmitContext& ctx, Id value) {
} }
Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPAdd16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b)); return DecorateNoContraction(ctx, inst, ctx.OpFAdd(ctx.F16[1], a, b));
} }
Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPAdd32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b)); return DecorateNoContraction(ctx, inst, ctx.OpFAdd(ctx.F32[1], a, b));
} }
Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPAdd64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return Decorate(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b)); return DecorateNoContraction(ctx, inst, ctx.OpFAdd(ctx.F64[1], a, b));
} }
Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { Id EmitFPFma16(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
return Decorate(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c)); return DecorateNoContraction(ctx, inst, ctx.OpFma(ctx.F16[1], a, b, c));
} }
Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { Id EmitFPFma32(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
return Decorate(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c)); return DecorateNoContraction(ctx, inst, ctx.OpFma(ctx.F32[1], a, b, c));
} }
Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) { Id EmitFPFma64(EmitContext& ctx, IR::Inst* inst, Id a, Id b, Id c) {
return Decorate(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c)); return DecorateNoContraction(ctx, inst, ctx.OpFma(ctx.F64[1], a, b, c));
} }
Id EmitFPMax32(EmitContext& ctx, Id a, Id b) { Id EmitFPMax32(EmitContext& ctx, Id a, Id b) {
@ -101,15 +101,15 @@ Id EmitFPMin64(EmitContext& ctx, Id a, Id b) {
} }
Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPMul16(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return Decorate(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b)); return DecorateNoContraction(ctx, inst, ctx.OpFMul(ctx.F16[1], a, b));
} }
Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPMul32(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return Decorate(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b)); return DecorateNoContraction(ctx, inst, ctx.OpFMul(ctx.F32[1], a, b));
} }
Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) { Id EmitFPMul64(EmitContext& ctx, IR::Inst* inst, Id a, Id b) {
return Decorate(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b)); return DecorateNoContraction(ctx, inst, ctx.OpFMul(ctx.F64[1], a, b));
} }
Id EmitFPNeg16(EmitContext& ctx, Id value) { Id EmitFPNeg16(EmitContext& ctx, Id value) {

View file

@ -243,7 +243,7 @@ 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;
} }
Id Decorate(EmitContext& ctx, IR::Inst* inst, Id sample) { Id DecorateRelaxedPrecision(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) {
ctx.Decorate(sample, spv::Decoration::RelaxedPrecision); ctx.Decorate(sample, spv::Decoration::RelaxedPrecision);
@ -256,14 +256,14 @@ Id Emit(MethodPtrType sparse_ptr, MethodPtrType non_sparse_ptr, EmitContext& ctx
Id result_type, Args&&... args) { Id result_type, Args&&... args) {
IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)}; IR::Inst* const sparse{inst->GetAssociatedPseudoOperation(IR::Opcode::GetSparseFromOp)};
if (!sparse) { if (!sparse) {
return Decorate(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...)); return DecorateRelaxedPrecision(ctx, inst, (ctx.*non_sparse_ptr)(result_type, std::forward<Args>(args)...));
} }
const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)}; const Id struct_type{ctx.TypeStruct(ctx.U32[1], result_type)};
const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)}; const Id sample{(ctx.*sparse_ptr)(struct_type, std::forward<Args>(args)...)};
const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)}; const Id resident_code{ctx.OpCompositeExtract(ctx.U32[1], sample, 0U)};
sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code)); sparse->SetDefinition(ctx.OpImageSparseTexelsResident(ctx.U1, resident_code));
sparse->Invalidate(); sparse->Invalidate();
Decorate(ctx, inst, sample); DecorateRelaxedPrecision(ctx, inst, sample);
return ctx.OpCompositeExtract(result_type, sample, 1U); return ctx.OpCompositeExtract(result_type, sample, 1U);
} }

View file

@ -1,3 +1,6 @@
// 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
@ -12,7 +15,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
union Encoding { union EncodingIBTT {
u64 raw; u64 raw;
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> src_reg; BitField<8, 8, IR::Reg> src_reg;
@ -45,7 +48,7 @@ std::optional<u64> TrackLDC(Environment& env, Location block_begin, Location& po
std::optional<u64> TrackSHL(Environment& env, Location block_begin, Location& pos, std::optional<u64> TrackSHL(Environment& env, Location block_begin, Location& pos,
IR::Reg ldc_reg) { IR::Reg ldc_reg) {
return Track(env, block_begin, pos, [ldc_reg](u64 insn, Opcode opcode) { return Track(env, block_begin, pos, [ldc_reg](u64 insn, Opcode opcode) {
const Encoding shl{insn}; const EncodingIBTT shl{insn};
return opcode == Opcode::SHL_imm && shl.dest_reg == ldc_reg; return opcode == Opcode::SHL_imm && shl.dest_reg == ldc_reg;
}); });
} }
@ -53,7 +56,7 @@ std::optional<u64> TrackSHL(Environment& env, Location block_begin, Location& po
std::optional<u64> TrackIMNMX(Environment& env, Location block_begin, Location& pos, std::optional<u64> TrackIMNMX(Environment& env, Location block_begin, Location& pos,
IR::Reg shl_reg) { IR::Reg shl_reg) {
return Track(env, block_begin, pos, [shl_reg](u64 insn, Opcode opcode) { return Track(env, block_begin, pos, [shl_reg](u64 insn, Opcode opcode) {
const Encoding imnmx{insn}; const EncodingIBTT imnmx{insn};
return opcode == Opcode::IMNMX_imm && imnmx.dest_reg == shl_reg; return opcode == Opcode::IMNMX_imm && imnmx.dest_reg == shl_reg;
}); });
} }
@ -66,8 +69,8 @@ std::optional<IndirectBranchTableInfo> TrackIndirectBranchTable(Environment& env
if (brx_opcode != Opcode::BRX && brx_opcode != Opcode::JMX) { if (brx_opcode != Opcode::BRX && brx_opcode != Opcode::JMX) {
throw LogicError("Tracked instruction is not BRX or JMX"); throw LogicError("Tracked instruction is not BRX or JMX");
} }
const IR::Reg brx_reg{Encoding{brx_insn}.src_reg}; const IR::Reg brx_reg{EncodingIBTT{brx_insn}.src_reg};
const s32 brx_offset{static_cast<s32>(Encoding{brx_insn}.brx_offset)}; const s32 brx_offset{static_cast<s32>(EncodingIBTT{brx_insn}.brx_offset)};
Location pos{brx_pos}; Location pos{brx_pos};
const std::optional<u64> ldc_insn{TrackLDC(env, block_begin, pos, brx_reg)}; const std::optional<u64> ldc_insn{TrackLDC(env, block_begin, pos, brx_reg)};
@ -83,14 +86,14 @@ std::optional<IndirectBranchTableInfo> TrackIndirectBranchTable(Environment& env
if (!shl_insn) { if (!shl_insn) {
return std::nullopt; return std::nullopt;
} }
const Encoding shl{*shl_insn}; const EncodingIBTT shl{*shl_insn};
const IR::Reg shl_reg{shl.src_reg}; const IR::Reg shl_reg{shl.src_reg};
const std::optional<u64> imnmx_insn{TrackIMNMX(env, block_begin, pos, shl_reg)}; const std::optional<u64> imnmx_insn{TrackIMNMX(env, block_begin, pos, shl_reg)};
if (!imnmx_insn) { if (!imnmx_insn) {
return std::nullopt; return std::nullopt;
} }
const Encoding imnmx{*imnmx_insn}; const EncodingIBTT imnmx{*imnmx_insn};
if (imnmx.is_negative != 0) { if (imnmx.is_negative != 0) {
return std::nullopt; return std::nullopt;
} }

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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
@ -10,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class AtomOp : u64 { enum class AtomicGlobalMemoryOp : u64 {
ADD, ADD,
MIN, MIN,
MAX, MAX,
@ -32,33 +32,33 @@ enum class AtomSize : u64 {
S64, S64,
}; };
IR::U32U64 ApplyIntegerAtomOp(IR::IREmitter& ir, const IR::U32U64& offset, const IR::U32U64& op_b, AtomOp op, AtomSize size) { IR::U32U64 ApplyIntegerAtomOp(IR::IREmitter& ir, const IR::U32U64& offset, const IR::U32U64& op_b, AtomicGlobalMemoryOp op, AtomSize size) {
bool const is_signed = size == AtomSize::S64 || size == AtomSize::S32; bool const is_signed = size == AtomSize::S64 || size == AtomSize::S32;
switch (op) { switch (op) {
case AtomOp::ADD: case AtomicGlobalMemoryOp::ADD:
return ir.GlobalAtomicIAdd(offset, op_b); return ir.GlobalAtomicIAdd(offset, op_b);
case AtomOp::MIN: case AtomicGlobalMemoryOp::MIN:
return ir.GlobalAtomicIMin(offset, op_b, is_signed); return ir.GlobalAtomicIMin(offset, op_b, is_signed);
case AtomOp::MAX: case AtomicGlobalMemoryOp::MAX:
return ir.GlobalAtomicIMax(offset, op_b, is_signed); return ir.GlobalAtomicIMax(offset, op_b, is_signed);
case AtomOp::INC: case AtomicGlobalMemoryOp::INC:
return ir.GlobalAtomicInc(offset, op_b); return ir.GlobalAtomicInc(offset, op_b);
case AtomOp::DEC: case AtomicGlobalMemoryOp::DEC:
return ir.GlobalAtomicDec(offset, op_b); return ir.GlobalAtomicDec(offset, op_b);
case AtomOp::AND: case AtomicGlobalMemoryOp::AND:
return ir.GlobalAtomicAnd(offset, op_b); return ir.GlobalAtomicAnd(offset, op_b);
case AtomOp::OR: case AtomicGlobalMemoryOp::OR:
return ir.GlobalAtomicOr(offset, op_b); return ir.GlobalAtomicOr(offset, op_b);
case AtomOp::XOR: case AtomicGlobalMemoryOp::XOR:
return ir.GlobalAtomicXor(offset, op_b); return ir.GlobalAtomicXor(offset, op_b);
case AtomOp::EXCH: case AtomicGlobalMemoryOp::EXCH:
return ir.GlobalAtomicExchange(offset, op_b); return ir.GlobalAtomicExchange(offset, op_b);
default: default:
throw NotImplementedException("Integer Atom Operation {}", op); throw NotImplementedException("Integer Atom Operation {}", op);
} }
} }
IR::Value ApplyFpAtomOp(IR::IREmitter& ir, const IR::U64& offset, const IR::Value& op_b, AtomOp op, IR::Value ApplyFpAtomOp(IR::IREmitter& ir, const IR::U64& offset, const IR::Value& op_b, AtomicGlobalMemoryOp op,
AtomSize size) { AtomSize size) {
static constexpr IR::FpControl f16_control{ static constexpr IR::FpControl f16_control{
.no_contraction = false, .no_contraction = false,
@ -71,12 +71,12 @@ IR::Value ApplyFpAtomOp(IR::IREmitter& ir, const IR::U64& offset, const IR::Valu
.fmz_mode = IR::FmzMode::FTZ, .fmz_mode = IR::FmzMode::FTZ,
}; };
switch (op) { switch (op) {
case AtomOp::ADD: case AtomicGlobalMemoryOp::ADD:
return size == AtomSize::F32 ? ir.GlobalAtomicF32Add(offset, op_b, f32_control) return size == AtomSize::F32 ? ir.GlobalAtomicF32Add(offset, op_b, f32_control)
: ir.GlobalAtomicF16x2Add(offset, op_b, f16_control); : ir.GlobalAtomicF16x2Add(offset, op_b, f16_control);
case AtomOp::MIN: case AtomicGlobalMemoryOp::MIN:
return ir.GlobalAtomicF16x2Min(offset, op_b, f16_control); return ir.GlobalAtomicF16x2Min(offset, op_b, f16_control);
case AtomOp::MAX: case AtomicGlobalMemoryOp::MAX:
return ir.GlobalAtomicF16x2Max(offset, op_b, f16_control); return ir.GlobalAtomicF16x2Max(offset, op_b, f16_control);
default: default:
throw NotImplementedException("FP Atom Operation {}", op); throw NotImplementedException("FP Atom Operation {}", op);
@ -112,19 +112,19 @@ IR::U64 AtomOffset(TranslatorVisitor& v, u64 insn) {
// ADD, INC, DEC for S64 does nothing // ADD, INC, DEC for S64 does nothing
// Only ADD does something for F32 // Only ADD does something for F32
// Only ADD, MIN and MAX does something for F16x2 // Only ADD, MIN and MAX does something for F16x2
bool AtomOpNotApplicable(AtomSize size, AtomOp op) { bool AtomOpNotApplicable(AtomSize size, AtomicGlobalMemoryOp op) {
// TODO: SAFEADD // TODO: SAFEADD
switch (size) { switch (size) {
case AtomSize::U32: case AtomSize::U32:
case AtomSize::S32: case AtomSize::S32:
case AtomSize::U64: case AtomSize::U64:
return (op == AtomOp::INC || op == AtomOp::DEC); return (op == AtomicGlobalMemoryOp::INC || op == AtomicGlobalMemoryOp::DEC);
case AtomSize::S64: case AtomSize::S64:
return (op == AtomOp::ADD || op == AtomOp::INC || op == AtomOp::DEC); return (op == AtomicGlobalMemoryOp::ADD || op == AtomicGlobalMemoryOp::INC || op == AtomicGlobalMemoryOp::DEC);
case AtomSize::F32: case AtomSize::F32:
return op != AtomOp::ADD; return op != AtomicGlobalMemoryOp::ADD;
case AtomSize::F16x2: case AtomSize::F16x2:
return !(op == AtomOp::ADD || op == AtomOp::MIN || op == AtomOp::MAX); return !(op == AtomicGlobalMemoryOp::ADD || op == AtomicGlobalMemoryOp::MIN || op == AtomicGlobalMemoryOp::MAX);
default: default:
return false; return false;
} }
@ -162,7 +162,7 @@ void StoreResult(TranslatorVisitor& v, IR::Reg dest_reg, const IR::Value& result
} }
IR::Value ApplyAtomOp(TranslatorVisitor& v, IR::Reg operand_reg, const IR::U64& offset, IR::Value ApplyAtomOp(TranslatorVisitor& v, IR::Reg operand_reg, const IR::U64& offset,
AtomSize size, AtomOp op) { AtomSize size, AtomicGlobalMemoryOp op) {
switch (size) { switch (size) {
case AtomSize::U32: case AtomSize::U32:
case AtomSize::S32: case AtomSize::S32:
@ -180,7 +180,7 @@ IR::Value ApplyAtomOp(TranslatorVisitor& v, IR::Reg operand_reg, const IR::U64&
} }
void GlobalAtomic(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, void GlobalAtomic(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg,
const IR::U64& offset, AtomSize size, AtomOp op, bool write_dest) { const IR::U64& offset, AtomSize size, AtomicGlobalMemoryOp op, bool write_dest) {
IR::Value result = AtomOpNotApplicable(size, op) IR::Value result = AtomOpNotApplicable(size, op)
? LoadGlobal(v.ir, offset, size) ? LoadGlobal(v.ir, offset, size)
: ApplyAtomOp(v, operand_reg, offset, size, op); : ApplyAtomOp(v, operand_reg, offset, size, op);
@ -195,7 +195,7 @@ void TranslatorVisitor::ATOM(u64 insn) {
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<20, 8, IR::Reg> operand_reg; BitField<20, 8, IR::Reg> operand_reg;
BitField<49, 3, AtomSize> size; BitField<49, 3, AtomSize> size;
BitField<52, 4, AtomOp> op; BitField<52, 4, AtomicGlobalMemoryOp> op;
} const atom{insn}; } const atom{insn};
const IR::U64 offset{AtomOffset(*this, insn)}; const IR::U64 offset{AtomOffset(*this, insn)};
GlobalAtomic(*this, atom.dest_reg, atom.operand_reg, offset, atom.size, atom.op, true); GlobalAtomic(*this, atom.dest_reg, atom.operand_reg, offset, atom.size, atom.op, true);
@ -206,7 +206,7 @@ void TranslatorVisitor::RED(u64 insn) {
u64 raw; u64 raw;
BitField<0, 8, IR::Reg> operand_reg; BitField<0, 8, IR::Reg> operand_reg;
BitField<20, 3, AtomSize> size; BitField<20, 3, AtomSize> size;
BitField<23, 3, AtomOp> op; BitField<23, 3, AtomicGlobalMemoryOp> op;
} const red{insn}; } const red{insn};
const IR::U64 offset{AtomOffset(*this, insn)}; const IR::U64 offset{AtomOffset(*this, insn)};
GlobalAtomic(*this, IR::Reg::RZ, red.operand_reg, offset, red.size, red.op, true); GlobalAtomic(*this, IR::Reg::RZ, red.operand_reg, offset, red.size, red.op, true);

View file

@ -1,3 +1,6 @@
// 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
@ -7,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class AtomOp : u64 { enum class AtomicSharedMemoryOp : u64 {
ADD, ADD,
MIN, MIN,
MAX, MAX,
@ -25,26 +28,25 @@ enum class AtomsSize : u64 {
U64, U64,
}; };
IR::U32U64 ApplyAtomsOp(IR::IREmitter& ir, const IR::U32& offset, const IR::U32U64& op_b, AtomOp op, IR::U32U64 ApplyAtomsOp(IR::IREmitter& ir, const IR::U32& offset, const IR::U32U64& op_b, AtomicSharedMemoryOp op, bool is_signed) {
bool is_signed) {
switch (op) { switch (op) {
case AtomOp::ADD: case AtomicSharedMemoryOp::ADD:
return ir.SharedAtomicIAdd(offset, op_b); return ir.SharedAtomicIAdd(offset, op_b);
case AtomOp::MIN: case AtomicSharedMemoryOp::MIN:
return ir.SharedAtomicIMin(offset, op_b, is_signed); return ir.SharedAtomicIMin(offset, op_b, is_signed);
case AtomOp::MAX: case AtomicSharedMemoryOp::MAX:
return ir.SharedAtomicIMax(offset, op_b, is_signed); return ir.SharedAtomicIMax(offset, op_b, is_signed);
case AtomOp::INC: case AtomicSharedMemoryOp::INC:
return ir.SharedAtomicInc(offset, op_b); return ir.SharedAtomicInc(offset, op_b);
case AtomOp::DEC: case AtomicSharedMemoryOp::DEC:
return ir.SharedAtomicDec(offset, op_b); return ir.SharedAtomicDec(offset, op_b);
case AtomOp::AND: case AtomicSharedMemoryOp::AND:
return ir.SharedAtomicAnd(offset, op_b); return ir.SharedAtomicAnd(offset, op_b);
case AtomOp::OR: case AtomicSharedMemoryOp::OR:
return ir.SharedAtomicOr(offset, op_b); return ir.SharedAtomicOr(offset, op_b);
case AtomOp::XOR: case AtomicSharedMemoryOp::XOR:
return ir.SharedAtomicXor(offset, op_b); return ir.SharedAtomicXor(offset, op_b);
case AtomOp::EXCH: case AtomicSharedMemoryOp::EXCH:
return ir.SharedAtomicExchange(offset, op_b); return ir.SharedAtomicExchange(offset, op_b);
default: default:
throw NotImplementedException("Integer Atoms Operation {}", op); throw NotImplementedException("Integer Atoms Operation {}", op);
@ -87,11 +89,11 @@ void TranslatorVisitor::ATOMS(u64 insn) {
BitField<8, 8, IR::Reg> addr_reg; BitField<8, 8, IR::Reg> addr_reg;
BitField<20, 8, IR::Reg> src_reg_b; BitField<20, 8, IR::Reg> src_reg_b;
BitField<28, 2, AtomsSize> size; BitField<28, 2, AtomsSize> size;
BitField<52, 4, AtomOp> op; BitField<52, 4, AtomicSharedMemoryOp> op;
} const atoms{insn}; } const atoms{insn};
const bool size_64{atoms.size == AtomsSize::U64}; const bool size_64{atoms.size == AtomsSize::U64};
if (size_64 && atoms.op != AtomOp::EXCH) { if (size_64 && atoms.op != AtomicSharedMemoryOp::EXCH) {
throw NotImplementedException("64-bit Atoms Operation {}", atoms.op.Value()); throw NotImplementedException("64-bit Atoms Operation {}", atoms.op.Value());
} }
const bool is_signed{atoms.size == AtomsSize::S32}; const bool is_signed{atoms.size == AtomsSize::S32};

View file

@ -1,3 +1,6 @@
// 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
@ -7,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Mode : u64 { enum class FPReduceMode : u64 {
SINCOS, SINCOS,
EX2, EX2,
}; };
@ -16,7 +19,7 @@ void RRO(TranslatorVisitor& v, u64 insn, const IR::F32& src) {
union { union {
u64 raw; u64 raw;
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<39, 1, Mode> mode; BitField<39, 1, FPReduceMode> mode;
BitField<45, 1, u64> neg; BitField<45, 1, u64> neg;
BitField<49, 1, u64> abs; BitField<49, 1, u64> abs;
} const rro{insn}; } const rro{insn};

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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
@ -24,7 +24,7 @@ enum class IntFormat : u64 {
U64 = 3, U64 = 3,
}; };
union Encoding { union EncodingIFPC {
u64 raw; u64 raw;
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 2, FloatFormat> float_format; BitField<8, 2, FloatFormat> float_format;
@ -38,7 +38,7 @@ union Encoding {
}; };
bool Is64(u64 insn) { bool Is64(u64 insn) {
return Encoding{insn}.int_format == IntFormat::U64; return EncodingIFPC{insn}.int_format == IntFormat::U64;
} }
int BitSize(FloatFormat format) { int BitSize(FloatFormat format) {
@ -62,7 +62,7 @@ IR::U32 SmallAbs(TranslatorVisitor& v, const IR::U32& value, int bitsize) {
} }
void I2F(TranslatorVisitor& v, u64 insn, IR::U32U64 src) { void I2F(TranslatorVisitor& v, u64 insn, IR::U32U64 src) {
const Encoding i2f{insn}; const EncodingIFPC i2f{insn};
if (i2f.cc != 0) { if (i2f.cc != 0) {
throw NotImplementedException("I2F CC"); throw NotImplementedException("I2F CC");
} }

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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
@ -10,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Mode : u64 { enum class ISBERDMode : u64 {
Default, Default,
Patch, Patch,
Prim, Prim,
@ -63,7 +63,7 @@ void TranslatorVisitor::ISBERD(u64 insn) {
BitField<24, 8, u32> imm; BitField<24, 8, u32> imm;
BitField<31, 1, u64> skew; BitField<31, 1, u64> skew;
BitField<32, 1, u64> o; BitField<32, 1, u64> o;
BitField<33, 2, Mode> mode; BitField<33, 2, ISBERDMode> mode;
BitField<36, 4, SZ> sz; BitField<36, 4, SZ> sz;
BitField<47, 2, Shift> shift; BitField<47, 2, Shift> shift;
} const isberd{insn}; } const isberd{insn};
@ -95,18 +95,18 @@ void TranslatorVisitor::ISBERD(u64 insn) {
return; return;
} }
if (isberd.mode.Value() != Mode::Default) { if (isberd.mode.Value() != ISBERDMode::Default) {
if (isberd.skew.Value()) { if (isberd.skew.Value()) {
index = ir.IAdd(index, skewBytes(ir, SZ::U32)); index = ir.IAdd(index, skewBytes(ir, SZ::U32));
} }
IR::F32 float_index{}; IR::F32 float_index{};
switch (isberd.mode.Value()) { switch (isberd.mode.Value()) {
case Mode::Patch: float_index = ir.GetPatch(index.Patch()); case ISBERDMode::Patch: float_index = ir.GetPatch(index.Patch());
break; break;
case Mode::Prim: float_index = ir.GetAttribute(index.Attribute()); case ISBERDMode::Prim: float_index = ir.GetAttribute(index.Attribute());
break; break;
case Mode::Attr: float_index = ir.GetAttributeIndexed(index); case ISBERDMode::Attr: float_index = ir.GetAttributeIndexed(index);
break; break;
default: UNREACHABLE(); default: UNREACHABLE();
} }

View file

@ -1,3 +1,6 @@
// 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
@ -9,7 +12,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Size : u64 { enum class InterpolationSize : u64 {
B32, B32,
B64, B64,
B96, B96,
@ -29,15 +32,15 @@ enum class SampleMode : u64 {
Offset, Offset,
}; };
u32 NumElements(Size size) { u32 NumElements(InterpolationSize size) {
switch (size) { switch (size) {
case Size::B32: case InterpolationSize::B32:
return 1; return 1;
case Size::B64: case InterpolationSize::B64:
return 2; return 2;
case Size::B96: case InterpolationSize::B96:
return 3; return 3;
case Size::B128: case InterpolationSize::B128:
return 4; return 4;
} }
throw InvalidArgument("Invalid size {}", size); throw InvalidArgument("Invalid size {}", size);
@ -65,7 +68,7 @@ void TranslatorVisitor::ALD(u64 insn) {
BitField<39, 8, IR::Reg> vertex_reg; BitField<39, 8, IR::Reg> vertex_reg;
BitField<32, 1, u64> o; BitField<32, 1, u64> o;
BitField<31, 1, u64> patch; BitField<31, 1, u64> patch;
BitField<47, 2, Size> size; BitField<47, 2, InterpolationSize> size;
} const ald{insn}; } const ald{insn};
const u64 offset{ald.absolute_offset.Value()}; const u64 offset{ald.absolute_offset.Value()};
@ -103,7 +106,7 @@ void TranslatorVisitor::AST(u64 insn) {
BitField<20, 11, s64> relative_offset; BitField<20, 11, s64> relative_offset;
BitField<31, 1, u64> patch; BitField<31, 1, u64> patch;
BitField<39, 8, IR::Reg> vertex_reg; BitField<39, 8, IR::Reg> vertex_reg;
BitField<47, 2, Size> size; BitField<47, 2, InterpolationSize> size;
} const ast{insn}; } const ast{insn};
if (ast.index_reg != IR::Reg::RZ) { if (ast.index_reg != IR::Reg::RZ) {

View file

@ -1,3 +1,6 @@
// 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
@ -7,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Size : u64 { enum class LoadStoreLocalSharedSize : u64 {
U8, U8,
S8, S8,
U16, U16,
@ -45,23 +48,23 @@ std::pair<IR::U32, IR::U32> WordOffset(TranslatorVisitor& v, u64 insn) {
std::pair<int, bool> GetSize(u64 insn) { std::pair<int, bool> GetSize(u64 insn) {
union { union {
u64 raw; u64 raw;
BitField<48, 3, Size> size; BitField<48, 3, LoadStoreLocalSharedSize> size;
} const encoding{insn}; } const encoding{insn};
switch (encoding.size) { switch (encoding.size) {
case Size::U8: case LoadStoreLocalSharedSize::U8:
return {8, false}; return {8, false};
case Size::S8: case LoadStoreLocalSharedSize::S8:
return {8, true}; return {8, true};
case Size::U16: case LoadStoreLocalSharedSize::U16:
return {16, false}; return {16, false};
case Size::S16: case LoadStoreLocalSharedSize::S16:
return {16, true}; return {16, true};
case Size::B32: case LoadStoreLocalSharedSize::B32:
return {32, false}; return {32, false};
case Size::B64: case LoadStoreLocalSharedSize::B64:
return {64, false}; return {64, false};
case Size::B128: case LoadStoreLocalSharedSize::B128:
return {128, false}; return {128, false};
default: default:
throw NotImplementedException("Invalid size {}", encoding.size.Value()); throw NotImplementedException("Invalid size {}", encoding.size.Value());

View file

@ -1,3 +1,6 @@
// 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
@ -7,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Mode : u64 { enum class MovePredicateFlagMode : u64 {
PR, PR,
CC, CC,
}; };
@ -26,12 +29,12 @@ void TranslatorVisitor::P2R_imm(u64 insn) {
u64 raw; u64 raw;
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> src; BitField<8, 8, IR::Reg> src;
BitField<40, 1, Mode> mode; BitField<40, 1, MovePredicateFlagMode> mode;
BitField<41, 2, u64> byte_selector; BitField<41, 2, u64> byte_selector;
} const p2r{insn}; } const p2r{insn};
const u32 mask{GetImm20(insn).U32()}; const u32 mask{GetImm20(insn).U32()};
const bool pr_mode{p2r.mode == Mode::PR}; const bool pr_mode{p2r.mode == MovePredicateFlagMode::PR};
const u32 num_items{pr_mode ? 7U : 4U}; const u32 num_items{pr_mode ? 7U : 4U};
const u32 offset{static_cast<u32>(p2r.byte_selector) * 8}; const u32 offset{static_cast<u32>(p2r.byte_selector) * 8};
IR::U32 insert{ir.Imm32(0)}; IR::U32 insert{ir.Imm32(0)};

View file

@ -1,3 +1,6 @@
// 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
@ -7,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Mode : u64 { enum class PredicateFlagMode : u64 {
PR, PR,
CC, CC,
}; };
@ -31,12 +34,12 @@ void R2P(TranslatorVisitor& v, u64 insn, const IR::U32& mask) {
union { union {
u64 raw; u64 raw;
BitField<8, 8, IR::Reg> src_reg; BitField<8, 8, IR::Reg> src_reg;
BitField<40, 1, Mode> mode; BitField<40, 1, PredicateFlagMode> mode;
BitField<41, 2, u64> byte_selector; BitField<41, 2, u64> byte_selector;
} const r2p{insn}; } const r2p{insn};
const IR::U32 src{v.X(r2p.src_reg)}; const IR::U32 src{v.X(r2p.src_reg)};
const IR::U32 count{v.ir.Imm32(1)}; const IR::U32 count{v.ir.Imm32(1)};
const bool pr_mode{r2p.mode == Mode::PR}; const bool pr_mode{r2p.mode == PredicateFlagMode::PR};
const u32 num_items{pr_mode ? 7U : 4U}; const u32 num_items{pr_mode ? 7U : 4U};
const u32 offset_base{static_cast<u32>(r2p.byte_selector) * 8}; const u32 offset_base{static_cast<u32>(r2p.byte_selector) * 8};
for (u32 index = 0; index < num_items; ++index) { for (u32 index = 0; index < num_items; ++index) {

View file

@ -1,3 +1,6 @@
// 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
@ -7,7 +10,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Mode : u64 { enum class PixelLoadMode : u64 {
Default, Default,
CovMask, CovMask,
Covered, Covered,
@ -20,7 +23,7 @@ enum class Mode : u64 {
void TranslatorVisitor::PIXLD(u64 insn) { void TranslatorVisitor::PIXLD(u64 insn) {
union { union {
u64 raw; u64 raw;
BitField<31, 3, Mode> mode; BitField<31, 3, PixelLoadMode> mode;
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> addr_reg; BitField<8, 8, IR::Reg> addr_reg;
BitField<20, 8, s64> addr_offset; BitField<20, 8, s64> addr_offset;
@ -34,11 +37,11 @@ void TranslatorVisitor::PIXLD(u64 insn) {
throw NotImplementedException("Non-zero source register"); throw NotImplementedException("Non-zero source register");
} }
switch (pixld.mode) { switch (pixld.mode) {
case Mode::MyIndex: case PixelLoadMode::MyIndex:
X(pixld.dest_reg, ir.SampleId()); X(pixld.dest_reg, ir.SampleId());
break; break;
default: default:
throw NotImplementedException("Mode {}", pixld.mode.Value()); throw NotImplementedException("PixelLoadMode {}", pixld.mode.Value());
} }
} }

View file

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2026 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
@ -11,7 +11,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Type : u64 { enum class SurfaceAtomicType : u64 {
_1D = 0, _1D = 0,
_1D_BUFFER = 1, _1D_BUFFER = 1,
_1D_ARRAY = 2, _1D_ARRAY = 2,
@ -25,7 +25,7 @@ enum class Type : u64 {
/// For any would be newcomer to here: Yes - GPU dissasembly says S64 should /// For any would be newcomer to here: Yes - GPU dissasembly says S64 should
/// be after F16x2FTZRN. However if you do plan to revert this, you MUST test /// be after F16x2FTZRN. However if you do plan to revert this, you MUST test
/// ToTK beforehand. As the game will break with the subtle change /// ToTK beforehand. As the game will break with the subtle change
enum class Size : u64 { enum class SurfaceAtomicSize : u64 {
U32, U32,
S32, S32,
U64, U64,
@ -48,46 +48,46 @@ enum class AtomicOp : u64 {
EXCH, EXCH,
}; };
enum class Clamp : u64 { enum class SurfaceAtomicClamp : u64 {
IGN, IGN,
Default, Default,
TRAP, TRAP,
}; };
TextureType GetType(Type type) { TextureType GetType(SurfaceAtomicType type) {
switch (type) { switch (type) {
case Type::_1D: case SurfaceAtomicType::_1D:
return TextureType::Color1D; return TextureType::Color1D;
case Type::_1D_BUFFER: case SurfaceAtomicType::_1D_BUFFER:
return TextureType::Buffer; return TextureType::Buffer;
case Type::_1D_ARRAY: case SurfaceAtomicType::_1D_ARRAY:
return TextureType::ColorArray1D; return TextureType::ColorArray1D;
case Type::_2D: case SurfaceAtomicType::_2D:
return TextureType::Color2D; return TextureType::Color2D;
case Type::_2D_ARRAY: case SurfaceAtomicType::_2D_ARRAY:
return TextureType::ColorArray2D; return TextureType::ColorArray2D;
case Type::_3D: case SurfaceAtomicType::_3D:
return TextureType::Color3D; return TextureType::Color3D;
default: default:
throw NotImplementedException("Invalid type {}", type); throw NotImplementedException("Invalid type {}", type);
} }
} }
IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, Type type) { IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, SurfaceAtomicType type) {
const auto array{[&](int index) { const auto array{[&](int index) {
return v.ir.BitFieldExtract(v.X(reg + index), v.ir.Imm32(0), v.ir.Imm32(16)); return v.ir.BitFieldExtract(v.X(reg + index), v.ir.Imm32(0), v.ir.Imm32(16));
}}; }};
switch (type) { switch (type) {
case Type::_1D: case SurfaceAtomicType::_1D:
case Type::_1D_BUFFER: case SurfaceAtomicType::_1D_BUFFER:
return v.X(reg); return v.X(reg);
case Type::_1D_ARRAY: case SurfaceAtomicType::_1D_ARRAY:
return v.ir.CompositeConstruct(v.X(reg), array(1)); return v.ir.CompositeConstruct(v.X(reg), array(1));
case Type::_2D: case SurfaceAtomicType::_2D:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1));
case Type::_2D_ARRAY: case SurfaceAtomicType::_2D_ARRAY:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), array(2)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), array(2));
case Type::_3D: case SurfaceAtomicType::_3D:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2));
default: default:
throw NotImplementedException("Invalid type {}", type); throw NotImplementedException("Invalid type {}", type);
@ -121,11 +121,11 @@ IR::Value ApplyAtomicOp(IR::IREmitter& ir, const IR::U32& handle, const IR::Valu
} }
} }
ImageFormat Format(Size size) { ImageFormat Format(SurfaceAtomicSize size) {
switch (size) { switch (size) {
case Size::U32: case SurfaceAtomicSize::U32:
case Size::S32: case SurfaceAtomicSize::S32:
case Size::SD32: case SurfaceAtomicSize::SD32:
return ImageFormat::R32_UINT; return ImageFormat::R32_UINT;
default: default:
break; break;
@ -133,11 +133,11 @@ ImageFormat Format(Size size) {
throw NotImplementedException("Invalid size {}", size); throw NotImplementedException("Invalid size {}", size);
} }
bool IsSizeInt32(Size size) { bool IsSizeInt32(SurfaceAtomicSize size) {
switch (size) { switch (size) {
case Size::U32: case SurfaceAtomicSize::U32:
case Size::S32: case SurfaceAtomicSize::S32:
case Size::SD32: case SurfaceAtomicSize::SD32:
return true; return true;
default: default:
return false; return false;
@ -145,15 +145,15 @@ bool IsSizeInt32(Size size) {
} }
void ImageAtomOp(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, IR::Reg coord_reg, void ImageAtomOp(TranslatorVisitor& v, IR::Reg dest_reg, IR::Reg operand_reg, IR::Reg coord_reg,
std::optional<IR::Reg> bindless_reg, AtomicOp op, Clamp clamp, Size size, Type type, std::optional<IR::Reg> bindless_reg, AtomicOp op, SurfaceAtomicClamp clamp, SurfaceAtomicSize size, SurfaceAtomicType type,
u64 bound_offset, bool is_bindless, bool write_result) { u64 bound_offset, bool is_bindless, bool write_result) {
if (clamp != Clamp::IGN) { if (clamp != SurfaceAtomicClamp::IGN) {
throw NotImplementedException("Clamp {}", clamp); throw NotImplementedException("SurfaceAtomicClamp {}", clamp);
} }
if (!IsSizeInt32(size)) { if (!IsSizeInt32(size)) {
throw NotImplementedException("Size {}", size); throw NotImplementedException("SurfaceAtomicSize {}", size);
} }
const bool is_signed{size == Size::S32}; const bool is_signed{size == SurfaceAtomicSize::S32};
const ImageFormat format{Format(size)}; const ImageFormat format{Format(size)};
const TextureType tex_type{GetType(type)}; const TextureType tex_type{GetType(type)};
const IR::Value coords{MakeCoords(v, coord_reg, type)}; const IR::Value coords{MakeCoords(v, coord_reg, type)};
@ -178,9 +178,9 @@ void TranslatorVisitor::SUATOM(u64 insn) {
u64 raw; u64 raw;
BitField<54, 1, u64> is_bindless; BitField<54, 1, u64> is_bindless;
BitField<29, 4, AtomicOp> op; BitField<29, 4, AtomicOp> op;
BitField<33, 3, Type> type; BitField<33, 3, SurfaceAtomicType> type;
BitField<51, 3, Size> size; BitField<51, 3, SurfaceAtomicSize> size;
BitField<49, 2, Clamp> clamp; BitField<49, 2, SurfaceAtomicClamp> clamp;
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg; BitField<8, 8, IR::Reg> coord_reg;
BitField<20, 8, IR::Reg> operand_reg; BitField<20, 8, IR::Reg> operand_reg;
@ -199,9 +199,9 @@ void TranslatorVisitor::SURED(u64 insn) {
u64 raw; u64 raw;
BitField<51, 1, u64> is_bound; BitField<51, 1, u64> is_bound;
BitField<24, 3, AtomicOp> op; //OK - 24 (SURedOp) BitField<24, 3, AtomicOp> op; //OK - 24 (SURedOp)
BitField<33, 3, Type> type; //OK? - 33 (Dim) BitField<33, 3, SurfaceAtomicType> type; //OK? - 33 (Dim)
BitField<20, 3, Size> size; //? BitField<20, 3, SurfaceAtomicSize> size; //?
BitField<49, 2, Clamp> clamp; //OK - 49 (Clamp4) BitField<49, 2, SurfaceAtomicClamp> clamp; //OK - 49 (Clamp4)
BitField<0, 8, IR::Reg> operand_reg; //RA? BitField<0, 8, IR::Reg> operand_reg; //RA?
BitField<8, 8, IR::Reg> coord_reg; //RB? BitField<8, 8, IR::Reg> coord_reg; //RB?
BitField<36, 13, u64> bound_offset; //OK 33 (TidB) BitField<36, 13, u64> bound_offset; //OK 33 (TidB)

View file

@ -1,3 +1,6 @@
// 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
@ -11,7 +14,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Type : u64 { enum class SurfaceLoadStoreType : u64 {
_1D, _1D,
BUFFER_1D, BUFFER_1D,
ARRAY_1D, ARRAY_1D,
@ -44,7 +47,7 @@ constexpr std::array MASK{
R | G | B | A, // R | G | B | A, //
}; };
enum class Size : u64 { enum class SurfaceLoadStoreSize : u64 {
U8, U8,
S8, S8,
U16, U16,
@ -54,7 +57,7 @@ enum class Size : u64 {
B128, B128,
}; };
enum class Clamp : u64 { enum class SurfaceLoadStoreClamp : u64 {
IGN, IGN,
Default, Default,
TRAP, TRAP,
@ -75,75 +78,75 @@ enum class StoreCache : u64 {
WT, // Cache write-through (to system memory, volatile?) WT, // Cache write-through (to system memory, volatile?)
}; };
ImageFormat Format(Size size) { ImageFormat Format(SurfaceLoadStoreSize size) {
switch (size) { switch (size) {
case Size::U8: case SurfaceLoadStoreSize::U8:
return ImageFormat::R8_UINT; return ImageFormat::R8_UINT;
case Size::S8: case SurfaceLoadStoreSize::S8:
return ImageFormat::R8_SINT; return ImageFormat::R8_SINT;
case Size::U16: case SurfaceLoadStoreSize::U16:
return ImageFormat::R16_UINT; return ImageFormat::R16_UINT;
case Size::S16: case SurfaceLoadStoreSize::S16:
return ImageFormat::R16_SINT; return ImageFormat::R16_SINT;
case Size::B32: case SurfaceLoadStoreSize::B32:
return ImageFormat::R32_UINT; return ImageFormat::R32_UINT;
case Size::B64: case SurfaceLoadStoreSize::B64:
return ImageFormat::R32G32_UINT; return ImageFormat::R32G32_UINT;
case Size::B128: case SurfaceLoadStoreSize::B128:
return ImageFormat::R32G32B32A32_UINT; return ImageFormat::R32G32B32A32_UINT;
} }
throw NotImplementedException("Invalid size {}", size); throw NotImplementedException("Invalid size {}", size);
} }
int SizeInRegs(Size size) { int SizeInRegs(SurfaceLoadStoreSize size) {
switch (size) { switch (size) {
case Size::U8: case SurfaceLoadStoreSize::U8:
case Size::S8: case SurfaceLoadStoreSize::S8:
case Size::U16: case SurfaceLoadStoreSize::U16:
case Size::S16: case SurfaceLoadStoreSize::S16:
case Size::B32: case SurfaceLoadStoreSize::B32:
return 1; return 1;
case Size::B64: case SurfaceLoadStoreSize::B64:
return 2; return 2;
case Size::B128: case SurfaceLoadStoreSize::B128:
return 4; return 4;
} }
throw NotImplementedException("Invalid size {}", size); throw NotImplementedException("Invalid size {}", size);
} }
TextureType GetType(Type type) { TextureType GetType(SurfaceLoadStoreType type) {
switch (type) { switch (type) {
case Type::_1D: case SurfaceLoadStoreType::_1D:
return TextureType::Color1D; return TextureType::Color1D;
case Type::BUFFER_1D: case SurfaceLoadStoreType::BUFFER_1D:
return TextureType::Buffer; return TextureType::Buffer;
case Type::ARRAY_1D: case SurfaceLoadStoreType::ARRAY_1D:
return TextureType::ColorArray1D; return TextureType::ColorArray1D;
case Type::_2D: case SurfaceLoadStoreType::_2D:
return TextureType::Color2D; return TextureType::Color2D;
case Type::ARRAY_2D: case SurfaceLoadStoreType::ARRAY_2D:
return TextureType::ColorArray2D; return TextureType::ColorArray2D;
case Type::_3D: case SurfaceLoadStoreType::_3D:
return TextureType::Color3D; return TextureType::Color3D;
} }
throw NotImplementedException("Invalid type {}", type); throw NotImplementedException("Invalid type {}", type);
} }
IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, Type type) { IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, SurfaceLoadStoreType type) {
const auto array{[&](int index) { const auto array{[&](int index) {
return v.ir.BitFieldExtract(v.X(reg + index), v.ir.Imm32(0), v.ir.Imm32(16)); return v.ir.BitFieldExtract(v.X(reg + index), v.ir.Imm32(0), v.ir.Imm32(16));
}}; }};
switch (type) { switch (type) {
case Type::_1D: case SurfaceLoadStoreType::_1D:
case Type::BUFFER_1D: case SurfaceLoadStoreType::BUFFER_1D:
return v.X(reg); return v.X(reg);
case Type::ARRAY_1D: case SurfaceLoadStoreType::ARRAY_1D:
return v.ir.CompositeConstruct(v.X(reg), array(1)); return v.ir.CompositeConstruct(v.X(reg), array(1));
case Type::_2D: case SurfaceLoadStoreType::_2D:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1));
case Type::ARRAY_2D: case SurfaceLoadStoreType::ARRAY_2D:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), array(2)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), array(2));
case Type::_3D: case SurfaceLoadStoreType::_3D:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2));
} }
throw NotImplementedException("Invalid type {}", type); throw NotImplementedException("Invalid type {}", type);
@ -174,19 +177,19 @@ void TranslatorVisitor::SULD(u64 insn) {
BitField<51, 1, u64> is_bound; BitField<51, 1, u64> is_bound;
BitField<52, 1, u64> d; BitField<52, 1, u64> d;
BitField<23, 1, u64> ba; BitField<23, 1, u64> ba;
BitField<33, 3, Type> type; BitField<33, 3, SurfaceLoadStoreType> type;
BitField<24, 2, LoadCache> cache; BitField<24, 2, LoadCache> cache;
BitField<20, 3, Size> size; // .D BitField<20, 3, SurfaceLoadStoreSize> size; // .D
BitField<20, 4, u64> swizzle; // .P BitField<20, 4, u64> swizzle; // .P
BitField<49, 2, Clamp> clamp; BitField<49, 2, SurfaceLoadStoreClamp> clamp;
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg; BitField<8, 8, IR::Reg> coord_reg;
BitField<36, 13, u64> bound_offset; // is_bound BitField<36, 13, u64> bound_offset; // is_bound
BitField<39, 8, IR::Reg> bindless_reg; // !is_bound BitField<39, 8, IR::Reg> bindless_reg; // !is_bound
} const suld{insn}; } const suld{insn};
if (suld.clamp != Clamp::IGN) { if (suld.clamp != SurfaceLoadStoreClamp::IGN) {
throw NotImplementedException("Clamp {}", suld.clamp.Value()); throw NotImplementedException("SurfaceLoadStoreClamp {}", suld.clamp.Value());
} }
if (suld.cache != LoadCache::CA && suld.cache != LoadCache::CG) { if (suld.cache != LoadCache::CA && suld.cache != LoadCache::CG) {
throw NotImplementedException("Cache {}", suld.cache.Value()); throw NotImplementedException("Cache {}", suld.cache.Value());
@ -234,19 +237,19 @@ void TranslatorVisitor::SUST(u64 insn) {
BitField<51, 1, u64> is_bound; BitField<51, 1, u64> is_bound;
BitField<52, 1, u64> d; BitField<52, 1, u64> d;
BitField<23, 1, u64> ba; BitField<23, 1, u64> ba;
BitField<33, 3, Type> type; BitField<33, 3, SurfaceLoadStoreType> type;
BitField<24, 2, StoreCache> cache; BitField<24, 2, StoreCache> cache;
BitField<20, 3, Size> size; // .D BitField<20, 3, SurfaceLoadStoreSize> size; // .D
BitField<20, 4, u64> swizzle; // .P BitField<20, 4, u64> swizzle; // .P
BitField<49, 2, Clamp> clamp; BitField<49, 2, SurfaceLoadStoreClamp> clamp;
BitField<0, 8, IR::Reg> data_reg; BitField<0, 8, IR::Reg> data_reg;
BitField<8, 8, IR::Reg> coord_reg; BitField<8, 8, IR::Reg> coord_reg;
BitField<36, 13, u64> bound_offset; // is_bound BitField<36, 13, u64> bound_offset; // is_bound
BitField<39, 8, IR::Reg> bindless_reg; // !is_bound BitField<39, 8, IR::Reg> bindless_reg; // !is_bound
} const sust{insn}; } const sust{insn};
if (sust.clamp != Clamp::IGN) { if (sust.clamp != SurfaceLoadStoreClamp::IGN) {
throw NotImplementedException("Clamp {}", sust.clamp.Value()); throw NotImplementedException("SurfaceLoadStoreClamp {}", sust.clamp.Value());
} }
if (sust.cache != StoreCache::WB && sust.cache != StoreCache::CG) { if (sust.cache != StoreCache::WB && sust.cache != StoreCache::CG) {
throw NotImplementedException("Cache {}", sust.cache.Value()); throw NotImplementedException("Cache {}", sust.cache.Value());

View file

@ -1,3 +1,6 @@
// 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
@ -21,7 +24,7 @@ enum class Blod : u64 {
LLA, LLA,
}; };
enum class TextureType : u64 { enum class TextureFetchType : u64 {
_1D, _1D,
ARRAY_1D, ARRAY_1D,
_2D, _2D,
@ -32,46 +35,46 @@ enum class TextureType : u64 {
ARRAY_CUBE, ARRAY_CUBE,
}; };
Shader::TextureType GetType(TextureType type) { Shader::TextureType GetType(TextureFetchType type) {
switch (type) { switch (type) {
case TextureType::_1D: case TextureFetchType::_1D:
return Shader::TextureType::Color1D; return Shader::TextureType::Color1D;
case TextureType::ARRAY_1D: case TextureFetchType::ARRAY_1D:
return Shader::TextureType::ColorArray1D; return Shader::TextureType::ColorArray1D;
case TextureType::_2D: case TextureFetchType::_2D:
return Shader::TextureType::Color2D; return Shader::TextureType::Color2D;
case TextureType::ARRAY_2D: case TextureFetchType::ARRAY_2D:
return Shader::TextureType::ColorArray2D; return Shader::TextureType::ColorArray2D;
case TextureType::_3D: case TextureFetchType::_3D:
return Shader::TextureType::Color3D; return Shader::TextureType::Color3D;
case TextureType::ARRAY_3D: case TextureFetchType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureFetchType::CUBE:
return Shader::TextureType::ColorCube; return Shader::TextureType::ColorCube;
case TextureType::ARRAY_CUBE: case TextureFetchType::ARRAY_CUBE:
return Shader::TextureType::ColorArrayCube; return Shader::TextureType::ColorArrayCube;
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
} }
IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureType type) { IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureFetchType type) {
const auto read_array{[&]() -> IR::F32 { return v.ir.ConvertUToF(32, 16, v.X(reg)); }}; const auto read_array{[&]() -> IR::F32 { return v.ir.ConvertUToF(32, 16, v.X(reg)); }};
switch (type) { switch (type) {
case TextureType::_1D: case TextureFetchType::_1D:
return v.F(reg); return v.F(reg);
case TextureType::ARRAY_1D: case TextureFetchType::ARRAY_1D:
return v.ir.CompositeConstruct(v.F(reg + 1), read_array()); return v.ir.CompositeConstruct(v.F(reg + 1), read_array());
case TextureType::_2D: case TextureFetchType::_2D:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1));
case TextureType::ARRAY_2D: case TextureFetchType::ARRAY_2D:
return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), read_array()); return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), read_array());
case TextureType::_3D: case TextureFetchType::_3D:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
case TextureType::ARRAY_3D: case TextureFetchType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureFetchType::CUBE:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
case TextureType::ARRAY_CUBE: case TextureFetchType::ARRAY_CUBE:
return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), v.F(reg + 3), read_array()); return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), v.F(reg + 3), read_array());
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
@ -95,25 +98,25 @@ IR::F32 MakeLod(TranslatorVisitor& v, IR::Reg& reg, Blod blod) {
throw NotImplementedException("Invalid blod {}", blod); throw NotImplementedException("Invalid blod {}", blod);
} }
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureType type) { IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureFetchType type) {
const IR::U32 value{v.X(reg++)}; const IR::U32 value{v.X(reg++)};
switch (type) { switch (type) {
case TextureType::_1D: case TextureFetchType::_1D:
case TextureType::ARRAY_1D: case TextureFetchType::ARRAY_1D:
return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true); return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true);
case TextureType::_2D: case TextureFetchType::_2D:
case TextureType::ARRAY_2D: case TextureFetchType::ARRAY_2D:
return v.ir.CompositeConstruct( return v.ir.CompositeConstruct(
v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true), v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true));
case TextureType::_3D: case TextureFetchType::_3D:
case TextureType::ARRAY_3D: case TextureFetchType::ARRAY_3D:
return v.ir.CompositeConstruct( return v.ir.CompositeConstruct(
v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true), v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true), v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(4), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(4), true));
case TextureType::CUBE: case TextureFetchType::CUBE:
case TextureType::ARRAY_CUBE: case TextureFetchType::ARRAY_CUBE:
throw NotImplementedException("Illegal offset on CUBE sample"); throw NotImplementedException("Illegal offset on CUBE sample");
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
@ -141,7 +144,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool aoffi, Blod blod, bool lc,
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg; BitField<8, 8, IR::Reg> coord_reg;
BitField<20, 8, IR::Reg> meta_reg; BitField<20, 8, IR::Reg> meta_reg;
BitField<28, 3, TextureType> type; BitField<28, 3, TextureFetchType> type;
BitField<31, 4, u64> mask; BitField<31, 4, u64> mask;
} const tex{insn}; } const tex{insn};

View file

@ -1,3 +1,6 @@
// 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
@ -8,14 +11,14 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Precision : u64 { enum class TextureFetchSwizzledPrecision : u64 {
F16, F16,
F32, F32,
}; };
union Encoding { union EncodinTFS {
u64 raw; u64 raw;
BitField<59, 1, Precision> precision; BitField<59, 1, TextureFetchSwizzledPrecision> precision;
BitField<53, 4, u64> encoding; BitField<53, 4, u64> encoding;
BitField<49, 1, u64> nodep; BitField<49, 1, u64> nodep;
BitField<28, 8, IR::Reg> dest_reg_b; BitField<28, 8, IR::Reg> dest_reg_b;
@ -26,31 +29,7 @@ union Encoding {
BitField<50, 3, u64> swizzle; BitField<50, 3, u64> swizzle;
}; };
constexpr unsigned R = 1; void CheckAlignmentTFS(IR::Reg reg, size_t alignment) {
constexpr unsigned G = 2;
constexpr unsigned B = 4;
constexpr unsigned A = 8;
constexpr std::array RG_LUT{
R, //
G, //
B, //
A, //
R | G, //
R | A, //
G | A, //
B | A, //
};
constexpr std::array RGBA_LUT{
R | G | B, //
R | G | A, //
R | B | A, //
G | B | A, //
R | G | B | A, //
};
void CheckAlignment(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) { if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg); throw NotImplementedException("Unaligned source register {}", reg);
} }
@ -65,14 +44,14 @@ IR::F32 ReadArray(TranslatorVisitor& v, const IR::U32& value) {
return v.ir.ConvertUToF(32, 16, v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(16))); return v.ir.ConvertUToF(32, 16, v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(16)));
} }
IR::Value Sample(TranslatorVisitor& v, u64 insn) { IR::Value SampleTFS(TranslatorVisitor& v, u64 insn) {
const Encoding texs{insn}; const EncodinTFS texs{insn};
const IR::U32 handle{v.ir.Imm32(static_cast<u32>(texs.cbuf_offset * 4))}; const IR::U32 handle{v.ir.Imm32(static_cast<u32>(texs.cbuf_offset * 4))};
const IR::F32 zero{v.ir.Imm32(0.0f)}; const IR::F32 zero{v.ir.Imm32(0.0f)};
const IR::Reg reg_a{texs.src_reg_a}; const IR::Reg reg_a{texs.src_reg_a};
const IR::Reg reg_b{texs.src_reg_b}; const IR::Reg reg_b{texs.src_reg_b};
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
if (texs.precision == Precision::F16) { if (texs.precision == TextureFetchSwizzledPrecision::F16) {
info.relaxed_precision.Assign(1); info.relaxed_precision.Assign(1);
} }
switch (texs.encoding) { switch (texs.encoding) {
@ -86,67 +65,67 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info); return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_b), zero, {}, info);
case 3: // 2D.LL case 3: // 2D.LL
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {}, return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), {},
info); info);
case 4: // 2D.DC case 4: // 2D.DC
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1); info.is_depth.Assign(1);
return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), return v.ir.ImageSampleDrefImplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
{}, {}, {}, info); {}, {}, {}, info);
case 5: // 2D.LL.DC case 5: // 2D.LL.DC
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
CheckAlignment(reg_b, 2); CheckAlignmentTFS(reg_b, 2);
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1); info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1),
v.F(reg_b + 1), v.F(reg_b), {}, info); v.F(reg_b + 1), v.F(reg_b), {}, info);
case 6: // 2D.LZ.DC case 6: // 2D.LZ.DC
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
info.type.Assign(TextureType::Color2D); info.type.Assign(TextureType::Color2D);
info.is_depth.Assign(1); info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b), return v.ir.ImageSampleDrefExplicitLod(handle, Composite(v, reg_a, reg_a + 1), v.F(reg_b),
zero, {}, info); zero, {}, info);
case 7: // ARRAY_2D case 7: // ARRAY_2D
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
info.type.Assign(TextureType::ColorArray2D); info.type.Assign(TextureType::ColorArray2D);
return v.ir.ImageSampleImplicitLod( return v.ir.ImageSampleImplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
{}, {}, {}, info); {}, {}, {}, info);
case 8: // ARRAY_2D.LZ case 8: // ARRAY_2D.LZ
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
info.type.Assign(TextureType::ColorArray2D); info.type.Assign(TextureType::ColorArray2D);
return v.ir.ImageSampleExplicitLod( return v.ir.ImageSampleExplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
zero, {}, info); zero, {}, info);
case 9: // ARRAY_2D.LZ.DC case 9: // ARRAY_2D.LZ.DC
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
CheckAlignment(reg_b, 2); CheckAlignmentTFS(reg_b, 2);
info.type.Assign(TextureType::ColorArray2D); info.type.Assign(TextureType::ColorArray2D);
info.is_depth.Assign(1); info.is_depth.Assign(1);
return v.ir.ImageSampleDrefExplicitLod( return v.ir.ImageSampleDrefExplicitLod(
handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))), handle, v.ir.CompositeConstruct(v.F(reg_a + 1), v.F(reg_b), ReadArray(v, v.X(reg_a))),
v.F(reg_b + 1), zero, {}, info); v.F(reg_b + 1), zero, {}, info);
case 10: // 3D case 10: // 3D
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
info.type.Assign(TextureType::Color3D); info.type.Assign(TextureType::Color3D);
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {}, return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
{}, info); {}, info);
case 11: // 3D.LZ case 11: // 3D.LZ
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
info.type.Assign(TextureType::Color3D); info.type.Assign(TextureType::Color3D);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {}, return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), zero, {},
info); info);
case 12: // CUBE case 12: // CUBE
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
info.type.Assign(TextureType::ColorCube); info.type.Assign(TextureType::ColorCube);
return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {}, return v.ir.ImageSampleImplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), {}, {},
{}, info); {}, info);
case 13: // CUBE.LL case 13: // CUBE.LL
CheckAlignment(reg_a, 2); CheckAlignmentTFS(reg_a, 2);
CheckAlignment(reg_b, 2); CheckAlignmentTFS(reg_b, 2);
info.type.Assign(TextureType::ColorCube); info.type.Assign(TextureType::ColorCube);
return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b), return v.ir.ImageSampleExplicitLod(handle, Composite(v, reg_a, reg_a + 1, reg_b),
v.F(reg_b + 1), {}, info); v.F(reg_b + 1), {}, info);
@ -156,17 +135,40 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
} }
unsigned Swizzle(u64 insn) { unsigned Swizzle(u64 insn) {
const Encoding texs{insn}; #define R 1
#define G 2
#define B 4
#define A 8
constexpr std::array<unsigned, 8> RG_LUT{
R, //
G, //
B, //
A, //
R | G, //
R | A, //
G | A, //
B | A, //
};
constexpr std::array<unsigned, 5> RGBA_LUT{
R | G | B, //
R | G | A, //
R | B | A, //
G | B | A, //
R | G | B | A, //
};
#undef R
#undef G
#undef B
#undef A
const EncodinTFS texs{insn};
const size_t encoding{texs.swizzle}; const size_t encoding{texs.swizzle};
if (texs.dest_reg_b == IR::Reg::RZ) { if (texs.dest_reg_b == IR::Reg::RZ) {
if (encoding >= RG_LUT.size()) { if (encoding >= RG_LUT.size())
throw NotImplementedException("Illegal RG encoding {}", encoding); throw NotImplementedException("Illegal RG encoding {}", encoding);
}
return RG_LUT[encoding]; return RG_LUT[encoding];
} else { } else {
if (encoding >= RGBA_LUT.size()) { if (encoding >= RGBA_LUT.size())
throw NotImplementedException("Illegal RGBA encoding {}", encoding); throw NotImplementedException("Illegal RGBA encoding {}", encoding);
}
return RGBA_LUT[encoding]; return RGBA_LUT[encoding];
} }
} }
@ -182,23 +184,23 @@ IR::F32 Extract(TranslatorVisitor& v, const IR::Value& sample, unsigned componen
} }
IR::Reg RegStoreComponent32(u64 insn, unsigned index) { IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
const Encoding texs{insn}; const EncodinTFS texs{insn};
switch (index) { switch (index) {
case 0: case 0:
return texs.dest_reg_a; return texs.dest_reg_a;
case 1: case 1:
CheckAlignment(texs.dest_reg_a, 2); CheckAlignmentTFS(texs.dest_reg_a, 2);
return texs.dest_reg_a + 1; return texs.dest_reg_a + 1;
case 2: case 2:
return texs.dest_reg_b; return texs.dest_reg_b;
case 3: case 3:
CheckAlignment(texs.dest_reg_b, 2); CheckAlignmentTFS(texs.dest_reg_b, 2);
return texs.dest_reg_b + 1; return texs.dest_reg_b + 1;
} }
throw LogicError("Invalid store index {}", index); throw LogicError("Invalid store index {}", index);
} }
void Store32(TranslatorVisitor& v, u64 insn, const IR::Value& sample) { void Store32TFS(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
const unsigned swizzle{Swizzle(insn)}; const unsigned swizzle{Swizzle(insn)};
unsigned store_index{0}; unsigned store_index{0};
for (unsigned component = 0; component < 4; ++component) { for (unsigned component = 0; component < 4; ++component) {
@ -211,11 +213,11 @@ void Store32(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
} }
} }
IR::U32 Pack(TranslatorVisitor& v, const IR::F32& lhs, const IR::F32& rhs) { IR::U32 PackTFS(TranslatorVisitor& v, const IR::F32& lhs, const IR::F32& rhs) {
return v.ir.PackHalf2x16(v.ir.CompositeConstruct(lhs, rhs)); return v.ir.PackHalf2x16(v.ir.CompositeConstruct(lhs, rhs));
} }
void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) { void Store16TFS(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
const unsigned swizzle{Swizzle(insn)}; const unsigned swizzle{Swizzle(insn)};
unsigned store_index{0}; unsigned store_index{0};
std::array<IR::F32, 4> swizzled; std::array<IR::F32, 4> swizzled;
@ -227,23 +229,23 @@ void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
++store_index; ++store_index;
} }
const IR::F32 zero{v.ir.Imm32(0.0f)}; const IR::F32 zero{v.ir.Imm32(0.0f)};
const Encoding texs{insn}; const EncodinTFS texs{insn};
switch (store_index) { switch (store_index) {
case 1: case 1:
v.X(texs.dest_reg_a, Pack(v, swizzled[0], zero)); v.X(texs.dest_reg_a, PackTFS(v, swizzled[0], zero));
break; break;
case 2: case 2:
case 3: case 3:
case 4: case 4:
v.X(texs.dest_reg_a, Pack(v, swizzled[0], swizzled[1])); v.X(texs.dest_reg_a, PackTFS(v, swizzled[0], swizzled[1]));
switch (store_index) { switch (store_index) {
case 2: case 2:
break; break;
case 3: case 3:
v.X(texs.dest_reg_b, Pack(v, swizzled[2], zero)); v.X(texs.dest_reg_b, PackTFS(v, swizzled[2], zero));
break; break;
case 4: case 4:
v.X(texs.dest_reg_b, Pack(v, swizzled[2], swizzled[3])); v.X(texs.dest_reg_b, PackTFS(v, swizzled[2], swizzled[3]));
break; break;
} }
break; break;
@ -252,11 +254,11 @@ void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
} // Anonymous namespace } // Anonymous namespace
void TranslatorVisitor::TEXS(u64 insn) { void TranslatorVisitor::TEXS(u64 insn) {
const IR::Value sample{Sample(*this, insn)}; const IR::Value sample{SampleTFS(*this, insn)};
if (Encoding{insn}.precision == Precision::F32) { if (EncodinTFS{insn}.precision == TextureFetchSwizzledPrecision::F32) {
Store32(*this, insn, sample); Store32TFS(*this, insn, sample);
} else { } else {
Store16(*this, insn, sample); Store16TFS(*this, insn, sample);
} }
} }

View file

@ -1,3 +1,6 @@
// 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
@ -9,7 +12,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class TextureType : u64 { enum class TextureGatherType : u64 {
_1D, _1D,
ARRAY_1D, ARRAY_1D,
_2D, _2D,
@ -27,77 +30,77 @@ enum class OffsetType : u64 {
Invalid, Invalid,
}; };
enum class ComponentType : u64 { enum class TextureGatherComponentType : u64 {
R = 0, R = 0,
G = 1, G = 1,
B = 2, B = 2,
A = 3, A = 3,
}; };
Shader::TextureType GetType(TextureType type) { Shader::TextureType GetTextureGatherType(TextureGatherType type) {
switch (type) { switch (type) {
case TextureType::_1D: case TextureGatherType::_1D:
return Shader::TextureType::Color1D; return Shader::TextureType::Color1D;
case TextureType::ARRAY_1D: case TextureGatherType::ARRAY_1D:
return Shader::TextureType::ColorArray1D; return Shader::TextureType::ColorArray1D;
case TextureType::_2D: case TextureGatherType::_2D:
return Shader::TextureType::Color2D; return Shader::TextureType::Color2D;
case TextureType::ARRAY_2D: case TextureGatherType::ARRAY_2D:
return Shader::TextureType::ColorArray2D; return Shader::TextureType::ColorArray2D;
case TextureType::_3D: case TextureGatherType::_3D:
return Shader::TextureType::Color3D; return Shader::TextureType::Color3D;
case TextureType::ARRAY_3D: case TextureGatherType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureGatherType::CUBE:
return Shader::TextureType::ColorCube; return Shader::TextureType::ColorCube;
case TextureType::ARRAY_CUBE: case TextureGatherType::ARRAY_CUBE:
return Shader::TextureType::ColorArrayCube; return Shader::TextureType::ColorArrayCube;
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
} }
IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureType type) { IR::Value MakeTextureGatherCoords(TranslatorVisitor& v, IR::Reg reg, TextureGatherType type) {
const auto read_array{[&]() -> IR::F32 { return v.ir.ConvertUToF(32, 16, v.X(reg)); }}; const auto read_array{[&]() -> IR::F32 { return v.ir.ConvertUToF(32, 16, v.X(reg)); }};
switch (type) { switch (type) {
case TextureType::_1D: case TextureGatherType::_1D:
return v.F(reg); return v.F(reg);
case TextureType::ARRAY_1D: case TextureGatherType::ARRAY_1D:
return v.ir.CompositeConstruct(v.F(reg + 1), read_array()); return v.ir.CompositeConstruct(v.F(reg + 1), read_array());
case TextureType::_2D: case TextureGatherType::_2D:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1));
case TextureType::ARRAY_2D: case TextureGatherType::ARRAY_2D:
return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), read_array()); return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), read_array());
case TextureType::_3D: case TextureGatherType::_3D:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
case TextureType::ARRAY_3D: case TextureGatherType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureGatherType::CUBE:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
case TextureType::ARRAY_CUBE: case TextureGatherType::ARRAY_CUBE:
return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), v.F(reg + 3), read_array()); return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), v.F(reg + 3), read_array());
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
} }
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureType type) { IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureGatherType type) {
const IR::U32 value{v.X(reg++)}; const IR::U32 value{v.X(reg++)};
switch (type) { switch (type) {
case TextureType::_1D: case TextureGatherType::_1D:
case TextureType::ARRAY_1D: case TextureGatherType::ARRAY_1D:
return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true); return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true);
case TextureType::_2D: case TextureGatherType::_2D:
case TextureType::ARRAY_2D: case TextureGatherType::ARRAY_2D:
return v.ir.CompositeConstruct( return v.ir.CompositeConstruct(
v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true), v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true));
case TextureType::_3D: case TextureGatherType::_3D:
case TextureType::ARRAY_3D: case TextureGatherType::ARRAY_3D:
return v.ir.CompositeConstruct( return v.ir.CompositeConstruct(
v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true), v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(6), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true), v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(16), v.ir.Imm32(6), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(16), v.ir.Imm32(6), true));
case TextureType::CUBE: case TextureGatherType::CUBE:
case TextureType::ARRAY_CUBE: case TextureGatherType::ARRAY_CUBE:
throw NotImplementedException("Illegal offset on CUBE sample"); throw NotImplementedException("Illegal offset on CUBE sample");
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
@ -116,7 +119,7 @@ std::pair<IR::Value, IR::Value> MakeOffsetPTP(TranslatorVisitor& v, IR::Reg& reg
return {make_vector(value1), make_vector(value2)}; return {make_vector(value1), make_vector(value2)};
} }
void Impl(TranslatorVisitor& v, u64 insn, ComponentType component_type, OffsetType offset_type, void Impl(TranslatorVisitor& v, u64 insn, TextureGatherComponentType component_type, OffsetType offset_type,
bool is_bindless) { bool is_bindless) {
union { union {
u64 raw; u64 raw;
@ -127,12 +130,12 @@ void Impl(TranslatorVisitor& v, u64 insn, ComponentType component_type, OffsetTy
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg; BitField<8, 8, IR::Reg> coord_reg;
BitField<20, 8, IR::Reg> meta_reg; BitField<20, 8, IR::Reg> meta_reg;
BitField<28, 3, TextureType> type; BitField<28, 3, TextureGatherType> type;
BitField<31, 4, u64> mask; BitField<31, 4, u64> mask;
BitField<36, 13, u64> cbuf_offset; BitField<36, 13, u64> cbuf_offset;
} const tld4{insn}; } const tld4{insn};
const IR::Value coords{MakeCoords(v, tld4.coord_reg, tld4.type)}; const IR::Value coords{MakeTextureGatherCoords(v, tld4.coord_reg, tld4.type)};
IR::Reg meta_reg{tld4.meta_reg}; IR::Reg meta_reg{tld4.meta_reg};
IR::Value handle; IR::Value handle;
@ -160,7 +163,7 @@ void Impl(TranslatorVisitor& v, u64 insn, ComponentType component_type, OffsetTy
dref = v.F(meta_reg++); dref = v.F(meta_reg++);
} }
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
info.type.Assign(GetType(tld4.type)); info.type.Assign(GetTextureGatherType(tld4.type));
info.is_depth.Assign(tld4.dc != 0 ? 1 : 0); info.is_depth.Assign(tld4.dc != 0 ? 1 : 0);
info.gather_component.Assign(static_cast<u32>(component_type)); info.gather_component.Assign(static_cast<u32>(component_type));
const IR::Value sample{[&] { const IR::Value sample{[&] {
@ -187,7 +190,7 @@ void Impl(TranslatorVisitor& v, u64 insn, ComponentType component_type, OffsetTy
void TranslatorVisitor::TLD4(u64 insn) { void TranslatorVisitor::TLD4(u64 insn) {
union { union {
u64 raw; u64 raw;
BitField<56, 2, ComponentType> component; BitField<56, 2, TextureGatherComponentType> component;
BitField<54, 2, OffsetType> offset; BitField<54, 2, OffsetType> offset;
} const tld4{insn}; } const tld4{insn};
Impl(*this, insn, tld4.component, tld4.offset, false); Impl(*this, insn, tld4.component, tld4.offset, false);
@ -196,7 +199,7 @@ void TranslatorVisitor::TLD4(u64 insn) {
void TranslatorVisitor::TLD4_b(u64 insn) { void TranslatorVisitor::TLD4_b(u64 insn) {
union { union {
u64 raw; u64 raw;
BitField<38, 2, ComponentType> component; BitField<38, 2, TextureGatherComponentType> component;
BitField<36, 2, OffsetType> offset; BitField<36, 2, OffsetType> offset;
} const tld4{insn}; } const tld4{insn};
Impl(*this, insn, tld4.component, tld4.offset, true); Impl(*this, insn, tld4.component, tld4.offset, true);

View file

@ -1,3 +1,6 @@
// 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
@ -8,22 +11,22 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Precision : u64 { enum class TextureGatherSwizzledPrecision : u64 {
F32, F32,
F16, F16,
}; };
enum class ComponentType : u64 { enum class TextureGatherSwizzledComponentType : u64 {
R = 0, R = 0,
G = 1, G = 1,
B = 2, B = 2,
A = 3, A = 3,
}; };
union Encoding { union EncodinTGS {
u64 raw; u64 raw;
BitField<55, 1, Precision> precision; BitField<55, 1, TextureGatherSwizzledPrecision> precision;
BitField<52, 2, ComponentType> component_type; BitField<52, 2, TextureGatherSwizzledComponentType> component_type;
BitField<51, 1, u64> aoffi; BitField<51, 1, u64> aoffi;
BitField<50, 1, u64> dc; BitField<50, 1, u64> dc;
BitField<49, 1, u64> nodep; BitField<49, 1, u64> nodep;
@ -34,7 +37,7 @@ union Encoding {
BitField<36, 13, u64> cbuf_offset; BitField<36, 13, u64> cbuf_offset;
}; };
void CheckAlignment(IR::Reg reg, size_t alignment) { void CheckAlignmentTGS(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) { if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg); throw NotImplementedException("Unaligned source register {}", reg);
} }
@ -46,13 +49,13 @@ IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(6), true));
} }
IR::Value Sample(TranslatorVisitor& v, u64 insn) { IR::Value SampleTGS(TranslatorVisitor& v, u64 insn) {
const Encoding tld4s{insn}; const EncodinTGS tld4s{insn};
const IR::U32 handle{v.ir.Imm32(static_cast<u32>(tld4s.cbuf_offset * 4))}; const IR::U32 handle{v.ir.Imm32(static_cast<u32>(tld4s.cbuf_offset * 4))};
const IR::Reg reg_a{tld4s.src_reg_a}; const IR::Reg reg_a{tld4s.src_reg_a};
const IR::Reg reg_b{tld4s.src_reg_b}; const IR::Reg reg_b{tld4s.src_reg_b};
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
if (tld4s.precision == Precision::F16) { if (tld4s.precision == TextureGatherSwizzledPrecision::F16) {
info.relaxed_precision.Assign(1); info.relaxed_precision.Assign(1);
} }
info.gather_component.Assign(static_cast<u32>(tld4s.component_type.Value())); info.gather_component.Assign(static_cast<u32>(tld4s.component_type.Value()));
@ -60,18 +63,18 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0); info.is_depth.Assign(tld4s.dc != 0 ? 1 : 0);
IR::Value coords; IR::Value coords;
if (tld4s.aoffi != 0) { if (tld4s.aoffi != 0) {
CheckAlignment(reg_a, 2); CheckAlignmentTGS(reg_a, 2);
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1)); coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
IR::Value offset = MakeOffset(v, reg_b); IR::Value offset = MakeOffset(v, reg_b);
if (tld4s.dc != 0) { if (tld4s.dc != 0) {
CheckAlignment(reg_b, 2); CheckAlignmentTGS(reg_b, 2);
IR::F32 dref = v.F(reg_b + 1); IR::F32 dref = v.F(reg_b + 1);
return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info); return v.ir.ImageGatherDref(handle, coords, offset, {}, dref, info);
} }
return v.ir.ImageGather(handle, coords, offset, {}, info); return v.ir.ImageGather(handle, coords, offset, {}, info);
} }
if (tld4s.dc != 0) { if (tld4s.dc != 0) {
CheckAlignment(reg_a, 2); CheckAlignmentTGS(reg_a, 2);
coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1)); coords = v.ir.CompositeConstruct(v.F(reg_a), v.F(reg_a + 1));
IR::F32 dref = v.F(reg_b); IR::F32 dref = v.F(reg_b);
return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info); return v.ir.ImageGatherDref(handle, coords, {}, {}, dref, info);
@ -81,50 +84,50 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
} }
IR::Reg RegStoreComponent32(u64 insn, size_t index) { IR::Reg RegStoreComponent32(u64 insn, size_t index) {
const Encoding tlds4{insn}; const EncodinTGS tlds4{insn};
switch (index) { switch (index) {
case 0: case 0:
return tlds4.dest_reg_a; return tlds4.dest_reg_a;
case 1: case 1:
CheckAlignment(tlds4.dest_reg_a, 2); CheckAlignmentTGS(tlds4.dest_reg_a, 2);
return tlds4.dest_reg_a + 1; return tlds4.dest_reg_a + 1;
case 2: case 2:
return tlds4.dest_reg_b; return tlds4.dest_reg_b;
case 3: case 3:
CheckAlignment(tlds4.dest_reg_b, 2); CheckAlignmentTGS(tlds4.dest_reg_b, 2);
return tlds4.dest_reg_b + 1; return tlds4.dest_reg_b + 1;
} }
throw LogicError("Invalid store index {}", index); throw LogicError("Invalid store index {}", index);
} }
void Store32(TranslatorVisitor& v, u64 insn, const IR::Value& sample) { void Store32TGS(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
for (size_t component = 0; component < 4; ++component) { for (size_t component = 0; component < 4; ++component) {
const IR::Reg dest{RegStoreComponent32(insn, component)}; const IR::Reg dest{RegStoreComponent32(insn, component)};
v.F(dest, IR::F32{v.ir.CompositeExtract(sample, component)}); v.F(dest, IR::F32{v.ir.CompositeExtract(sample, component)});
} }
} }
IR::U32 Pack(TranslatorVisitor& v, const IR::F32& lhs, const IR::F32& rhs) { IR::U32 PackTGS(TranslatorVisitor& v, const IR::F32& lhs, const IR::F32& rhs) {
return v.ir.PackHalf2x16(v.ir.CompositeConstruct(lhs, rhs)); return v.ir.PackHalf2x16(v.ir.CompositeConstruct(lhs, rhs));
} }
void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) { void Store16TGS(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
std::array<IR::F32, 4> swizzled; std::array<IR::F32, 4> swizzled;
for (size_t component = 0; component < 4; ++component) { for (size_t component = 0; component < 4; ++component) {
swizzled[component] = IR::F32{v.ir.CompositeExtract(sample, component)}; swizzled[component] = IR::F32{v.ir.CompositeExtract(sample, component)};
} }
const Encoding tld4s{insn}; const EncodinTGS tld4s{insn};
v.X(tld4s.dest_reg_a, Pack(v, swizzled[0], swizzled[1])); v.X(tld4s.dest_reg_a, PackTGS(v, swizzled[0], swizzled[1]));
v.X(tld4s.dest_reg_b, Pack(v, swizzled[2], swizzled[3])); v.X(tld4s.dest_reg_b, PackTGS(v, swizzled[2], swizzled[3]));
} }
} // Anonymous namespace } // Anonymous namespace
void TranslatorVisitor::TLD4S(u64 insn) { void TranslatorVisitor::TLD4S(u64 insn) {
const IR::Value sample{Sample(*this, insn)}; const IR::Value sample{SampleTGS(*this, insn)};
if (Encoding{insn}.precision == Precision::F32) { if (EncodinTGS{insn}.precision == TextureGatherSwizzledPrecision::F32) {
Store32(*this, insn, sample); Store32TGS(*this, insn, sample);
} else { } else {
Store16(*this, insn, sample); Store16TGS(*this, insn, sample);
} }
} }

View file

@ -1,3 +1,6 @@
// 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
@ -9,7 +12,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class TextureType : u64 { enum class TextureGradientType : u64 {
_1D, _1D,
ARRAY_1D, ARRAY_1D,
_2D, _2D,
@ -20,23 +23,23 @@ enum class TextureType : u64 {
ARRAY_CUBE, ARRAY_CUBE,
}; };
Shader::TextureType GetType(TextureType type) { Shader::TextureType GetType(TextureGradientType type) {
switch (type) { switch (type) {
case TextureType::_1D: case TextureGradientType::_1D:
return Shader::TextureType::Color1D; return Shader::TextureType::Color1D;
case TextureType::ARRAY_1D: case TextureGradientType::ARRAY_1D:
return Shader::TextureType::ColorArray1D; return Shader::TextureType::ColorArray1D;
case TextureType::_2D: case TextureGradientType::_2D:
return Shader::TextureType::Color2D; return Shader::TextureType::Color2D;
case TextureType::ARRAY_2D: case TextureGradientType::ARRAY_2D:
return Shader::TextureType::ColorArray2D; return Shader::TextureType::ColorArray2D;
case TextureType::_3D: case TextureGradientType::_3D:
return Shader::TextureType::Color3D; return Shader::TextureType::Color3D;
case TextureType::ARRAY_3D: case TextureGradientType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureGradientType::CUBE:
return Shader::TextureType::ColorCube; return Shader::TextureType::ColorCube;
case TextureType::ARRAY_CUBE: case TextureGradientType::ARRAY_CUBE:
return Shader::TextureType::ColorArrayCube; return Shader::TextureType::ColorArrayCube;
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
@ -50,7 +53,7 @@ IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg, bool has_lod_clamp) {
v.ir.BitFieldExtract(value, v.ir.Imm32(base + 4), v.ir.Imm32(4), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(base + 4), v.ir.Imm32(4), true));
} }
void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { void TextureGatherImpl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
union { union {
u64 raw; u64 raw;
BitField<49, 1, u64> nodep; BitField<49, 1, u64> nodep;
@ -60,7 +63,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg; BitField<8, 8, IR::Reg> coord_reg;
BitField<20, 8, IR::Reg> derivative_reg; BitField<20, 8, IR::Reg> derivative_reg;
BitField<28, 3, TextureType> type; BitField<28, 3, TextureGradientType> type;
BitField<31, 4, u64> mask; BitField<31, 4, u64> mask;
BitField<36, 13, u64> cbuf_offset; BitField<36, 13, u64> cbuf_offset;
} const txd{insn}; } const txd{insn};
@ -88,25 +91,25 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
return v.ir.ConvertUToF(32, 16, array_index); return v.ir.ConvertUToF(32, 16, array_index);
}}; }};
switch (txd.type) { switch (txd.type) {
case TextureType::_1D: { case TextureGradientType::_1D: {
coords = v.F(base_reg); coords = v.F(base_reg);
num_derivatives = 1; num_derivatives = 1;
last_reg = base_reg + 1; last_reg = base_reg + 1;
break; break;
} }
case TextureType::ARRAY_1D: { case TextureGradientType::ARRAY_1D: {
last_reg = base_reg + 1; last_reg = base_reg + 1;
coords = v.ir.CompositeConstruct(v.F(base_reg), read_array()); coords = v.ir.CompositeConstruct(v.F(base_reg), read_array());
num_derivatives = 1; num_derivatives = 1;
break; break;
} }
case TextureType::_2D: { case TextureGradientType::_2D: {
last_reg = base_reg + 2; last_reg = base_reg + 2;
coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1)); coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1));
num_derivatives = 2; num_derivatives = 2;
break; break;
} }
case TextureType::ARRAY_2D: { case TextureGradientType::ARRAY_2D: {
last_reg = base_reg + 2; last_reg = base_reg + 2;
coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array()); coords = v.ir.CompositeConstruct(v.F(base_reg), v.F(base_reg + 1), read_array());
num_derivatives = 2; num_derivatives = 2;
@ -170,11 +173,11 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
} // Anonymous namespace } // Anonymous namespace
void TranslatorVisitor::TXD(u64 insn) { void TranslatorVisitor::TXD(u64 insn) {
Impl(*this, insn, false); TextureGatherImpl(*this, insn, false);
} }
void TranslatorVisitor::TXD_b(u64 insn) { void TranslatorVisitor::TXD_b(u64 insn) {
Impl(*this, insn, true); TextureGatherImpl(*this, insn, true);
} }
} // namespace Shader::Maxwell } // namespace Shader::Maxwell

View file

@ -1,3 +1,6 @@
// 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
@ -9,7 +12,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class TextureType : u64 { enum class TextureLoadType : u64 {
_1D, _1D,
ARRAY_1D, ARRAY_1D,
_2D, _2D,
@ -20,77 +23,77 @@ enum class TextureType : u64 {
ARRAY_CUBE, ARRAY_CUBE,
}; };
Shader::TextureType GetType(TextureType type) { Shader::TextureType GetType(TextureLoadType type) {
switch (type) { switch (type) {
case TextureType::_1D: case TextureLoadType::_1D:
return Shader::TextureType::Color1D; return Shader::TextureType::Color1D;
case TextureType::ARRAY_1D: case TextureLoadType::ARRAY_1D:
return Shader::TextureType::ColorArray1D; return Shader::TextureType::ColorArray1D;
case TextureType::_2D: case TextureLoadType::_2D:
return Shader::TextureType::Color2D; return Shader::TextureType::Color2D;
case TextureType::ARRAY_2D: case TextureLoadType::ARRAY_2D:
return Shader::TextureType::ColorArray2D; return Shader::TextureType::ColorArray2D;
case TextureType::_3D: case TextureLoadType::_3D:
return Shader::TextureType::Color3D; return Shader::TextureType::Color3D;
case TextureType::ARRAY_3D: case TextureLoadType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureLoadType::CUBE:
return Shader::TextureType::ColorCube; return Shader::TextureType::ColorCube;
case TextureType::ARRAY_CUBE: case TextureLoadType::ARRAY_CUBE:
return Shader::TextureType::ColorArrayCube; return Shader::TextureType::ColorArrayCube;
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
} }
IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureType type) { IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureLoadType type) {
const auto read_array{ const auto read_array{
[&]() -> IR::U32 { return v.ir.BitFieldExtract(v.X(reg), v.ir.Imm32(0), v.ir.Imm32(16)); }}; [&]() -> IR::U32 { return v.ir.BitFieldExtract(v.X(reg), v.ir.Imm32(0), v.ir.Imm32(16)); }};
switch (type) { switch (type) {
case TextureType::_1D: case TextureLoadType::_1D:
return v.X(reg); return v.X(reg);
case TextureType::ARRAY_1D: case TextureLoadType::ARRAY_1D:
return v.ir.CompositeConstruct(v.X(reg + 1), read_array()); return v.ir.CompositeConstruct(v.X(reg + 1), read_array());
case TextureType::_2D: case TextureLoadType::_2D:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1));
case TextureType::ARRAY_2D: case TextureLoadType::ARRAY_2D:
return v.ir.CompositeConstruct(v.X(reg + 1), v.X(reg + 2), read_array()); return v.ir.CompositeConstruct(v.X(reg + 1), v.X(reg + 2), read_array());
case TextureType::_3D: case TextureLoadType::_3D:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2));
case TextureType::ARRAY_3D: case TextureLoadType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureLoadType::CUBE:
return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2)); return v.ir.CompositeConstruct(v.X(reg), v.X(reg + 1), v.X(reg + 2));
case TextureType::ARRAY_CUBE: case TextureLoadType::ARRAY_CUBE:
return v.ir.CompositeConstruct(v.X(reg + 1), v.X(reg + 2), v.X(reg + 3), read_array()); return v.ir.CompositeConstruct(v.X(reg + 1), v.X(reg + 2), v.X(reg + 3), read_array());
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
} }
IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureType type) { IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg& reg, TextureLoadType type) {
const IR::U32 value{v.X(reg++)}; const IR::U32 value{v.X(reg++)};
switch (type) { switch (type) {
case TextureType::_1D: case TextureLoadType::_1D:
case TextureType::ARRAY_1D: case TextureLoadType::ARRAY_1D:
return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true); return v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true);
case TextureType::_2D: case TextureLoadType::_2D:
case TextureType::ARRAY_2D: case TextureLoadType::ARRAY_2D:
return v.ir.CompositeConstruct( return v.ir.CompositeConstruct(
v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true), v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true));
case TextureType::_3D: case TextureLoadType::_3D:
case TextureType::ARRAY_3D: case TextureLoadType::ARRAY_3D:
return v.ir.CompositeConstruct( return v.ir.CompositeConstruct(
v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true), v.ir.BitFieldExtract(value, v.ir.Imm32(0), v.ir.Imm32(4), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true), v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true),
v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(4), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(8), v.ir.Imm32(4), true));
case TextureType::CUBE: case TextureLoadType::CUBE:
case TextureType::ARRAY_CUBE: case TextureLoadType::ARRAY_CUBE:
throw NotImplementedException("Illegal offset on CUBE sample"); throw NotImplementedException("Illegal offset on CUBE sample");
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
} }
void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { void TextureLoadImpl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
union { union {
u64 raw; u64 raw;
BitField<49, 1, u64> nodep; BitField<49, 1, u64> nodep;
@ -102,7 +105,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg; BitField<8, 8, IR::Reg> coord_reg;
BitField<20, 8, IR::Reg> meta_reg; BitField<20, 8, IR::Reg> meta_reg;
BitField<28, 3, TextureType> type; BitField<28, 3, TextureLoadType> type;
BitField<31, 4, u64> mask; BitField<31, 4, u64> mask;
BitField<36, 13, u64> cbuf_offset; BitField<36, 13, u64> cbuf_offset;
} const tld{insn}; } const tld{insn};
@ -152,11 +155,11 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
} // Anonymous namespace } // Anonymous namespace
void TranslatorVisitor::TLD(u64 insn) { void TranslatorVisitor::TLD(u64 insn) {
Impl(*this, insn, false); TextureLoadImpl(*this, insn, false);
} }
void TranslatorVisitor::TLD_b(u64 insn) { void TranslatorVisitor::TLD_b(u64 insn) {
Impl(*this, insn, true); TextureLoadImpl(*this, insn, true);
} }
} // namespace Shader::Maxwell } // namespace Shader::Maxwell

View file

@ -1,3 +1,6 @@
// 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
@ -10,38 +13,14 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Precision : u64 { enum class TextureLoadSwizzledPrecision : u64 {
F16, F16,
F32, F32,
}; };
constexpr unsigned R = 1; union EncodinTLS {
constexpr unsigned G = 2;
constexpr unsigned B = 4;
constexpr unsigned A = 8;
constexpr std::array RG_LUT{
R, //
G, //
B, //
A, //
R | G, //
R | A, //
G | A, //
B | A, //
};
constexpr std::array RGBA_LUT{
R | G | B, //
R | G | A, //
R | B | A, //
G | B | A, //
R | G | B | A, //
};
union Encoding {
u64 raw; u64 raw;
BitField<59, 1, Precision> precision; BitField<59, 1, TextureLoadSwizzledPrecision> precision;
BitField<54, 1, u64> aoffi; BitField<54, 1, u64> aoffi;
BitField<53, 1, u64> lod; BitField<53, 1, u64> lod;
BitField<55, 1, u64> ms; BitField<55, 1, u64> ms;
@ -55,7 +34,7 @@ union Encoding {
BitField<53, 4, u64> encoding; BitField<53, 4, u64> encoding;
}; };
void CheckAlignment(IR::Reg reg, size_t alignment) { void CheckAlignmentTLS(IR::Reg reg, size_t alignment) {
if (!IR::IsAligned(reg, alignment)) { if (!IR::IsAligned(reg, alignment)) {
throw NotImplementedException("Unaligned source register {}", reg); throw NotImplementedException("Unaligned source register {}", reg);
} }
@ -67,8 +46,8 @@ IR::Value MakeOffset(TranslatorVisitor& v, IR::Reg reg) {
v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true)); v.ir.BitFieldExtract(value, v.ir.Imm32(4), v.ir.Imm32(4), true));
} }
IR::Value Sample(TranslatorVisitor& v, u64 insn) { IR::Value SampleTLS(TranslatorVisitor& v, u64 insn) {
const Encoding tlds{insn}; const EncodinTLS tlds{insn};
const IR::U32 handle{v.ir.Imm32(static_cast<u32>(tlds.cbuf_offset * 4))}; const IR::U32 handle{v.ir.Imm32(static_cast<u32>(tlds.cbuf_offset * 4))};
const IR::Reg reg_a{tlds.src_reg_a}; const IR::Reg reg_a{tlds.src_reg_a};
const IR::Reg reg_b{tlds.src_reg_b}; const IR::Reg reg_b{tlds.src_reg_b};
@ -92,38 +71,38 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_b)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_b));
break; break;
case 4: case 4:
CheckAlignment(reg_a, 2); CheckAlignmentTLS(reg_a, 2);
texture_type = Shader::TextureType::Color2D; texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
offsets = MakeOffset(v, reg_b); offsets = MakeOffset(v, reg_b);
break; break;
case 5: case 5:
CheckAlignment(reg_a, 2); CheckAlignmentTLS(reg_a, 2);
texture_type = Shader::TextureType::Color2D; texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
lod = v.X(reg_b); lod = v.X(reg_b);
break; break;
case 6: case 6:
CheckAlignment(reg_a, 2); CheckAlignmentTLS(reg_a, 2);
texture_type = Shader::TextureType::Color2D; texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
multisample = v.X(reg_b); multisample = v.X(reg_b);
break; break;
case 7: case 7:
CheckAlignment(reg_a, 2); CheckAlignmentTLS(reg_a, 2);
texture_type = Shader::TextureType::Color3D; texture_type = Shader::TextureType::Color3D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1), v.X(reg_b)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1), v.X(reg_b));
break; break;
case 8: { case 8: {
CheckAlignment(reg_b, 2); CheckAlignmentTLS(reg_b, 2);
const IR::U32 array{v.ir.BitFieldExtract(v.X(reg_a), v.ir.Imm32(0), v.ir.Imm32(16))}; const IR::U32 array{v.ir.BitFieldExtract(v.X(reg_a), v.ir.Imm32(0), v.ir.Imm32(16))};
texture_type = Shader::TextureType::ColorArray2D; texture_type = Shader::TextureType::ColorArray2D;
coords = v.ir.CompositeConstruct(v.X(reg_b), v.X(reg_b + 1), array); coords = v.ir.CompositeConstruct(v.X(reg_b), v.X(reg_b + 1), array);
break; break;
} }
case 12: case 12:
CheckAlignment(reg_a, 2); CheckAlignmentTLS(reg_a, 2);
CheckAlignment(reg_b, 2); CheckAlignmentTLS(reg_b, 2);
texture_type = Shader::TextureType::Color2D; texture_type = Shader::TextureType::Color2D;
coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1)); coords = v.ir.CompositeConstruct(v.X(reg_a), v.X(reg_a + 1));
lod = v.X(reg_b); lod = v.X(reg_b);
@ -133,7 +112,7 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
throw NotImplementedException("Illegal encoding {}", tlds.encoding.Value()); throw NotImplementedException("Illegal encoding {}", tlds.encoding.Value());
} }
IR::TextureInstInfo info{}; IR::TextureInstInfo info{};
if (tlds.precision == Precision::F16) { if (tlds.precision == TextureLoadSwizzledPrecision::F16) {
info.relaxed_precision.Assign(1); info.relaxed_precision.Assign(1);
} }
info.type.Assign(texture_type); info.type.Assign(texture_type);
@ -141,7 +120,32 @@ IR::Value Sample(TranslatorVisitor& v, u64 insn) {
} }
unsigned Swizzle(u64 insn) { unsigned Swizzle(u64 insn) {
const Encoding tlds{insn}; #define R 1
#define G 2
#define B 4
#define A 8
static constexpr std::array<unsigned, 8> RG_LUT{
R, //
G, //
B, //
A, //
R | G, //
R | A, //
G | A, //
B | A, //
};
static constexpr std::array<unsigned, 5> RGBA_LUT{
R | G | B, //
R | G | A, //
R | B | A, //
G | B | A, //
R | G | B | A, //
};
#undef R
#undef G
#undef B
#undef A
const EncodinTLS tlds{insn};
const size_t encoding{tlds.swizzle}; const size_t encoding{tlds.swizzle};
if (tlds.dest_reg_b == IR::Reg::RZ) { if (tlds.dest_reg_b == IR::Reg::RZ) {
if (encoding >= RG_LUT.size()) { if (encoding >= RG_LUT.size()) {
@ -161,23 +165,23 @@ IR::F32 Extract(TranslatorVisitor& v, const IR::Value& sample, unsigned componen
} }
IR::Reg RegStoreComponent32(u64 insn, unsigned index) { IR::Reg RegStoreComponent32(u64 insn, unsigned index) {
const Encoding tlds{insn}; const EncodinTLS tlds{insn};
switch (index) { switch (index) {
case 0: case 0:
return tlds.dest_reg_a; return tlds.dest_reg_a;
case 1: case 1:
CheckAlignment(tlds.dest_reg_a, 2); CheckAlignmentTLS(tlds.dest_reg_a, 2);
return tlds.dest_reg_a + 1; return tlds.dest_reg_a + 1;
case 2: case 2:
return tlds.dest_reg_b; return tlds.dest_reg_b;
case 3: case 3:
CheckAlignment(tlds.dest_reg_b, 2); CheckAlignmentTLS(tlds.dest_reg_b, 2);
return tlds.dest_reg_b + 1; return tlds.dest_reg_b + 1;
} }
throw LogicError("Invalid store index {}", index); throw LogicError("Invalid store index {}", index);
} }
void Store32(TranslatorVisitor& v, u64 insn, const IR::Value& sample) { void Store32TLS(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
const unsigned swizzle{Swizzle(insn)}; const unsigned swizzle{Swizzle(insn)};
unsigned store_index{0}; unsigned store_index{0};
for (unsigned component = 0; component < 4; ++component) { for (unsigned component = 0; component < 4; ++component) {
@ -190,11 +194,11 @@ void Store32(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
} }
} }
IR::U32 Pack(TranslatorVisitor& v, const IR::F32& lhs, const IR::F32& rhs) { IR::U32 PackTLS(TranslatorVisitor& v, const IR::F32& lhs, const IR::F32& rhs) {
return v.ir.PackHalf2x16(v.ir.CompositeConstruct(lhs, rhs)); return v.ir.PackHalf2x16(v.ir.CompositeConstruct(lhs, rhs));
} }
void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) { void Store16TLS(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
const unsigned swizzle{Swizzle(insn)}; const unsigned swizzle{Swizzle(insn)};
unsigned store_index{0}; unsigned store_index{0};
std::array<IR::F32, 4> swizzled; std::array<IR::F32, 4> swizzled;
@ -206,23 +210,23 @@ void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
++store_index; ++store_index;
} }
const IR::F32 zero{v.ir.Imm32(0.0f)}; const IR::F32 zero{v.ir.Imm32(0.0f)};
const Encoding tlds{insn}; const EncodinTLS tlds{insn};
switch (store_index) { switch (store_index) {
case 1: case 1:
v.X(tlds.dest_reg_a, Pack(v, swizzled[0], zero)); v.X(tlds.dest_reg_a, PackTLS(v, swizzled[0], zero));
break; break;
case 2: case 2:
case 3: case 3:
case 4: case 4:
v.X(tlds.dest_reg_a, Pack(v, swizzled[0], swizzled[1])); v.X(tlds.dest_reg_a, PackTLS(v, swizzled[0], swizzled[1]));
switch (store_index) { switch (store_index) {
case 2: case 2:
break; break;
case 3: case 3:
v.X(tlds.dest_reg_b, Pack(v, swizzled[2], zero)); v.X(tlds.dest_reg_b, PackTLS(v, swizzled[2], zero));
break; break;
case 4: case 4:
v.X(tlds.dest_reg_b, Pack(v, swizzled[2], swizzled[3])); v.X(tlds.dest_reg_b, PackTLS(v, swizzled[2], swizzled[3]));
break; break;
} }
break; break;
@ -231,11 +235,11 @@ void Store16(TranslatorVisitor& v, u64 insn, const IR::Value& sample) {
} // Anonymous namespace } // Anonymous namespace
void TranslatorVisitor::TLDS(u64 insn) { void TranslatorVisitor::TLDS(u64 insn) {
const IR::Value sample{Sample(*this, insn)}; const IR::Value sample{SampleTLS(*this, insn)};
if (Encoding{insn}.precision == Precision::F32) { if (EncodinTLS{insn}.precision == TextureLoadSwizzledPrecision::F32) {
Store32(*this, insn, sample); Store32TLS(*this, insn, sample);
} else { } else {
Store16(*this, insn, sample); Store16TLS(*this, insn, sample);
} }
} }
} // namespace Shader::Maxwell } // namespace Shader::Maxwell

View file

@ -1,3 +1,6 @@
// 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
@ -9,7 +12,7 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class TextureType : u64 { enum class TextureMipmapLevelType : u64 {
_1D, _1D,
ARRAY_1D, ARRAY_1D,
_2D, _2D,
@ -20,53 +23,53 @@ enum class TextureType : u64 {
ARRAY_CUBE, ARRAY_CUBE,
}; };
Shader::TextureType GetType(TextureType type) { Shader::TextureType GetType(TextureMipmapLevelType type) {
switch (type) { switch (type) {
case TextureType::_1D: case TextureMipmapLevelType::_1D:
return Shader::TextureType::Color1D; return Shader::TextureType::Color1D;
case TextureType::ARRAY_1D: case TextureMipmapLevelType::ARRAY_1D:
return Shader::TextureType::ColorArray1D; return Shader::TextureType::ColorArray1D;
case TextureType::_2D: case TextureMipmapLevelType::_2D:
return Shader::TextureType::Color2D; return Shader::TextureType::Color2D;
case TextureType::ARRAY_2D: case TextureMipmapLevelType::ARRAY_2D:
return Shader::TextureType::ColorArray2D; return Shader::TextureType::ColorArray2D;
case TextureType::_3D: case TextureMipmapLevelType::_3D:
return Shader::TextureType::Color3D; return Shader::TextureType::Color3D;
case TextureType::ARRAY_3D: case TextureMipmapLevelType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureMipmapLevelType::CUBE:
return Shader::TextureType::ColorCube; return Shader::TextureType::ColorCube;
case TextureType::ARRAY_CUBE: case TextureMipmapLevelType::ARRAY_CUBE:
return Shader::TextureType::ColorArrayCube; return Shader::TextureType::ColorArrayCube;
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
} }
IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureType type) { IR::Value MakeCoords(TranslatorVisitor& v, IR::Reg reg, TextureMipmapLevelType type) {
// The ISA reads an array component here, but this is not needed on high level shading languages // The ISA reads an array component here, but this is not needed on high level shading languages
// We are dropping this information. // We are dropping this information.
switch (type) { switch (type) {
case TextureType::_1D: case TextureMipmapLevelType::_1D:
return v.F(reg); return v.F(reg);
case TextureType::ARRAY_1D: case TextureMipmapLevelType::ARRAY_1D:
return v.F(reg + 1); return v.F(reg + 1);
case TextureType::_2D: case TextureMipmapLevelType::_2D:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1));
case TextureType::ARRAY_2D: case TextureMipmapLevelType::ARRAY_2D:
return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2)); return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2));
case TextureType::_3D: case TextureMipmapLevelType::_3D:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
case TextureType::ARRAY_3D: case TextureMipmapLevelType::ARRAY_3D:
throw NotImplementedException("3D array texture type"); throw NotImplementedException("3D array texture type");
case TextureType::CUBE: case TextureMipmapLevelType::CUBE:
return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2)); return v.ir.CompositeConstruct(v.F(reg), v.F(reg + 1), v.F(reg + 2));
case TextureType::ARRAY_CUBE: case TextureMipmapLevelType::ARRAY_CUBE:
return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), v.F(reg + 3)); return v.ir.CompositeConstruct(v.F(reg + 1), v.F(reg + 2), v.F(reg + 3));
} }
throw NotImplementedException("Invalid texture type {}", type); throw NotImplementedException("Invalid texture type {}", type);
} }
void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) { void TextureMipmapLevelImpl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
union { union {
u64 raw; u64 raw;
BitField<49, 1, u64> nodep; BitField<49, 1, u64> nodep;
@ -74,7 +77,7 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> coord_reg; BitField<8, 8, IR::Reg> coord_reg;
BitField<20, 8, IR::Reg> meta_reg; BitField<20, 8, IR::Reg> meta_reg;
BitField<28, 3, TextureType> type; BitField<28, 3, TextureMipmapLevelType> type;
BitField<31, 4, u64> mask; BitField<31, 4, u64> mask;
BitField<36, 13, u64> cbuf_offset; BitField<36, 13, u64> cbuf_offset;
} const tmml{insn}; } const tmml{insn};
@ -113,11 +116,11 @@ void Impl(TranslatorVisitor& v, u64 insn, bool is_bindless) {
} // Anonymous namespace } // Anonymous namespace
void TranslatorVisitor::TMML(u64 insn) { void TranslatorVisitor::TMML(u64 insn) {
Impl(*this, insn, false); TextureMipmapLevelImpl(*this, insn, false);
} }
void TranslatorVisitor::TMML_b(u64 insn) { void TranslatorVisitor::TMML_b(u64 insn) {
Impl(*this, insn, true); TextureMipmapLevelImpl(*this, insn, true);
} }
} // namespace Shader::Maxwell } // namespace Shader::Maxwell

View file

@ -1,3 +1,6 @@
// 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
@ -9,24 +12,24 @@
namespace Shader::Maxwell { namespace Shader::Maxwell {
namespace { namespace {
enum class Mode : u64 { enum class TextureQueryMode : u64 {
Dimension = 1, Dimension = 1,
TextureType = 2, TextureType = 2,
SamplePos = 5, SamplePos = 5,
}; };
IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, Mode mode, IR::Reg src_reg, u64 mask) { IR::Value Query(TranslatorVisitor& v, const IR::U32& handle, TextureQueryMode mode, IR::Reg src_reg, u64 mask) {
switch (mode) { switch (mode) {
case Mode::Dimension: { case TextureQueryMode::Dimension: {
const bool needs_num_mips{((mask >> 3) & 1) != 0}; const bool needs_num_mips{((mask >> 3) & 1) != 0};
const IR::U1 skip_mips{v.ir.Imm1(!needs_num_mips)}; const IR::U1 skip_mips{v.ir.Imm1(!needs_num_mips)};
const IR::U32 lod{v.X(src_reg)}; const IR::U32 lod{v.X(src_reg)};
return v.ir.ImageQueryDimension(handle, lod, skip_mips); return v.ir.ImageQueryDimension(handle, lod, skip_mips);
} }
case Mode::TextureType: case TextureQueryMode::TextureType:
case Mode::SamplePos: case TextureQueryMode::SamplePos:
default: default:
throw NotImplementedException("Mode {}", mode); throw NotImplementedException("TextureQueryMode {}", mode);
} }
} }
@ -36,7 +39,7 @@ void Impl(TranslatorVisitor& v, u64 insn, std::optional<u32> cbuf_offset) {
BitField<49, 1, u64> nodep; BitField<49, 1, u64> nodep;
BitField<0, 8, IR::Reg> dest_reg; BitField<0, 8, IR::Reg> dest_reg;
BitField<8, 8, IR::Reg> src_reg; BitField<8, 8, IR::Reg> src_reg;
BitField<22, 3, Mode> mode; BitField<22, 3, TextureQueryMode> mode;
BitField<31, 4, u64> mask; BitField<31, 4, u64> mask;
} const txq{insn}; } const txq{insn};

View file

@ -1,3 +1,6 @@
// 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
@ -6,7 +9,7 @@
namespace Shader::Optimization { namespace Shader::Optimization {
namespace { namespace {
IR::Opcode Replace(IR::Opcode op) { IR::Opcode ReplaceFP16ToFP32(IR::Opcode op) {
switch (op) { switch (op) {
case IR::Opcode::FPAbs16: case IR::Opcode::FPAbs16:
return IR::Opcode::FPAbs32; return IR::Opcode::FPAbs32;
@ -131,7 +134,7 @@ IR::Opcode Replace(IR::Opcode op) {
void LowerFp16ToFp32(IR::Program& program) { void LowerFp16ToFp32(IR::Program& program) {
for (IR::Block* const block : program.blocks) { for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) { for (IR::Inst& inst : block->Instructions()) {
inst.ReplaceOpcode(Replace(inst.GetOpcode())); inst.ReplaceOpcode(ReplaceFP16ToFP32(inst.GetOpcode()));
} }
} }
} }

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// 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
@ -49,7 +52,7 @@ IR::Value F32ToPackedF64(IR::IREmitter& ir, const IR::Value& raw) {
return ir.CompositeConstruct(lo, hi); return ir.CompositeConstruct(lo, hi);
} }
IR::Opcode Replace(IR::Opcode op) { IR::Opcode ReplaceFP64ToFP32(IR::Opcode op) {
switch (op) { switch (op) {
case IR::Opcode::FPAbs64: case IR::Opcode::FPAbs64:
return IR::Opcode::FPAbs32; return IR::Opcode::FPAbs32;
@ -154,7 +157,7 @@ IR::Opcode Replace(IR::Opcode op) {
} }
} }
void Lower(IR::Block& block, IR::Inst& inst) { void LowerFP64ToFP32(IR::Block& block, IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::PackDouble2x32: { case IR::Opcode::PackDouble2x32: {
IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst)); IR::IREmitter ir(block, IR::Block::InstructionList::s_iterator_to(inst));
@ -167,7 +170,7 @@ void Lower(IR::Block& block, IR::Inst& inst) {
break; break;
} }
default: default:
inst.ReplaceOpcode(Replace(inst.GetOpcode())); inst.ReplaceOpcode(ReplaceFP64ToFP32(inst.GetOpcode()));
break; break;
} }
} }
@ -177,7 +180,7 @@ void Lower(IR::Block& block, IR::Inst& inst) {
void LowerFp64ToFp32(IR::Program& program) { void LowerFp64ToFp32(IR::Program& program) {
for (IR::Block* const block : program.blocks) { for (IR::Block* const block : program.blocks) {
for (IR::Inst& inst : block->Instructions()) { for (IR::Inst& inst : block->Instructions()) {
Lower(*block, inst); LowerFP64ToFP32(*block, inst);
} }
} }
} }

View file

@ -1,3 +1,6 @@
// 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
@ -181,7 +184,7 @@ void ShiftRightArithmetic64To32(IR::Block& block, IR::Inst& inst) {
inst.ReplaceUsesWith(ir.CompositeConstruct(ret_lo, ret_hi)); inst.ReplaceUsesWith(ir.CompositeConstruct(ret_lo, ret_hi));
} }
void Lower(IR::Block& block, IR::Inst& inst) { void LowerI64ToI32(IR::Block& block, IR::Inst& inst) {
switch (inst.GetOpcode()) { switch (inst.GetOpcode()) {
case IR::Opcode::PackUint2x32: case IR::Opcode::PackUint2x32:
case IR::Opcode::UnpackUint2x32: case IR::Opcode::UnpackUint2x32:
@ -229,7 +232,7 @@ void LowerInt64ToInt32(IR::Program& program) {
for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) { for (auto it = program.post_order_blocks.rbegin(); it != end; ++it) {
IR::Block* const block{*it}; IR::Block* const block{*it};
for (IR::Inst& inst : block->Instructions()) { for (IR::Inst& inst : block->Instructions()) {
Lower(*block, inst); LowerI64ToI32(*block, inst);
} }
} }
} }

View file

@ -16,7 +16,6 @@ endif()
add_library(video_core STATIC add_library(video_core STATIC
buffer_cache/buffer_base.h buffer_cache/buffer_base.h
buffer_cache/buffer_cache_base.h buffer_cache/buffer_cache_base.h
buffer_cache/buffer_cache.cpp
buffer_cache/buffer_cache.h buffer_cache/buffer_cache.h
buffer_cache/memory_tracker_base.h buffer_cache/memory_tracker_base.h
buffer_cache/usage_tracker.h buffer_cache/usage_tracker.h

View file

@ -1,14 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/control/channel_state_cache.inc"
namespace VideoCommon {
template class VideoCommon::ChannelSetupCaches<VideoCommon::BufferCacheChannelInfo>;
} // namespace VideoCommon

View file

@ -1,3 +1,6 @@
// 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
@ -23,10 +26,9 @@ constexpr std::array VIEW_CLASS_128_BITS{
constexpr std::array VIEW_CLASS_96_BITS{ constexpr std::array VIEW_CLASS_96_BITS{
PixelFormat::R32G32B32_FLOAT, PixelFormat::R32G32B32_FLOAT,
PixelFormat::R32G32B32A32_SINT,
PixelFormat::R32G32B32A32_UINT,
}; };
// Missing formats:
// PixelFormat::RGB32UI,
// PixelFormat::RGB32I,
constexpr std::array VIEW_CLASS_64_BITS{ constexpr std::array VIEW_CLASS_64_BITS{
PixelFormat::R32G32_FLOAT, PixelFormat::R32G32_UINT, PixelFormat::R32G32_FLOAT, PixelFormat::R32G32_UINT,

View file

@ -1,7 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project // SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "video_core/control/channel_state_cache.inc" #include "video_core/buffer_cache/buffer_cache_base.h"
#include "video_core/control/channel_state.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/texture_cache/texture_cache_base.h"
namespace VideoCommon { namespace VideoCommon {
@ -9,6 +15,82 @@ ChannelInfo::ChannelInfo(Tegra::Control::ChannelState& channel_state)
: maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute}, : maxwell3d{*channel_state.maxwell_3d}, kepler_compute{*channel_state.kepler_compute},
gpu_memory{*channel_state.memory_manager}, program_id{channel_state.program_id} {} gpu_memory{*channel_state.memory_manager}, program_id{channel_state.program_id} {}
template <class P>
ChannelSetupCaches<P>::~ChannelSetupCaches() = default;
template <class P>
void ChannelSetupCaches<P>::CreateChannel(struct Tegra::Control::ChannelState& channel) {
std::unique_lock<std::mutex> lk(config_mutex);
ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0);
auto new_id = [this, &channel]() {
if (!free_channel_ids.empty()) {
auto id = free_channel_ids.front();
free_channel_ids.pop_front();
new (&channel_storage[id]) P(channel);
return id;
}
channel_storage.emplace_back(channel);
return channel_storage.size() - 1;
}();
channel_map.emplace(channel.bind_id, new_id);
if (current_channel_id != UNSET_CHANNEL) {
channel_state = &channel_storage[current_channel_id];
}
active_channel_ids.push_back(new_id);
auto as_it = address_spaces.find(channel.memory_manager->GetID());
if (as_it != address_spaces.end()) {
as_it->second.ref_count++;
return;
}
AddressSpaceRef new_gpu_mem_ref{
.ref_count = 1,
.storage_id = address_spaces.size(),
.gpu_memory = channel.memory_manager.get(),
};
address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref);
OnGPUASRegister(channel.memory_manager->GetID());
}
/// Bind a channel for execution.
template <class P>
void ChannelSetupCaches<P>::BindToChannel(s32 id) {
std::unique_lock<std::mutex> lk(config_mutex);
auto it = channel_map.find(id);
ASSERT(it != channel_map.end() && id >= 0);
current_channel_id = it->second;
channel_state = &channel_storage[current_channel_id];
maxwell3d = &channel_state->maxwell3d;
kepler_compute = &channel_state->kepler_compute;
gpu_memory = &channel_state->gpu_memory;
program_id = channel_state->program_id;
current_address_space = gpu_memory->GetID();
}
/// Erase channel's channel_state.
template <class P>
void ChannelSetupCaches<P>::EraseChannel(s32 id) {
std::unique_lock<std::mutex> lk(config_mutex);
const auto it = channel_map.find(id);
ASSERT(it != channel_map.end() && id >= 0);
const auto this_id = it->second;
free_channel_ids.push_back(this_id);
channel_map.erase(it);
if (this_id == current_channel_id) {
current_channel_id = UNSET_CHANNEL;
channel_state = nullptr;
maxwell3d = nullptr;
kepler_compute = nullptr;
gpu_memory = nullptr;
program_id = 0;
} else if (current_channel_id != UNSET_CHANNEL) {
channel_state = &channel_storage[current_channel_id];
}
active_channel_ids.erase(
std::find(active_channel_ids.begin(), active_channel_ids.end(), this_id));
}
template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>; template class VideoCommon::ChannelSetupCaches<VideoCommon::ChannelInfo>;
template class VideoCommon::ChannelSetupCaches<VideoCommon::BufferCacheChannelInfo>;
template class VideoCommon::ChannelSetupCaches<VideoCommon::TextureCacheChannelInfo>;
} // namespace VideoCommon } // namespace VideoCommon

View file

@ -1,88 +0,0 @@
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include <algorithm>
#include "video_core/control/channel_state.h"
#include "video_core/control/channel_state_cache.h"
#include "video_core/engines/kepler_compute.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/memory_manager.h"
namespace VideoCommon {
template <class P>
ChannelSetupCaches<P>::~ChannelSetupCaches() = default;
template <class P>
void ChannelSetupCaches<P>::CreateChannel(struct Tegra::Control::ChannelState& channel) {
std::unique_lock<std::mutex> lk(config_mutex);
ASSERT(channel_map.find(channel.bind_id) == channel_map.end() && channel.bind_id >= 0);
auto new_id = [this, &channel]() {
if (!free_channel_ids.empty()) {
auto id = free_channel_ids.front();
free_channel_ids.pop_front();
new (&channel_storage[id]) P(channel);
return id;
}
channel_storage.emplace_back(channel);
return channel_storage.size() - 1;
}();
channel_map.emplace(channel.bind_id, new_id);
if (current_channel_id != UNSET_CHANNEL) {
channel_state = &channel_storage[current_channel_id];
}
active_channel_ids.push_back(new_id);
auto as_it = address_spaces.find(channel.memory_manager->GetID());
if (as_it != address_spaces.end()) {
as_it->second.ref_count++;
return;
}
AddressSpaceRef new_gpu_mem_ref{
.ref_count = 1,
.storage_id = address_spaces.size(),
.gpu_memory = channel.memory_manager.get(),
};
address_spaces.emplace(channel.memory_manager->GetID(), new_gpu_mem_ref);
OnGPUASRegister(channel.memory_manager->GetID());
}
/// Bind a channel for execution.
template <class P>
void ChannelSetupCaches<P>::BindToChannel(s32 id) {
std::unique_lock<std::mutex> lk(config_mutex);
auto it = channel_map.find(id);
ASSERT(it != channel_map.end() && id >= 0);
current_channel_id = it->second;
channel_state = &channel_storage[current_channel_id];
maxwell3d = &channel_state->maxwell3d;
kepler_compute = &channel_state->kepler_compute;
gpu_memory = &channel_state->gpu_memory;
program_id = channel_state->program_id;
current_address_space = gpu_memory->GetID();
}
/// Erase channel's channel_state.
template <class P>
void ChannelSetupCaches<P>::EraseChannel(s32 id) {
std::unique_lock<std::mutex> lk(config_mutex);
const auto it = channel_map.find(id);
ASSERT(it != channel_map.end() && id >= 0);
const auto this_id = it->second;
free_channel_ids.push_back(this_id);
channel_map.erase(it);
if (this_id == current_channel_id) {
current_channel_id = UNSET_CHANNEL;
channel_state = nullptr;
maxwell3d = nullptr;
kepler_compute = nullptr;
gpu_memory = nullptr;
program_id = 0;
} else if (current_channel_id != UNSET_CHANNEL) {
channel_state = &channel_storage[current_channel_id];
}
active_channel_ids.erase(
std::find(active_channel_ids.begin(), active_channel_ids.end(), this_id));
}
} // namespace VideoCommon

View file

@ -414,7 +414,7 @@ VkExtent2D GetConversionExtent(const ImageView& src_image_view) {
}; };
} }
void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, void BlitImageTransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout,
VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) {
constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT};
@ -578,7 +578,7 @@ void BlitImageHelper::BlitColor(const Framebuffer* dst_framebuffer, VkImageView
scheduler.RequestOutsideRenderPassOperationContext(); scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region, scheduler.Record([this, dst_framebuffer, src_image_view, src_image, src_sampler, dst_region,
src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) { src_region, src_size, pipeline, layout](vk::CommandBuffer cmdbuf) {
TransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); BlitImageTransitionImageLayout(cmdbuf, src_image, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
BeginRenderPass(cmdbuf, dst_framebuffer); BeginRenderPass(cmdbuf, dst_framebuffer);
const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit(); const VkDescriptorSet descriptor_set = one_texture_descriptor_allocator.Commit();
UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view); UpdateOneTextureDescriptorSet(device, descriptor_set, src_sampler, src_image_view);

View file

@ -7,9 +7,6 @@
#include <variant> #include <variant>
#include "video_core/present.h" #include "video_core/present.h"
#include "video_core/renderer_vulkan/present/anti_alias_pass.h" #include "video_core/renderer_vulkan/present/anti_alias_pass.h"
/* X11 defines */
#undef Success
#undef BadValue
#include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "common/settings.h" #include "common/settings.h"

View file

@ -1,3 +1,6 @@
// 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
@ -5,256 +8,12 @@
#include <string> #include <string>
#include <fmt/base.h>
#include <fmt/ranges.h> #include <fmt/ranges.h>
#include "video_core/surface.h" #include "video_core/surface.h"
#include "video_core/texture_cache/types.h" #include "video_core/texture_cache/types.h"
template <>
struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::string_view> {
template <typename FormatContext>
auto format(VideoCore::Surface::PixelFormat format, FormatContext& ctx) const {
using VideoCore::Surface::PixelFormat;
const string_view name = [format] {
switch (format) {
case PixelFormat::A8B8G8R8_UNORM:
return "A8B8G8R8_UNORM";
case PixelFormat::A8B8G8R8_SNORM:
return "A8B8G8R8_SNORM";
case PixelFormat::A8B8G8R8_SINT:
return "A8B8G8R8_SINT";
case PixelFormat::A8B8G8R8_UINT:
return "A8B8G8R8_UINT";
case PixelFormat::R5G6B5_UNORM:
return "R5G6B5_UNORM";
case PixelFormat::B5G6R5_UNORM:
return "B5G6R5_UNORM";
case PixelFormat::A1R5G5B5_UNORM:
return "A1R5G5B5_UNORM";
case PixelFormat::A2B10G10R10_UNORM:
return "A2B10G10R10_UNORM";
case PixelFormat::A2B10G10R10_UINT:
return "A2B10G10R10_UINT";
case PixelFormat::A2R10G10B10_UNORM:
return "A2R10G10B10_UNORM";
case PixelFormat::A1B5G5R5_UNORM:
return "A1B5G5R5_UNORM";
case PixelFormat::A5B5G5R1_UNORM:
return "A5B5G5R1_UNORM";
case PixelFormat::R8_UNORM:
return "R8_UNORM";
case PixelFormat::R8_SNORM:
return "R8_SNORM";
case PixelFormat::R8_SINT:
return "R8_SINT";
case PixelFormat::R8_UINT:
return "R8_UINT";
case PixelFormat::R16G16B16A16_FLOAT:
return "R16G16B16A16_FLOAT";
case PixelFormat::R16G16B16A16_UNORM:
return "R16G16B16A16_UNORM";
case PixelFormat::R16G16B16A16_SNORM:
return "R16G16B16A16_SNORM";
case PixelFormat::R16G16B16A16_SINT:
return "R16G16B16A16_SINT";
case PixelFormat::R16G16B16A16_UINT:
return "R16G16B16A16_UINT";
case PixelFormat::B10G11R11_FLOAT:
return "B10G11R11_FLOAT";
case PixelFormat::R32G32B32A32_UINT:
return "R32G32B32A32_UINT";
case PixelFormat::BC1_RGBA_UNORM:
return "BC1_RGBA_UNORM";
case PixelFormat::BC2_UNORM:
return "BC2_UNORM";
case PixelFormat::BC3_UNORM:
return "BC3_UNORM";
case PixelFormat::BC4_UNORM:
return "BC4_UNORM";
case PixelFormat::BC4_SNORM:
return "BC4_SNORM";
case PixelFormat::BC5_UNORM:
return "BC5_UNORM";
case PixelFormat::BC5_SNORM:
return "BC5_SNORM";
case PixelFormat::BC7_UNORM:
return "BC7_UNORM";
case PixelFormat::BC6H_UFLOAT:
return "BC6H_UFLOAT";
case PixelFormat::BC6H_SFLOAT:
return "BC6H_SFLOAT";
case PixelFormat::ASTC_2D_4X4_UNORM:
return "ASTC_2D_4X4_UNORM";
case PixelFormat::B8G8R8A8_UNORM:
return "B8G8R8A8_UNORM";
case PixelFormat::R32G32B32A32_FLOAT:
return "R32G32B32A32_FLOAT";
case PixelFormat::R32G32B32A32_SINT:
return "R32G32B32A32_SINT";
case PixelFormat::R32G32_FLOAT:
return "R32G32_FLOAT";
case PixelFormat::R32G32_SINT:
return "R32G32_SINT";
case PixelFormat::R32_FLOAT:
return "R32_FLOAT";
case PixelFormat::R16_FLOAT:
return "R16_FLOAT";
case PixelFormat::R16_UNORM:
return "R16_UNORM";
case PixelFormat::R16_SNORM:
return "R16_SNORM";
case PixelFormat::R16_UINT:
return "R16_UINT";
case PixelFormat::R16_SINT:
return "R16_SINT";
case PixelFormat::R16G16_UNORM:
return "R16G16_UNORM";
case PixelFormat::R16G16_FLOAT:
return "R16G16_FLOAT";
case PixelFormat::R16G16_UINT:
return "R16G16_UINT";
case PixelFormat::R16G16_SINT:
return "R16G16_SINT";
case PixelFormat::R16G16_SNORM:
return "R16G16_SNORM";
case PixelFormat::R32G32B32_FLOAT:
return "R32G32B32_FLOAT";
case PixelFormat::A8B8G8R8_SRGB:
return "A8B8G8R8_SRGB";
case PixelFormat::R8G8_UNORM:
return "R8G8_UNORM";
case PixelFormat::R8G8_SNORM:
return "R8G8_SNORM";
case PixelFormat::R8G8_SINT:
return "R8G8_SINT";
case PixelFormat::R8G8_UINT:
return "R8G8_UINT";
case PixelFormat::R32G32_UINT:
return "R32G32_UINT";
case PixelFormat::R16G16B16X16_FLOAT:
return "R16G16B16X16_FLOAT";
case PixelFormat::R32_UINT:
return "R32_UINT";
case PixelFormat::R32_SINT:
return "R32_SINT";
case PixelFormat::ASTC_2D_8X8_UNORM:
return "ASTC_2D_8X8_UNORM";
case PixelFormat::ASTC_2D_8X5_UNORM:
return "ASTC_2D_8X5_UNORM";
case PixelFormat::ASTC_2D_5X4_UNORM:
return "ASTC_2D_5X4_UNORM";
case PixelFormat::B8G8R8A8_SRGB:
return "B8G8R8A8_SRGB";
case PixelFormat::BC1_RGBA_SRGB:
return "BC1_RGBA_SRGB";
case PixelFormat::BC2_SRGB:
return "BC2_SRGB";
case PixelFormat::BC3_SRGB:
return "BC3_SRGB";
case PixelFormat::BC7_SRGB:
return "BC7_SRGB";
case PixelFormat::A4B4G4R4_UNORM:
return "A4B4G4R4_UNORM";
case PixelFormat::G4R4_UNORM:
return "G4R4_UNORM";
case PixelFormat::ASTC_2D_4X4_SRGB:
return "ASTC_2D_4X4_SRGB";
case PixelFormat::ASTC_2D_8X8_SRGB:
return "ASTC_2D_8X8_SRGB";
case PixelFormat::ASTC_2D_8X5_SRGB:
return "ASTC_2D_8X5_SRGB";
case PixelFormat::ASTC_2D_5X4_SRGB:
return "ASTC_2D_5X4_SRGB";
case PixelFormat::ASTC_2D_5X5_UNORM:
return "ASTC_2D_5X5_UNORM";
case PixelFormat::ASTC_2D_5X5_SRGB:
return "ASTC_2D_5X5_SRGB";
case PixelFormat::ASTC_2D_10X8_UNORM:
return "ASTC_2D_10X8_UNORM";
case PixelFormat::ASTC_2D_10X8_SRGB:
return "ASTC_2D_10X8_SRGB";
case PixelFormat::ASTC_2D_6X6_UNORM:
return "ASTC_2D_6X6_UNORM";
case PixelFormat::ASTC_2D_6X6_SRGB:
return "ASTC_2D_6X6_SRGB";
case PixelFormat::ASTC_2D_10X6_UNORM:
return "ASTC_2D_10X6_UNORM";
case PixelFormat::ASTC_2D_10X6_SRGB:
return "ASTC_2D_10X6_SRGB";
case PixelFormat::ASTC_2D_10X5_UNORM:
return "ASTC_2D_10X5_UNORM";
case PixelFormat::ASTC_2D_10X5_SRGB:
return "ASTC_2D_10X5_SRGB";
case PixelFormat::ASTC_2D_10X10_UNORM:
return "ASTC_2D_10X10_UNORM";
case PixelFormat::ASTC_2D_10X10_SRGB:
return "ASTC_2D_10X10_SRGB";
case PixelFormat::ASTC_2D_12X10_UNORM:
return "ASTC_2D_12X10_UNORM";
case PixelFormat::ASTC_2D_12X10_SRGB:
return "ASTC_2D_12X10_SRGB";
case PixelFormat::ASTC_2D_12X12_UNORM:
return "ASTC_2D_12X12_UNORM";
case PixelFormat::ASTC_2D_12X12_SRGB:
return "ASTC_2D_12X12_SRGB";
case PixelFormat::ASTC_2D_8X6_UNORM:
return "ASTC_2D_8X6_UNORM";
case PixelFormat::ASTC_2D_8X6_SRGB:
return "ASTC_2D_8X6_SRGB";
case PixelFormat::ASTC_2D_6X5_UNORM:
return "ASTC_2D_6X5_UNORM";
case PixelFormat::ASTC_2D_6X5_SRGB:
return "ASTC_2D_6X5_SRGB";
case PixelFormat::E5B9G9R9_FLOAT:
return "E5B9G9R9_FLOAT";
case PixelFormat::D32_FLOAT:
return "D32_FLOAT";
case PixelFormat::D16_UNORM:
return "D16_UNORM";
case PixelFormat::X8_D24_UNORM:
return "X8_D24_UNORM";
case PixelFormat::S8_UINT:
return "S8_UINT";
case PixelFormat::D24_UNORM_S8_UINT:
return "D24_UNORM_S8_UINT";
case PixelFormat::S8_UINT_D24_UNORM:
return "S8_UINT_D24_UNORM";
case PixelFormat::D32_FLOAT_S8_UINT:
return "D32_FLOAT_S8_UINT";
case PixelFormat::MaxDepthStencilFormat:
case PixelFormat::Invalid:
return "Invalid";
}
return "Invalid";
}();
return formatter<string_view>::format(name, ctx);
}
};
template <>
struct fmt::formatter<VideoCommon::ImageType> : fmt::formatter<fmt::string_view> {
template <typename FormatContext>
auto format(VideoCommon::ImageType type, FormatContext& ctx) const {
const string_view name = [type] {
using VideoCommon::ImageType;
switch (type) {
case ImageType::e1D:
return "1D";
case ImageType::e2D:
return "2D";
case ImageType::e3D:
return "3D";
case ImageType::Linear:
return "Linear";
case ImageType::Buffer:
return "Buffer";
}
return "Invalid";
}();
return formatter<string_view>::format(name, ctx);
}
};
template <> template <>
struct fmt::formatter<VideoCommon::Extent3D> { struct fmt::formatter<VideoCommon::Extent3D> {
constexpr auto parse(fmt::format_parse_context& ctx) { constexpr auto parse(fmt::format_parse_context& ctx) {
@ -263,8 +22,7 @@ struct fmt::formatter<VideoCommon::Extent3D> {
template <typename FormatContext> template <typename FormatContext>
auto format(const VideoCommon::Extent3D& extent, FormatContext& ctx) const { auto format(const VideoCommon::Extent3D& extent, FormatContext& ctx) const {
return fmt::format_to(ctx.out(), "{{{}, {}, {}}}", extent.width, extent.height, return fmt::format_to(ctx.out(), "{{{}, {}, {}}}", extent.width, extent.height, extent.depth);
extent.depth);
} }
}; };

View file

@ -1,7 +1,9 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project // SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include "video_core/control/channel_state_cache.inc"
#include "video_core/texture_cache/texture_cache_base.h" #include "video_core/texture_cache/texture_cache_base.h"
namespace VideoCommon { namespace VideoCommon {
@ -10,6 +12,4 @@ TextureCacheChannelInfo::TextureCacheChannelInfo(Tegra::Control::ChannelState& s
: ChannelInfo(state), graphics_image_table{gpu_memory}, graphics_sampler_table{gpu_memory}, : ChannelInfo(state), graphics_image_table{gpu_memory}, graphics_sampler_table{gpu_memory},
compute_image_table{gpu_memory}, compute_sampler_table{gpu_memory} {} compute_image_table{gpu_memory}, compute_sampler_table{gpu_memory} {}
template class VideoCommon::ChannelSetupCaches<VideoCommon::TextureCacheChannelInfo>;
} // namespace VideoCommon } // namespace VideoCommon

View file

@ -22,6 +22,14 @@
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
// X11 defines
#ifdef Success
# undef Success
#endif
#ifdef BadValue
# undef BadValue
#endif
// Define maintenance 7-8 extension names (not yet in official Vulkan headers) // Define maintenance 7-8 extension names (not yet in official Vulkan headers)
#ifndef VK_KHR_MAINTENANCE_7_EXTENSION_NAME #ifndef VK_KHR_MAINTENANCE_7_EXTENSION_NAME
#define VK_KHR_MAINTENANCE_7_EXTENSION_NAME "VK_KHR_maintenance7" #define VK_KHR_MAINTENANCE_7_EXTENSION_NAME "VK_KHR_maintenance7"