diff options
Diffstat (limited to 'src')
198 files changed, 6937 insertions, 5565 deletions
diff --git a/src/common/hex_util.h b/src/common/hex_util.h index a00904939..618f53152 100644 --- a/src/common/hex_util.h +++ b/src/common/hex_util.h @@ -9,6 +9,7 @@ #include <string> #include <vector> #include <fmt/format.h> +#include "common/assert.h" #include "common/common_types.h" namespace Common { @@ -29,6 +30,8 @@ namespace Common { template <std::size_t Size, bool le = false> [[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) { + ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size"); + std::array<u8, Size> out{}; if constexpr (le) { for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) { diff --git a/src/common/typed_address.h b/src/common/typed_address.h index 64f4a07c2..d5e743583 100644 --- a/src/common/typed_address.h +++ b/src/common/typed_address.h @@ -186,68 +186,68 @@ static_assert(std::is_trivially_destructible_v<PhysicalAddress>); static_assert(std::is_trivially_destructible_v<VirtualAddress>); static_assert(std::is_trivially_destructible_v<ProcessAddress>); -static_assert(Null<uint64_t> == 0); +static_assert(Null<uint64_t> == 0U); static_assert(Null<PhysicalAddress> == Null<uint64_t>); static_assert(Null<VirtualAddress> == Null<uint64_t>); static_assert(Null<ProcessAddress> == Null<uint64_t>); // Constructor/assignment validations. static_assert([] { - const PhysicalAddress a(5); + const PhysicalAddress a(5U); PhysicalAddress b(a); return b; -}() == PhysicalAddress(5)); +}() == PhysicalAddress(5U)); static_assert([] { - const PhysicalAddress a(5); - PhysicalAddress b(10); + const PhysicalAddress a(5U); + PhysicalAddress b(10U); b = a; return b; -}() == PhysicalAddress(5)); +}() == PhysicalAddress(5U)); // Arithmetic validations. -static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15)); -static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5)); +static_assert(PhysicalAddress(10U) + 5U == PhysicalAddress(15U)); +static_assert(PhysicalAddress(10U) - 5U == PhysicalAddress(5U)); static_assert([] { - PhysicalAddress v(10); - v += 5; + PhysicalAddress v(10U); + v += 5U; return v; -}() == PhysicalAddress(15)); +}() == PhysicalAddress(15U)); static_assert([] { - PhysicalAddress v(10); - v -= 5; + PhysicalAddress v(10U); + v -= 5U; return v; -}() == PhysicalAddress(5)); -static_assert(PhysicalAddress(10)++ == PhysicalAddress(10)); -static_assert(++PhysicalAddress(10) == PhysicalAddress(11)); -static_assert(PhysicalAddress(10)-- == PhysicalAddress(10)); -static_assert(--PhysicalAddress(10) == PhysicalAddress(9)); +}() == PhysicalAddress(5U)); +static_assert(PhysicalAddress(10U)++ == PhysicalAddress(10U)); +static_assert(++PhysicalAddress(10U) == PhysicalAddress(11U)); +static_assert(PhysicalAddress(10U)-- == PhysicalAddress(10U)); +static_assert(--PhysicalAddress(10U) == PhysicalAddress(9U)); // Logical validations. -static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111); -static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101); -static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110); -static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010); -static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101); -static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010); -static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000); -static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101); -static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111); -static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111); +static_assert((PhysicalAddress(0b11111111U) >> 1) == 0b01111111U); +static_assert((PhysicalAddress(0b10101010U) >> 1) == 0b01010101U); +static_assert((PhysicalAddress(0b11111111U) << 1) == 0b111111110U); +static_assert((PhysicalAddress(0b01010101U) << 1) == 0b10101010U); +static_assert((PhysicalAddress(0b11111111U) & 0b01010101U) == 0b01010101U); +static_assert((PhysicalAddress(0b11111111U) & 0b10101010U) == 0b10101010U); +static_assert((PhysicalAddress(0b01010101U) & 0b10101010U) == 0b00000000U); +static_assert((PhysicalAddress(0b00000000U) | 0b01010101U) == 0b01010101U); +static_assert((PhysicalAddress(0b11111111U) | 0b01010101U) == 0b11111111U); +static_assert((PhysicalAddress(0b10101010U) | 0b01010101U) == 0b11111111U); // Comparisons. -static_assert(PhysicalAddress(0) == PhysicalAddress(0)); -static_assert(PhysicalAddress(0) != PhysicalAddress(1)); -static_assert(PhysicalAddress(0) < PhysicalAddress(1)); -static_assert(PhysicalAddress(0) <= PhysicalAddress(1)); -static_assert(PhysicalAddress(1) > PhysicalAddress(0)); -static_assert(PhysicalAddress(1) >= PhysicalAddress(0)); - -static_assert(!(PhysicalAddress(0) == PhysicalAddress(1))); -static_assert(!(PhysicalAddress(0) != PhysicalAddress(0))); -static_assert(!(PhysicalAddress(1) < PhysicalAddress(0))); -static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0))); -static_assert(!(PhysicalAddress(0) > PhysicalAddress(1))); -static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1))); +static_assert(PhysicalAddress(0U) == PhysicalAddress(0U)); +static_assert(PhysicalAddress(0U) != PhysicalAddress(1U)); +static_assert(PhysicalAddress(0U) < PhysicalAddress(1U)); +static_assert(PhysicalAddress(0U) <= PhysicalAddress(1U)); +static_assert(PhysicalAddress(1U) > PhysicalAddress(0U)); +static_assert(PhysicalAddress(1U) >= PhysicalAddress(0U)); + +static_assert(!(PhysicalAddress(0U) == PhysicalAddress(1U))); +static_assert(!(PhysicalAddress(0U) != PhysicalAddress(0U))); +static_assert(!(PhysicalAddress(1U) < PhysicalAddress(0U))); +static_assert(!(PhysicalAddress(1U) <= PhysicalAddress(0U))); +static_assert(!(PhysicalAddress(0U) > PhysicalAddress(1U))); +static_assert(!(PhysicalAddress(0U) >= PhysicalAddress(1U))); } // namespace Common diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 570acb193..eb8f643a2 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -775,6 +775,9 @@ add_library(core STATIC hle/service/nvnflinger/graphic_buffer_producer.h hle/service/nvnflinger/hos_binder_driver_server.cpp hle/service/nvnflinger/hos_binder_driver_server.h + hle/service/nvnflinger/hardware_composer.cpp + hle/service/nvnflinger/hardware_composer.h + hle/service/nvnflinger/hwc_layer.h hle/service/nvnflinger/nvnflinger.cpp hle/service/nvnflinger/nvnflinger.h hle/service/nvnflinger/parcel.h diff --git a/src/core/debugger/gdbstub_arch.cpp b/src/core/debugger/gdbstub_arch.cpp index f2a407dc8..452f565be 100644 --- a/src/core/debugger/gdbstub_arch.cpp +++ b/src/core/debugger/gdbstub_arch.cpp @@ -383,7 +383,7 @@ std::string GDBStubA32::RegRead(const Kernel::KThread* thread, size_t id) const } else if (id == CPSR_REGISTER) { return ValueToHex(context.pstate); } else if (id >= D0_REGISTER && id < Q0_REGISTER) { - return ValueToHex(fprs[id - D0_REGISTER][0]); + return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]); } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { return ValueToHex(fprs[id - Q0_REGISTER]); } else if (id == FPSCR_REGISTER) { @@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v } else if (id == CPSR_REGISTER) { context.pstate = HexToValue<u32>(value); } else if (id >= D0_REGISTER && id < Q0_REGISTER) { - fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0}; + fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value); } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { fprs[id - Q0_REGISTER] = HexToValue<u128>(value); } else if (id == FPSCR_REGISTER) { diff --git a/src/core/hle/service/caps/caps_a.cpp b/src/core/hle/service/caps/caps_a.cpp index 69acb3a8b..47ff072c5 100644 --- a/src/core/hle/service/caps/caps_a.cpp +++ b/src/core/hle/service/caps/caps_a.cpp @@ -5,7 +5,7 @@ #include "core/hle/service/caps/caps_a.h" #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_result.h" -#include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" namespace Service::Capture { @@ -18,9 +18,9 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, {0, nullptr, "GetAlbumFileCount"}, {1, nullptr, "GetAlbumFileList"}, {2, nullptr, "LoadAlbumFile"}, - {3, &IAlbumAccessorService::DeleteAlbumFile, "DeleteAlbumFile"}, + {3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"}, {4, nullptr, "StorageCopyAlbumFile"}, - {5, &IAlbumAccessorService::IsAlbumMounted, "IsAlbumMounted"}, + {5, C<&IAlbumAccessorService::IsAlbumMounted>, "IsAlbumMounted"}, {6, nullptr, "GetAlbumUsage"}, {7, nullptr, "GetAlbumFileSize"}, {8, nullptr, "LoadAlbumFileThumbnail"}, @@ -33,18 +33,18 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, {15, nullptr, "GetAlbumUsage3"}, {16, nullptr, "GetAlbumMountResult"}, {17, nullptr, "GetAlbumUsage16"}, - {18, &IAlbumAccessorService::Unknown18, "Unknown18"}, + {18, C<&IAlbumAccessorService::Unknown18>, "Unknown18"}, {19, nullptr, "Unknown19"}, {100, nullptr, "GetAlbumFileCountEx0"}, - {101, &IAlbumAccessorService::GetAlbumFileListEx0, "GetAlbumFileListEx0"}, + {101, C<&IAlbumAccessorService::GetAlbumFileListEx0>, "GetAlbumFileListEx0"}, {202, nullptr, "SaveEditedScreenShot"}, {301, nullptr, "GetLastThumbnail"}, {302, nullptr, "GetLastOverlayMovieThumbnail"}, - {401, &IAlbumAccessorService::GetAutoSavingStorage, "GetAutoSavingStorage"}, + {401, C<&IAlbumAccessorService::GetAutoSavingStorage>, "GetAutoSavingStorage"}, {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, - {1002, &IAlbumAccessorService::LoadAlbumScreenShotImageEx1, "LoadAlbumScreenShotImageEx1"}, - {1003, &IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1, "LoadAlbumScreenShotThumbnailImageEx1"}, + {1002, C<&IAlbumAccessorService::LoadAlbumScreenShotImageEx1>, "LoadAlbumScreenShotImageEx1"}, + {1003, C<&IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1>, "LoadAlbumScreenShotThumbnailImageEx1"}, {8001, nullptr, "ForceAlbumUnmounted"}, {8002, nullptr, "ResetAlbumMountStatus"}, {8011, nullptr, "RefreshAlbumCache"}, @@ -62,138 +62,70 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, IAlbumAccessorService::~IAlbumAccessorService() = default; -void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto file_id{rp.PopRaw<AlbumFileId>()}; - +Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) { LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", file_id.application_id, file_id.storage, file_id.type); - Result result = manager->DeleteAlbumFile(file_id); - result = TranslateResult(result); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + const Result result = manager->DeleteAlbumFile(file_id); + R_RETURN(TranslateResult(result)); } -void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto storage{rp.PopEnum<AlbumStorage>()}; - +Result IAlbumAccessorService::IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage) { LOG_INFO(Service_Capture, "called, storage={}", storage); - Result result = manager->IsAlbumMounted(storage); - const bool is_mounted = result.IsSuccess(); - result = TranslateResult(result); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push<u8>(is_mounted); + const Result result = manager->IsAlbumMounted(storage); + *out_is_mounted = result.IsSuccess(); + R_RETURN(TranslateResult(result)); } -void IAlbumAccessorService::Unknown18(HLERequestContext& ctx) { - struct UnknownBuffer { - INSERT_PADDING_BYTES(0x10); - }; - static_assert(sizeof(UnknownBuffer) == 0x10, "UnknownBuffer is an invalid size"); - +Result IAlbumAccessorService::Unknown18( + Out<u32> out_buffer_size, + OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer) { LOG_WARNING(Service_Capture, "(STUBBED) called"); - - std::vector<UnknownBuffer> buffer{}; - - if (!buffer.empty()) { - ctx.WriteBuffer(buffer); - } - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(buffer.size())); + *out_buffer_size = 0; + R_SUCCEED(); } -void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto storage{rp.PopEnum<AlbumStorage>()}; - const auto flags{rp.Pop<u8>()}; - const auto album_entry_size{ctx.GetWriteBufferNumElements<AlbumEntry>()}; - +Result IAlbumAccessorService::GetAlbumFileListEx0( + Out<u64> out_entries_size, AlbumStorage storage, u8 flags, + OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) { LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags); - std::vector<AlbumEntry> entries; - Result result = manager->GetAlbumFileList(entries, storage, flags); - result = TranslateResult(result); - - entries.resize(std::min(album_entry_size, entries.size())); - - if (!entries.empty()) { - ctx.WriteBuffer(entries); - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push<u64>(entries.size()); + const Result result = manager->GetAlbumFileList(out_entries, *out_entries_size, storage, flags); + R_RETURN(TranslateResult(result)); } -void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { +Result IAlbumAccessorService::GetAutoSavingStorage(Out<bool> out_is_autosaving) { LOG_WARNING(Service_Capture, "(STUBBED) called"); - bool is_autosaving{}; - Result result = manager->GetAutoSavingStorage(is_autosaving); - result = TranslateResult(result); - - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(result); - rb.Push<u8>(is_autosaving); + const Result result = manager->GetAutoSavingStorage(*out_is_autosaving); + R_RETURN(TranslateResult(result)); } -void IAlbumAccessorService::LoadAlbumScreenShotImageEx1(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto file_id{rp.PopRaw<AlbumFileId>()}; - const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; - const auto image_buffer_size{ctx.GetWriteBufferSize(1)}; - +Result IAlbumAccessorService::LoadAlbumScreenShotImageEx1( + const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options, + OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output, + OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image, + OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) { LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); - std::vector<u8> image; - LoadAlbumScreenShotImageOutput image_output; - Result result = - manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options); - result = TranslateResult(result); - - if (image.size() > image_buffer_size) { - result = ResultWorkMemoryError; - } - - if (result.IsSuccess()) { - ctx.WriteBuffer(image_output, 0); - ctx.WriteBuffer(image, 1); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + const Result result = + manager->LoadAlbumScreenShotImage(*out_image_output, out_image, file_id, decoder_options); + R_RETURN(TranslateResult(result)); } -void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto file_id{rp.PopRaw<AlbumFileId>()}; - const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; - +Result IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1( + const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options, + OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output, + OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image, + OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) { LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); - std::vector<u8> image(ctx.GetWriteBufferSize(1)); - LoadAlbumScreenShotImageOutput image_output; - Result result = - manager->LoadAlbumScreenShotThumbnail(image_output, image, file_id, decoder_options); - result = TranslateResult(result); - - if (result.IsSuccess()) { - ctx.WriteBuffer(image_output, 0); - ctx.WriteBuffer(image, 1); - } - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + const Result result = manager->LoadAlbumScreenShotThumbnail(*out_image_output, out_image, + file_id, decoder_options); + R_RETURN(TranslateResult(result)); } Result IAlbumAccessorService::TranslateResult(Result in_result) { diff --git a/src/core/hle/service/caps/caps_a.h b/src/core/hle/service/caps/caps_a.h index c90cff71e..2cb9b4547 100644 --- a/src/core/hle/service/caps/caps_a.h +++ b/src/core/hle/service/caps/caps_a.h @@ -3,6 +3,8 @@ #pragma once +#include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" namespace Core { @@ -19,13 +21,31 @@ public: ~IAlbumAccessorService() override; private: - void DeleteAlbumFile(HLERequestContext& ctx); - void IsAlbumMounted(HLERequestContext& ctx); - void Unknown18(HLERequestContext& ctx); - void GetAlbumFileListEx0(HLERequestContext& ctx); - void GetAutoSavingStorage(HLERequestContext& ctx); - void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx); - void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx); + Result DeleteAlbumFile(AlbumFileId file_id); + + Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage); + + Result Unknown18( + Out<u32> out_buffer_size, + OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> + out_buffer); + + Result GetAlbumFileListEx0(Out<u64> out_entries_size, AlbumStorage storage, u8 flags, + OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries); + + Result GetAutoSavingStorage(Out<bool> out_is_autosaving); + + Result LoadAlbumScreenShotImageEx1( + const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options, + OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output, + OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image, + OutArray<u8, BufferAttr_HipcMapAlias> out_buffer); + + Result LoadAlbumScreenShotThumbnailImageEx1( + const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options, + OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output, + OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image, + OutArray<u8, BufferAttr_HipcMapAlias> out_buffer); Result TranslateResult(Result in_result); diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp index 1e7fe6474..6993c04c2 100644 --- a/src/core/hle/service/caps/caps_c.cpp +++ b/src/core/hle/service/caps/caps_c.cpp @@ -6,6 +6,7 @@ #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_result.h" #include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" namespace Service::Capture { @@ -17,7 +18,7 @@ IAlbumControlService::IAlbumControlService(Core::System& system_, static const FunctionInfo functions[] = { {1, nullptr, "CaptureRawImage"}, {2, nullptr, "CaptureRawImageWithTimeout"}, - {33, &IAlbumControlService::SetShimLibraryVersion, "SetShimLibraryVersion"}, + {33, C<&IAlbumControlService::SetShimLibraryVersion>, "SetShimLibraryVersion"}, {1001, nullptr, "RequestTakingScreenShot"}, {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, {1011, nullptr, "NotifyTakingScreenShotRefused"}, @@ -42,16 +43,11 @@ IAlbumControlService::IAlbumControlService(Core::System& system_, IAlbumControlService::~IAlbumControlService() = default; -void IAlbumControlService::SetShimLibraryVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto library_version{rp.Pop<u64>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - +Result IAlbumControlService::SetShimLibraryVersion(ShimLibraryVersion library_version, + ClientAppletResourceUserId aruid) { LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", - library_version, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + library_version, aruid.pid); + R_SUCCEED(); } } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h index 92ba242db..0ecdfa114 100644 --- a/src/core/hle/service/caps/caps_c.h +++ b/src/core/hle/service/caps/caps_c.h @@ -3,6 +3,7 @@ #pragma once +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" namespace Core { @@ -11,6 +12,7 @@ class System; namespace Service::Capture { class AlbumManager; +enum class ShimLibraryVersion : u64; class IAlbumControlService final : public ServiceFramework<IAlbumControlService> { public: @@ -19,7 +21,8 @@ public: ~IAlbumControlService() override; private: - void SetShimLibraryVersion(HLERequestContext& ctx); + Result SetShimLibraryVersion(ShimLibraryVersion library_version, + ClientAppletResourceUserId aruid); std::shared_ptr<AlbumManager> manager = nullptr; }; diff --git a/src/core/hle/service/caps/caps_manager.cpp b/src/core/hle/service/caps/caps_manager.cpp index 3a22b135f..7f0bc127f 100644 --- a/src/core/hle/service/caps/caps_manager.cpp +++ b/src/core/hle/service/caps/caps_manager.cpp @@ -58,8 +58,8 @@ Result AlbumManager::IsAlbumMounted(AlbumStorage storage) { return is_mounted ? ResultSuccess : ResultIsNotMounted; } -Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, - u8 flags) const { +Result AlbumManager::GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count, + AlbumStorage storage, u8 flags) const { if (storage > AlbumStorage::Sd) { return ResultInvalidStorage; } @@ -72,51 +72,55 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu if (file_id.storage != storage) { continue; } - if (out_entries.size() >= SdAlbumFileLimit) { + if (out_entries_count >= SdAlbumFileLimit) { + break; + } + if (out_entries_count >= out_entries.size()) { break; } const auto entry_size = Common::FS::GetSize(path); - out_entries.push_back({ + out_entries[out_entries_count++] = { .entry_size = entry_size, .file_id = file_id, - }); + }; } return ResultSuccess; } -Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, - ContentType content_type, s64 start_posix_time, - s64 end_posix_time, u64 aruid) const { +Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries, + u64& out_entries_count, ContentType content_type, + s64 start_posix_time, s64 end_posix_time, u64 aruid) const { if (!is_mounted) { return ResultIsNotMounted; } - std::vector<ApplicationAlbumEntry> album_entries; + std::vector<ApplicationAlbumEntry> album_entries(out_entries.size()); const auto start_date = ConvertToAlbumDateTime(start_posix_time); const auto end_date = ConvertToAlbumDateTime(end_posix_time); - const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid); + const auto result = GetAlbumFileList(album_entries, out_entries_count, content_type, start_date, + end_date, aruid); if (result.IsError()) { return result; } - for (const auto& album_entry : album_entries) { - ApplicationAlbumFileEntry entry{ - .entry = album_entry, - .datetime = album_entry.datetime, + for (std::size_t i = 0; i < out_entries_count; i++) { + out_entries[i] = { + .entry = album_entries[i], + .datetime = album_entries[i].datetime, .unknown = {}, }; - out_entries.push_back(entry); } return ResultSuccess; } -Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, - ContentType content_type, AlbumFileDateTime start_date, - AlbumFileDateTime end_date, u64 aruid) const { +Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries, + u64& out_entries_count, ContentType content_type, + AlbumFileDateTime start_date, AlbumFileDateTime end_date, + u64 aruid) const { if (!is_mounted) { return ResultIsNotMounted; } @@ -131,12 +135,15 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en if (file_id.date < end_date) { continue; } - if (out_entries.size() >= SdAlbumFileLimit) { + if (out_entries_count >= SdAlbumFileLimit) { + break; + } + if (out_entries_count >= out_entries.size()) { break; } const auto entry_size = Common::FS::GetSize(path); - ApplicationAlbumEntry entry{ + out_entries[out_entries_count++] = { .size = entry_size, .hash{}, .datetime = file_id.date, @@ -144,7 +151,6 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en .content = content_type, .unknown = 1, }; - out_entries.push_back(entry); } return ResultSuccess; @@ -156,8 +162,7 @@ Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const { } Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, - std::vector<u8>& out_image, - const AlbumFileId& file_id, + std::span<u8> out_image, const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const { if (file_id.storage > AlbumStorage::Sd) { return ResultInvalidStorage; @@ -176,7 +181,9 @@ Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& ou .orientation = AlbumImageOrientation::None, .unknown_1{}, .unknown_2{}, + .pad163{}, }, + .pad179{}, }; std::filesystem::path path; @@ -186,14 +193,12 @@ Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& ou return result; } - out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha); - return LoadImage(out_image, path, static_cast<int>(out_image_output.width), +static_cast<int>(out_image_output.height), decoder_options.flags); } Result AlbumManager::LoadAlbumScreenShotThumbnail( - LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image, + LoadAlbumScreenShotImageOutput& out_image_output, std::span<u8> out_image, const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const { if (file_id.storage > AlbumStorage::Sd) { return ResultInvalidStorage; @@ -212,7 +217,9 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail( .orientation = AlbumImageOrientation::None, .unknown_1{}, .unknown_2{}, + .pad163{}, }, + .pad179{}, }; std::filesystem::path path; @@ -222,8 +229,6 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail( return result; } - out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha); - return LoadImage(out_image, path, static_cast<int>(out_image_output.width), +static_cast<int>(out_image_output.height), decoder_options.flags); } diff --git a/src/core/hle/service/caps/caps_manager.h b/src/core/hle/service/caps/caps_manager.h index 6fd34f589..893a9075a 100644 --- a/src/core/hle/service/caps/caps_manager.h +++ b/src/core/hle/service/caps/caps_manager.h @@ -42,20 +42,20 @@ public: Result DeleteAlbumFile(const AlbumFileId& file_id); Result IsAlbumMounted(AlbumStorage storage); - Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, - u8 flags) const; - Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, - ContentType content_type, s64 start_posix_time, s64 end_posix_time, - u64 aruid) const; - Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, + Result GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count, + AlbumStorage storage, u8 flags) const; + Result GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries, + u64& out_entries_count, ContentType content_type, s64 start_posix_time, + s64 end_posix_time, u64 aruid) const; + Result GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries, u64& out_entries_count, ContentType content_type, AlbumFileDateTime start_date, AlbumFileDateTime end_date, u64 aruid) const; Result GetAutoSavingStorage(bool& out_is_autosaving) const; Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, - std::vector<u8>& out_image, const AlbumFileId& file_id, + std::span<u8> out_image, const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const; Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output, - std::vector<u8>& out_image, const AlbumFileId& file_id, + std::span<u8> out_image, const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const; Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, diff --git a/src/core/hle/service/caps/caps_ss.cpp b/src/core/hle/service/caps/caps_ss.cpp index eab023568..dfa7f1a84 100644 --- a/src/core/hle/service/caps/caps_ss.cpp +++ b/src/core/hle/service/caps/caps_ss.cpp @@ -3,10 +3,9 @@ #include "common/logging/log.h" #include "core/hle/service/caps/caps_manager.h" -#include "core/hle/service/caps/caps_types.h" -#include "core/hle/service/ipc_helpers.h" - #include "core/hle/service/caps/caps_ss.h" +#include "core/hle/service/cmif_serialization.h" +#include "core/hle/service/ipc_helpers.h" namespace Service::Capture { @@ -17,9 +16,9 @@ IScreenShotService::IScreenShotService(Core::System& system_, static const FunctionInfo functions[] = { {201, nullptr, "SaveScreenShot"}, {202, nullptr, "SaveEditedScreenShot"}, - {203, &IScreenShotService::SaveScreenShotEx0, "SaveScreenShotEx0"}, + {203, C<&IScreenShotService::SaveScreenShotEx0>, "SaveScreenShotEx0"}, {204, nullptr, "SaveEditedScreenShotEx0"}, - {206, &IScreenShotService::SaveEditedScreenShotEx1, "SaveEditedScreenShotEx1"}, + {206, C<&IScreenShotService::SaveEditedScreenShotEx1>, "SaveEditedScreenShotEx1"}, {208, nullptr, "SaveScreenShotOfMovieEx1"}, {1000, nullptr, "Unknown1000"}, }; @@ -30,69 +29,38 @@ IScreenShotService::IScreenShotService(Core::System& system_, IScreenShotService::~IScreenShotService() = default; -void IScreenShotService::SaveScreenShotEx0(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - ScreenShotAttribute attribute{}; - AlbumReportOption report_option{}; - INSERT_PADDING_BYTES(0x4); - u64 applet_resource_user_id{}; - }; - static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - const auto image_data_buffer = ctx.ReadBuffer(); - +Result IScreenShotService::SaveScreenShotEx0( + Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, ClientAppletResourceUserId aruid, + InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + image_data_buffer) { LOG_INFO(Service_Capture, "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", - parameters.report_option, image_data_buffer.size(), - parameters.applet_resource_user_id); + report_option, image_data_buffer.size(), aruid.pid); - ApplicationAlbumEntry entry{}; manager->FlipVerticallyOnWrite(false); - const auto result = - manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, - image_data_buffer, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(result); - rb.PushRaw(entry); + R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer, + aruid.pid)); } -void IScreenShotService::SaveEditedScreenShotEx1(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - ScreenShotAttribute attribute; - u64 width; - u64 height; - u64 thumbnail_width; - u64 thumbnail_height; - AlbumFileId file_id; - }; - static_assert(sizeof(Parameters) == 0x78, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - const auto application_data_buffer = ctx.ReadBuffer(0); - const auto image_data_buffer = ctx.ReadBuffer(1); - const auto thumbnail_image_data_buffer = ctx.ReadBuffer(2); - +Result IScreenShotService::SaveEditedScreenShotEx1( + Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width, + u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id, + const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer, + const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + image_data_buffer, + const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + thumbnail_image_data_buffer) { LOG_INFO(Service_Capture, "called, width={}, height={}, thumbnail_width={}, thumbnail_height={}, " - "application_id={:016x}, storage={}, type={}, app_data_buffer_size={}, " + "application_id={:016x}, storage={}, type={}, " "image_data_buffer_size={}, thumbnail_image_buffer_size={}", - parameters.width, parameters.height, parameters.thumbnail_width, - parameters.thumbnail_height, parameters.file_id.application_id, - parameters.file_id.storage, parameters.file_id.type, application_data_buffer.size(), - image_data_buffer.size(), thumbnail_image_data_buffer.size()); + width, height, thumbnail_width, thumbnail_height, file_id.application_id, + file_id.storage, file_id.type, image_data_buffer.size(), + thumbnail_image_data_buffer.size()); - ApplicationAlbumEntry entry{}; manager->FlipVerticallyOnWrite(false); - const auto result = manager->SaveEditedScreenShot(entry, parameters.attribute, - parameters.file_id, image_data_buffer); - - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(result); - rb.PushRaw(entry); + R_RETURN(manager->SaveEditedScreenShot(*out_entry, attribute, file_id, image_data_buffer)); } } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_ss.h b/src/core/hle/service/caps/caps_ss.h index a7e9972ab..da4b4cc5f 100644 --- a/src/core/hle/service/caps/caps_ss.h +++ b/src/core/hle/service/caps/caps_ss.h @@ -3,6 +3,8 @@ #pragma once +#include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" namespace Core { @@ -17,8 +19,20 @@ public: ~IScreenShotService() override; private: - void SaveScreenShotEx0(HLERequestContext& ctx); - void SaveEditedScreenShotEx1(HLERequestContext& ctx); + Result SaveScreenShotEx0( + Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, ClientAppletResourceUserId aruid, + InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + image_data_buffer); + + Result SaveEditedScreenShotEx1( + Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width, + u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id, + const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer, + const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + image_data_buffer, + const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + thumbnail_image_data_buffer); std::shared_ptr<AlbumManager> manager; }; diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp index 296b07b00..528f364f5 100644 --- a/src/core/hle/service/caps/caps_su.cpp +++ b/src/core/hle/service/caps/caps_su.cpp @@ -6,6 +6,7 @@ #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_su.h" #include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" #include "video_core/renderer_base.h" @@ -16,10 +17,10 @@ IScreenShotApplicationService::IScreenShotApplicationService( : ServiceFramework{system_, "caps:su"}, manager{album_manager} { // clang-format off static const FunctionInfo functions[] = { - {32, &IScreenShotApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, + {32, C<&IScreenShotApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"}, {201, nullptr, "SaveScreenShot"}, - {203, &IScreenShotApplicationService::SaveScreenShotEx0, "SaveScreenShotEx0"}, - {205, &IScreenShotApplicationService::SaveScreenShotEx1, "SaveScreenShotEx1"}, + {203, C<&IScreenShotApplicationService::SaveScreenShotEx0>, "SaveScreenShotEx0"}, + {205, C<&IScreenShotApplicationService::SaveScreenShotEx1>, "SaveScreenShotEx1"}, {210, nullptr, "SaveScreenShotEx2"}, }; // clang-format on @@ -29,77 +30,40 @@ IScreenShotApplicationService::IScreenShotApplicationService( IScreenShotApplicationService::~IScreenShotApplicationService() = default; -void IScreenShotApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto library_version{rp.Pop<u64>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - +Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version, + ClientAppletResourceUserId aruid) { LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", - library_version, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + library_version, aruid.pid); + R_SUCCEED(); } -void IScreenShotApplicationService::SaveScreenShotEx0(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - ScreenShotAttribute attribute{}; - AlbumReportOption report_option{}; - INSERT_PADDING_BYTES(0x4); - u64 applet_resource_user_id{}; - }; - static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - const auto image_data_buffer = ctx.ReadBuffer(); - +Result IScreenShotApplicationService::SaveScreenShotEx0( + Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, ClientAppletResourceUserId aruid, + InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + image_data_buffer) { LOG_INFO(Service_Capture, "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", - parameters.report_option, image_data_buffer.size(), - parameters.applet_resource_user_id); + report_option, image_data_buffer.size(), aruid.pid); - ApplicationAlbumEntry entry{}; manager->FlipVerticallyOnWrite(false); - const auto result = - manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, - image_data_buffer, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(result); - rb.PushRaw(entry); + R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer, + aruid.pid)); } -void IScreenShotApplicationService::SaveScreenShotEx1(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - ScreenShotAttribute attribute{}; - AlbumReportOption report_option{}; - INSERT_PADDING_BYTES(0x4); - u64 applet_resource_user_id{}; - }; - static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - const auto app_data_buffer = ctx.ReadBuffer(0); - const auto image_data_buffer = ctx.ReadBuffer(1); - +Result IScreenShotApplicationService::SaveScreenShotEx1( + Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, ClientAppletResourceUserId aruid, + const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer, + const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + image_data_buffer) { LOG_INFO(Service_Capture, "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", - parameters.report_option, image_data_buffer.size(), - parameters.applet_resource_user_id); + report_option, image_data_buffer.size(), aruid.pid); - ApplicationAlbumEntry entry{}; - ApplicationData app_data{}; - std::memcpy(&app_data, app_data_buffer.data(), sizeof(ApplicationData)); manager->FlipVerticallyOnWrite(false); - const auto result = - manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, app_data, - image_data_buffer, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 10}; - rb.Push(result); - rb.PushRaw(entry); + R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, *app_data_buffer, + image_data_buffer, aruid.pid)); } void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) { @@ -112,6 +76,7 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r .orientation = Capture::AlbumImageOrientation::None, .unknown_1{}, .unknown_2{}, + .pad163{}, }; renderer.RequestScreenshot( diff --git a/src/core/hle/service/caps/caps_su.h b/src/core/hle/service/caps/caps_su.h index 21912e95f..4b4cbd09e 100644 --- a/src/core/hle/service/caps/caps_su.h +++ b/src/core/hle/service/caps/caps_su.h @@ -3,6 +3,8 @@ #pragma once +#include "core/hle/service/caps/caps_types.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" namespace Core { @@ -26,9 +28,19 @@ private: static constexpr std::size_t screenshot_height = 720; static constexpr std::size_t bytes_per_pixel = 4; - void SetShimLibraryVersion(HLERequestContext& ctx); - void SaveScreenShotEx0(HLERequestContext& ctx); - void SaveScreenShotEx1(HLERequestContext& ctx); + Result SetShimLibraryVersion(ShimLibraryVersion library_version, + ClientAppletResourceUserId aruid); + Result SaveScreenShotEx0( + Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, ClientAppletResourceUserId aruid, + InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + image_data_buffer); + Result SaveScreenShotEx1( + Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, + AlbumReportOption report_option, ClientAppletResourceUserId aruid, + const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer, + const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> + image_data_buffer); std::array<u8, screenshot_width * screenshot_height * bytes_per_pixel> image_data; diff --git a/src/core/hle/service/caps/caps_types.h b/src/core/hle/service/caps/caps_types.h index 589ac28d3..3deaaad5b 100644 --- a/src/core/hle/service/caps/caps_types.h +++ b/src/core/hle/service/caps/caps_types.h @@ -41,6 +41,10 @@ enum class ScreenShotDecoderFlag : u64 { EnableBlockSmoothing = 1 << 1, }; +enum class ShimLibraryVersion : u64 { + Version1 = 1, +}; + // This is nn::capsrv::AlbumFileDateTime struct AlbumFileDateTime { s16 year{}; @@ -144,19 +148,23 @@ static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, "ApplicationAlbumFileEntry has incorrect size."); struct ApplicationData { - std::array<u8, 0x400> data{}; - u32 data_size{}; + std::array<u8, 0x400> data; + u32 data_size; }; static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size"); +static_assert(std::is_trivial_v<ApplicationData>, + "ApplicationData type must be trivially copyable."); struct ScreenShotAttribute { - u32 unknown_0{}; - AlbumImageOrientation orientation{}; - u32 unknown_1{}; - u32 unknown_2{}; - INSERT_PADDING_BYTES(0x30); + u32 unknown_0; + AlbumImageOrientation orientation; + u32 unknown_1; + u32 unknown_2; + INSERT_PADDING_BYTES_NOINIT(0x30); }; static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size"); +static_assert(std::is_trivial_v<ScreenShotAttribute>, + "ScreenShotAttribute type must be trivially copyable."); struct ScreenShotDecodeOption { ScreenShotDecoderFlag flags{}; @@ -165,13 +173,15 @@ struct ScreenShotDecodeOption { static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size"); struct LoadAlbumScreenShotImageOutput { - s64 width{}; - s64 height{}; - ScreenShotAttribute attribute{}; - INSERT_PADDING_BYTES(0x400); + s64 width; + s64 height; + ScreenShotAttribute attribute; + INSERT_PADDING_BYTES_NOINIT(0x400); }; static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450, "LoadAlbumScreenShotImageOutput is an invalid size"); +static_assert(std::is_trivial_v<LoadAlbumScreenShotImageOutput>, + "LoadAlbumScreenShotImageOutput type must be trivially copyable."); struct LoadAlbumScreenShotImageOutputForApplication { s64 width{}; diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp index b6b33fb2f..40d4d05fe 100644 --- a/src/core/hle/service/caps/caps_u.cpp +++ b/src/core/hle/service/caps/caps_u.cpp @@ -5,6 +5,7 @@ #include "core/hle/service/caps/caps_manager.h" #include "core/hle/service/caps/caps_types.h" #include "core/hle/service/caps/caps_u.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/ipc_helpers.h" namespace Service::Capture { @@ -14,8 +15,8 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_, : ServiceFramework{system_, "caps:u"}, manager{album_manager} { // clang-format off static const FunctionInfo functions[] = { - {32, &IAlbumApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, - {102, &IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated, "GetAlbumFileList0AafeAruidDeprecated"}, + {32, C<&IAlbumApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"}, + {102, C<&IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated>, "GetAlbumFileList0AafeAruidDeprecated"}, {103, nullptr, "DeleteAlbumFileByAruid"}, {104, nullptr, "GetAlbumFileSizeByAruid"}, {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, @@ -24,7 +25,7 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_, {130, nullptr, "PrecheckToCreateContentsByAruid"}, {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, - {142, &IAlbumApplicationService::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, + {142, C<&IAlbumApplicationService::GetAlbumFileList3AaeAruid>, "GetAlbumFileList3AaeAruid"}, {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, {60002, nullptr, "OpenAccessorSessionForApplication"}, @@ -36,101 +37,40 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_, IAlbumApplicationService::~IAlbumApplicationService() = default; -void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto library_version{rp.Pop<u64>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - +Result IAlbumApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version, + ClientAppletResourceUserId aruid) { LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", - library_version, applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + library_version, aruid.pid); + R_SUCCEED(); } -void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - ContentType content_type; - INSERT_PADDING_BYTES(7); - s64 start_posix_time; - s64 end_posix_time; - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - +Result IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated( + Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time, s64 end_posix_time, + ClientAppletResourceUserId aruid, + OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries) { LOG_WARNING(Service_Capture, "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, " "applet_resource_user_id={}", - parameters.content_type, parameters.start_posix_time, parameters.end_posix_time, - parameters.applet_resource_user_id); - - Result result = ResultSuccess; - - if (result.IsSuccess()) { - result = manager->IsAlbumMounted(AlbumStorage::Sd); - } - - std::vector<ApplicationAlbumFileEntry> entries; - if (result.IsSuccess()) { - result = manager->GetAlbumFileList(entries, parameters.content_type, - parameters.start_posix_time, parameters.end_posix_time, - parameters.applet_resource_user_id); - } + content_type, start_posix_time, end_posix_time, aruid.pid); - if (!entries.empty()) { - ctx.WriteBuffer(entries); - } - - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push<u64>(entries.size()); + R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd)); + R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type, + start_posix_time, end_posix_time, aruid.pid)); } -void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - ContentType content_type; - INSERT_PADDING_BYTES(1); - AlbumFileDateTime start_date_time; - AlbumFileDateTime end_date_time; - INSERT_PADDING_BYTES(6); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - +Result IAlbumApplicationService::GetAlbumFileList3AaeAruid( + Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time, + AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid, + OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries) { LOG_WARNING(Service_Capture, "(STUBBED) called. content_type={}, start_date={}/{}/{}, " "end_date={}/{}/{}, applet_resource_user_id={}", - parameters.content_type, parameters.start_date_time.year, - parameters.start_date_time.month, parameters.start_date_time.day, - parameters.end_date_time.year, parameters.end_date_time.month, - parameters.end_date_time.day, parameters.applet_resource_user_id); - - Result result = ResultSuccess; - - if (result.IsSuccess()) { - result = manager->IsAlbumMounted(AlbumStorage::Sd); - } - - std::vector<ApplicationAlbumEntry> entries; - if (result.IsSuccess()) { - result = - manager->GetAlbumFileList(entries, parameters.content_type, parameters.start_date_time, - parameters.end_date_time, parameters.applet_resource_user_id); - } - - if (!entries.empty()) { - ctx.WriteBuffer(entries); - } + content_type, start_date_time.year, start_date_time.month, start_date_time.day, + end_date_time.year, end_date_time.month, end_date_time.day, aruid.pid); - IPC::ResponseBuilder rb{ctx, 4}; - rb.Push(result); - rb.Push<u64>(entries.size()); + R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd)); + R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type, + start_date_time, end_date_time, aruid.pid)); } } // namespace Service::Capture diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h index 9458c128e..023ee1fe7 100644 --- a/src/core/hle/service/caps/caps_u.h +++ b/src/core/hle/service/caps/caps_u.h @@ -3,6 +3,7 @@ #pragma once +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" namespace Core { @@ -19,9 +20,18 @@ public: ~IAlbumApplicationService() override; private: - void SetShimLibraryVersion(HLERequestContext& ctx); - void GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx); - void GetAlbumFileList3AaeAruid(HLERequestContext& ctx); + Result SetShimLibraryVersion(ShimLibraryVersion library_version, + ClientAppletResourceUserId aruid); + + Result GetAlbumFileList0AafeAruidDeprecated( + Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time, + s64 end_posix_time, ClientAppletResourceUserId aruid, + OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries); + + Result GetAlbumFileList3AaeAruid( + Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time, + AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid, + OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries); std::shared_ptr<AlbumManager> manager = nullptr; }; diff --git a/src/core/hle/service/cmif_serialization.h b/src/core/hle/service/cmif_serialization.h index 315475e71..e985fe317 100644 --- a/src/core/hle/service/cmif_serialization.h +++ b/src/core/hle/service/cmif_serialization.h @@ -115,6 +115,11 @@ struct ArgumentTraits { static constexpr ArgumentType Type = ArgumentType::InData; }; +template <typename... Ts> +consteval bool ConstIfReference() { + return ((!std::is_reference_v<Ts> || std::is_const_v<std::remove_reference_t<Ts>>) && ... && true); +} + struct RequestLayout { u32 copy_handle_count; u32 move_handle_count; @@ -435,6 +440,7 @@ void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) { } const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false; + static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const"); using MethodArguments = std::tuple<std::remove_cvref_t<A>...>; OutTemporaryBuffers buffers{}; diff --git a/src/core/hle/service/cmif_types.h b/src/core/hle/service/cmif_types.h index dc06169f4..84f4c2456 100644 --- a/src/core/hle/service/cmif_types.h +++ b/src/core/hle/service/cmif_types.h @@ -4,10 +4,9 @@ #pragma once #include <memory> +#include <span> -#include "common/common_funcs.h" #include "common/common_types.h" -#include "core/hle/service/hle_ipc.h" namespace Service { @@ -22,8 +21,10 @@ class Out { public: using Type = T; + /* implicit */ Out(const Out& t) : raw(t.raw) {} /* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {} /* implicit */ Out(Type* t) : raw(t) {} + Out& operator=(const Out&) = delete; Type* Get() const { return raw; @@ -37,6 +38,10 @@ public: return raw; } + operator Type*() const { + return raw; + } + private: Type* raw; }; @@ -113,8 +118,10 @@ class OutCopyHandle { public: using Type = T*; + /* implicit */ OutCopyHandle(const OutCopyHandle& t) : raw(t.raw) {} /* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {} /* implicit */ OutCopyHandle(Type* t) : raw(t) {} + OutCopyHandle& operator=(const OutCopyHandle&) = delete; Type* Get() const { return raw; @@ -128,6 +135,10 @@ public: return raw; } + operator Type*() const { + return raw; + } + private: Type* raw; }; @@ -137,8 +148,10 @@ class OutMoveHandle { public: using Type = T*; + /* implicit */ OutMoveHandle(const OutMoveHandle& t) : raw(t.raw) {} /* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {} /* implicit */ OutMoveHandle(Type* t) : raw(t) {} + OutMoveHandle& operator=(const OutMoveHandle&) = delete; Type* Get() const { return raw; @@ -152,6 +165,10 @@ public: return raw; } + operator Type*() const { + return raw; + } + private: Type* raw; }; @@ -248,8 +265,10 @@ public: static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize); using Type = T; + /* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {} /* implicit */ OutLargeData(Type* t) : raw(t) {} /* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {} + OutLargeData& operator=(const OutLargeData&) = delete; Type* Get() const { return raw; @@ -263,6 +282,10 @@ public: return raw; } + operator Type*() const { + return raw; + } + private: Type* raw; }; diff --git a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp index 5fe534c73..63c2d3a58 100644 --- a/src/core/hle/service/filesystem/fsp/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp/fsp_srv.cpp @@ -115,6 +115,11 @@ private: if (type->GetName() == "save") { for (const auto& save_id : type->GetSubdirectories()) { for (const auto& user_id : save_id->GetSubdirectories()) { + // Skip non user id subdirectories + if (user_id->GetName().size() != 0x20) { + continue; + } + const auto save_id_numeric = stoull_be(save_id->GetName()); auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); std::reverse(user_id_numeric.begin(), user_id_numeric.end()); @@ -160,6 +165,10 @@ private: } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { // Temporary Storage for (const auto& user_id : type->GetSubdirectories()) { + // Skip non user id subdirectories + if (user_id->GetName().size() != 0x20) { + continue; + } for (const auto& title_id : user_id->GetSubdirectories()) { if (!title_id->GetFiles().empty() || !title_id->GetSubdirectories().empty()) { diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp index 10376bfac..ea2843462 100644 --- a/src/core/hle/service/glue/glue.cpp +++ b/src/core/hle/service/glue/glue.cpp @@ -31,8 +31,11 @@ void LoopProcess(Core::System& system) { // Error Context server_manager->RegisterNamedService("ectx:aw", std::make_shared<ECTX_AW>(system)); - // Notification Services for application - server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system)); + // Notification Services + server_manager->RegisterNamedService( + "notif:a", std::make_shared<INotificationServicesForApplication>(system)); + server_manager->RegisterNamedService("notif:s", + std::make_shared<INotificationServices>(system)); // Time auto time = std::make_shared<Time::TimeManager>(system); diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp index fec4ad86c..5a03d34c1 100644 --- a/src/core/hle/service/glue/notif.cpp +++ b/src/core/hle/service/glue/notif.cpp @@ -6,48 +6,31 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/glue/notif.h" #include "core/hle/service/ipc_helpers.h" +#include "core/hle/service/kernel_helpers.h" namespace Service::Glue { -NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} { - // clang-format off - static const FunctionInfo functions[] = { - {500, &NOTIF_A::RegisterAlarmSetting, "RegisterAlarmSetting"}, - {510, &NOTIF_A::UpdateAlarmSetting, "UpdateAlarmSetting"}, - {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"}, - {530, &NOTIF_A::LoadApplicationParameter, "LoadApplicationParameter"}, - {540, &NOTIF_A::DeleteAlarmSetting, "DeleteAlarmSetting"}, - {1000, &NOTIF_A::Initialize, "Initialize"}, - }; - // clang-format on - - RegisterHandlers(functions); -} - -NOTIF_A::~NOTIF_A() = default; +namespace { -void NOTIF_A::RegisterAlarmSetting(HLERequestContext& ctx) { - const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); - const auto application_parameter_size = ctx.GetReadBufferSize(1); - - ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), - "alarm_setting_buffer_size is not 0x40 bytes"); - ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), - "application_parameter_size is bigger than 0x400 bytes"); +constexpr inline std::size_t MaxAlarms = 8; - AlarmSetting new_alarm{}; - memcpy(&new_alarm, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); +} - // TODO: Count alarms per game id - if (alarms.size() >= max_alarms) { +Result NotificationServiceImpl::RegisterAlarmSetting(AlarmSettingId* out_alarm_setting_id, + const AlarmSetting& alarm_setting, + std::span<const u8> application_parameter) { + if (alarms.size() > MaxAlarms) { LOG_ERROR(Service_NOTIF, "Alarm limit reached"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } + ASSERT_MSG(application_parameter.size() <= sizeof(ApplicationParameter), + "application_parameter_size is bigger than 0x400 bytes"); + + AlarmSetting new_alarm = alarm_setting; new_alarm.alarm_setting_id = last_alarm_setting_id++; alarms.push_back(new_alarm); @@ -55,100 +38,82 @@ void NOTIF_A::RegisterAlarmSetting(HLERequestContext& ctx) { LOG_WARNING(Service_NOTIF, "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", - application_parameter_size, new_alarm.alarm_setting_id, new_alarm.kind, + application_parameter.size(), new_alarm.alarm_setting_id, new_alarm.kind, new_alarm.muted); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - rb.Push(new_alarm.alarm_setting_id); + *out_alarm_setting_id = new_alarm.alarm_setting_id; + R_SUCCEED(); } -void NOTIF_A::UpdateAlarmSetting(HLERequestContext& ctx) { - const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); - const auto application_parameter_size = ctx.GetReadBufferSize(1); - - ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), - "alarm_setting_buffer_size is not 0x40 bytes"); - ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), +Result NotificationServiceImpl::UpdateAlarmSetting(const AlarmSetting& alarm_setting, + std::span<const u8> application_parameter) { + ASSERT_MSG(application_parameter.size() <= sizeof(ApplicationParameter), "application_parameter_size is bigger than 0x400 bytes"); - AlarmSetting alarm_setting{}; - memcpy(&alarm_setting, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); - const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id); if (alarm_it != alarms.end()) { LOG_DEBUG(Service_NOTIF, "Alarm updated"); *alarm_it = alarm_setting; - // TODO: Save application parameter data } LOG_WARNING(Service_NOTIF, "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", - application_parameter_size, alarm_setting.alarm_setting_id, alarm_setting.kind, + application_parameter.size(), alarm_setting.alarm_setting_id, alarm_setting.kind, alarm_setting.muted); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void NOTIF_A::ListAlarmSettings(HLERequestContext& ctx) { +Result NotificationServiceImpl::ListAlarmSettings(s32* out_count, + std::span<AlarmSetting> out_alarms) { LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size()); - // TODO: Only return alarms of this game id - ctx.WriteBuffer(alarms); + const auto count = std::min(out_alarms.size(), alarms.size()); + for (size_t i = 0; i < count; i++) { + out_alarms[i] = alarms[i]; + } - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(alarms.size())); + *out_count = static_cast<s32>(count); + R_SUCCEED(); } -void NOTIF_A::LoadApplicationParameter(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto alarm_setting_id{rp.Pop<AlarmSettingId>()}; - +Result NotificationServiceImpl::LoadApplicationParameter(u32* out_size, + std::span<u8> out_application_parameter, + AlarmSettingId alarm_setting_id) { const auto alarm_it = GetAlarmFromId(alarm_setting_id); if (alarm_it == alarms.end()) { LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; + R_THROW(ResultUnknown); } // TODO: Read application parameter related to this setting id ApplicationParameter application_parameter{}; LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id); + std::memcpy(out_application_parameter.data(), application_parameter.data(), + std::min(sizeof(application_parameter), out_application_parameter.size())); - ctx.WriteBuffer(application_parameter); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); - rb.Push(static_cast<u32>(application_parameter.size())); + *out_size = static_cast<u32>(application_parameter.size()); + R_SUCCEED(); } -void NOTIF_A::DeleteAlarmSetting(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto alarm_setting_id{rp.Pop<AlarmSettingId>()}; - +Result NotificationServiceImpl::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) { std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) { return alarm.alarm_setting_id == alarm_setting_id; }); LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -void NOTIF_A::Initialize(HLERequestContext& ctx) { +Result NotificationServiceImpl::Initialize(u64 aruid) { // TODO: Load previous alarms from config LOG_WARNING(Service_NOTIF, "(STUBBED) called"); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + R_SUCCEED(); } -std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId( +std::vector<AlarmSetting>::iterator NotificationServiceImpl::GetAlarmFromId( AlarmSettingId alarm_setting_id) { return std::find_if(alarms.begin(), alarms.end(), [alarm_setting_id](const AlarmSetting& alarm) { @@ -156,4 +121,174 @@ std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId( }); } +INotificationServicesForApplication::INotificationServicesForApplication(Core::System& system_) + : ServiceFramework{system_, "notif:a"} { + // clang-format off + static const FunctionInfo functions[] = { + {500, D<&INotificationServicesForApplication::RegisterAlarmSetting>, "RegisterAlarmSetting"}, + {510, D<&INotificationServicesForApplication::UpdateAlarmSetting>, "UpdateAlarmSetting"}, + {520, D<&INotificationServicesForApplication::ListAlarmSettings>, "ListAlarmSettings"}, + {530, D<&INotificationServicesForApplication::LoadApplicationParameter>, "LoadApplicationParameter"}, + {540, D<&INotificationServicesForApplication::DeleteAlarmSetting>, "DeleteAlarmSetting"}, + {1000, D<&INotificationServicesForApplication::Initialize>, "Initialize"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +INotificationServicesForApplication::~INotificationServicesForApplication() = default; + +Result INotificationServicesForApplication::RegisterAlarmSetting( + Out<AlarmSettingId> out_alarm_setting_id, + InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, + InBuffer<BufferAttr_HipcMapAlias> application_parameter) { + R_RETURN(impl.RegisterAlarmSetting(out_alarm_setting_id.Get(), *alarm_setting, + application_parameter)); +} + +Result INotificationServicesForApplication::UpdateAlarmSetting( + InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, + InBuffer<BufferAttr_HipcMapAlias> application_parameter) { + R_RETURN(impl.UpdateAlarmSetting(*alarm_setting, application_parameter)); +} + +Result INotificationServicesForApplication::ListAlarmSettings( + Out<s32> out_count, OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms) { + R_RETURN(impl.ListAlarmSettings(out_count.Get(), out_alarms)); +} + +Result INotificationServicesForApplication::LoadApplicationParameter( + Out<u32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter, + AlarmSettingId alarm_setting_id) { + R_RETURN( + impl.LoadApplicationParameter(out_size.Get(), out_application_parameter, alarm_setting_id)); +} + +Result INotificationServicesForApplication::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) { + R_RETURN(impl.DeleteAlarmSetting(alarm_setting_id)); +} + +Result INotificationServicesForApplication::Initialize(ClientAppletResourceUserId aruid) { + R_RETURN(impl.Initialize(*aruid)); +} + +class INotificationSystemEventAccessor final + : public ServiceFramework<INotificationSystemEventAccessor> { +public: + explicit INotificationSystemEventAccessor(Core::System& system_) + : ServiceFramework{system_, "INotificationSystemEventAccessor"}, + service_context{system_, "INotificationSystemEventAccessor"} { + // clang-format off + static const FunctionInfo functions[] = { + {0, D<&INotificationSystemEventAccessor::GetSystemEvent>, "GetSystemEvent"}, + }; + // clang-format on + + RegisterHandlers(functions); + + notification_event = + service_context.CreateEvent("INotificationSystemEventAccessor:NotificationEvent"); + } + + ~INotificationSystemEventAccessor() { + service_context.CloseEvent(notification_event); + } + +private: + Result GetSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_readable_event) { + LOG_WARNING(Service_NOTIF, "(STUBBED) called"); + + *out_readable_event = ¬ification_event->GetReadableEvent(); + R_SUCCEED(); + } + + KernelHelpers::ServiceContext service_context; + Kernel::KEvent* notification_event; +}; + +INotificationServices::INotificationServices(Core::System& system_) + : ServiceFramework{system_, "notif:s"} { + // clang-format off + static const FunctionInfo functions[] = { + {500, D<&INotificationServices::RegisterAlarmSetting>, "RegisterAlarmSetting"}, + {510, D<&INotificationServices::UpdateAlarmSetting>, "UpdateAlarmSetting"}, + {520, D<&INotificationServices::ListAlarmSettings>, "ListAlarmSettings"}, + {530, D<&INotificationServices::LoadApplicationParameter>, "LoadApplicationParameter"}, + {540, D<&INotificationServices::DeleteAlarmSetting>, "DeleteAlarmSetting"}, + {1000, D<&INotificationServices::Initialize>, "Initialize"}, + {1010, nullptr, "ListNotifications"}, + {1020, nullptr, "DeleteNotification"}, + {1030, nullptr, "ClearNotifications"}, + {1040, D<&INotificationServices::OpenNotificationSystemEventAccessor>, "OpenNotificationSystemEventAccessor"}, + {1500, nullptr, "SetNotificationPresentationSetting"}, + {1510, D<&INotificationServices::GetNotificationPresentationSetting>, "GetNotificationPresentationSetting"}, + {2000, nullptr, "GetAlarmSetting"}, + {2001, nullptr, "GetAlarmSettingWithApplicationParameter"}, + {2010, nullptr, "MuteAlarmSetting"}, + {2020, nullptr, "IsAlarmSettingReady"}, + {8000, nullptr, "RegisterAppletResourceUserId"}, + {8010, nullptr, "UnregisterAppletResourceUserId"}, + {8999, nullptr, "GetCurrentTime"}, + {9000, nullptr, "GetAlarmSettingNextNotificationTime"}, + }; + // clang-format on + + RegisterHandlers(functions); +} + +INotificationServices::~INotificationServices() = default; + +Result INotificationServices::RegisterAlarmSetting( + Out<AlarmSettingId> out_alarm_setting_id, + InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, + InBuffer<BufferAttr_HipcMapAlias> application_parameter) { + R_RETURN(impl.RegisterAlarmSetting(out_alarm_setting_id.Get(), *alarm_setting, + application_parameter)); +} + +Result INotificationServices::UpdateAlarmSetting( + InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, + InBuffer<BufferAttr_HipcMapAlias> application_parameter) { + R_RETURN(impl.UpdateAlarmSetting(*alarm_setting, application_parameter)); +} + +Result INotificationServices::ListAlarmSettings( + Out<s32> out_count, OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms) { + R_RETURN(impl.ListAlarmSettings(out_count.Get(), out_alarms)); +} + +Result INotificationServices::LoadApplicationParameter( + Out<u32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter, + AlarmSettingId alarm_setting_id) { + R_RETURN( + impl.LoadApplicationParameter(out_size.Get(), out_application_parameter, alarm_setting_id)); +} + +Result INotificationServices::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) { + R_RETURN(impl.DeleteAlarmSetting(alarm_setting_id)); +} + +Result INotificationServices::Initialize(ClientAppletResourceUserId aruid) { + R_RETURN(impl.Initialize(*aruid)); +} + +Result INotificationServices::OpenNotificationSystemEventAccessor( + Out<SharedPointer<INotificationSystemEventAccessor>> out_notification_system_event_accessor) { + LOG_WARNING(Service_NOTIF, "(STUBBED) called"); + + *out_notification_system_event_accessor = + std::make_shared<INotificationSystemEventAccessor>(system); + R_SUCCEED(); +} + +Result INotificationServices::GetNotificationPresentationSetting( + Out<NotificationPresentationSetting> out_notification_presentation_setting, + NotificationChannel notification_channel) { + LOG_WARNING(Service_NOTIF, "(STUBBED) called"); + + *out_notification_presentation_setting = {}; + R_SUCCEED(); +} + } // namespace Service::Glue diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h index b1187f3a3..ef2522fdf 100644 --- a/src/core/hle/service/glue/notif.h +++ b/src/core/hle/service/glue/notif.h @@ -7,6 +7,7 @@ #include <vector> #include "common/uuid.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" namespace Core { @@ -15,58 +16,117 @@ class System; namespace Service::Glue { -class NOTIF_A final : public ServiceFramework<NOTIF_A> { +// This is nn::notification::AlarmSettingId +using AlarmSettingId = u16; +static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size"); + +using ApplicationParameter = std::array<u8, 0x400>; +static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size"); + +struct DailyAlarmSetting { + s8 hour; + s8 minute; +}; +static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size"); + +struct WeeklyScheduleAlarmSetting { + INSERT_PADDING_BYTES_NOINIT(0xA); + std::array<DailyAlarmSetting, 0x7> day_of_week; +}; +static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18, + "WeeklyScheduleAlarmSetting is an invalid size"); + +// This is nn::notification::AlarmSetting +struct AlarmSetting { + AlarmSettingId alarm_setting_id; + u8 kind; + u8 muted; + INSERT_PADDING_BYTES_NOINIT(0x4); + Common::UUID account_id; + u64 application_id; + INSERT_PADDING_BYTES_NOINIT(0x8); + WeeklyScheduleAlarmSetting schedule; +}; +static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size"); + +enum class NotificationChannel : u8 { + Unknown0 = 0, +}; + +struct NotificationPresentationSetting { + INSERT_PADDING_BYTES_NOINIT(0x10); +}; +static_assert(sizeof(NotificationPresentationSetting) == 0x10, + "NotificationPresentationSetting is an invalid size"); + +class NotificationServiceImpl { public: - explicit NOTIF_A(Core::System& system_); - ~NOTIF_A() override; + Result RegisterAlarmSetting(AlarmSettingId* out_alarm_setting_id, + const AlarmSetting& alarm_setting, + std::span<const u8> application_parameter); + Result UpdateAlarmSetting(const AlarmSetting& alarm_setting, + std::span<const u8> application_parameter); + Result ListAlarmSettings(s32* out_count, std::span<AlarmSetting> out_alarms); + Result LoadApplicationParameter(u32* out_size, std::span<u8> out_application_parameter, + AlarmSettingId alarm_setting_id); + Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id); + Result Initialize(u64 aruid); private: - static constexpr std::size_t max_alarms = 8; - - // This is nn::notification::AlarmSettingId - using AlarmSettingId = u16; - static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size"); - - using ApplicationParameter = std::array<u8, 0x400>; - static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size"); - - struct DailyAlarmSetting { - s8 hour; - s8 minute; - }; - static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size"); - - struct WeeklyScheduleAlarmSetting { - INSERT_PADDING_BYTES(0xA); - std::array<DailyAlarmSetting, 0x7> day_of_week; - }; - static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18, - "WeeklyScheduleAlarmSetting is an invalid size"); - - // This is nn::notification::AlarmSetting - struct AlarmSetting { - AlarmSettingId alarm_setting_id; - u8 kind; - u8 muted; - INSERT_PADDING_BYTES(0x4); - Common::UUID account_id; - u64 application_id; - INSERT_PADDING_BYTES(0x8); - WeeklyScheduleAlarmSetting schedule; - }; - static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size"); - - void RegisterAlarmSetting(HLERequestContext& ctx); - void UpdateAlarmSetting(HLERequestContext& ctx); - void ListAlarmSettings(HLERequestContext& ctx); - void LoadApplicationParameter(HLERequestContext& ctx); - void DeleteAlarmSetting(HLERequestContext& ctx); - void Initialize(HLERequestContext& ctx); - std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id); - std::vector<AlarmSetting> alarms{}; AlarmSettingId last_alarm_setting_id{}; }; +class INotificationServicesForApplication final + : public ServiceFramework<INotificationServicesForApplication> { +public: + explicit INotificationServicesForApplication(Core::System& system_); + ~INotificationServicesForApplication() override; + +private: + Result RegisterAlarmSetting(Out<AlarmSettingId> out_alarm_setting_id, + InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, + InBuffer<BufferAttr_HipcMapAlias> application_parameter); + Result UpdateAlarmSetting(InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, + InBuffer<BufferAttr_HipcMapAlias> application_parameter); + Result ListAlarmSettings(Out<s32> out_count, + OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms); + Result LoadApplicationParameter(Out<u32> out_size, + OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter, + AlarmSettingId alarm_setting_id); + Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id); + Result Initialize(ClientAppletResourceUserId aruid); + + NotificationServiceImpl impl; +}; + +class INotificationSystemEventAccessor; + +class INotificationServices final : public ServiceFramework<INotificationServices> { +public: + explicit INotificationServices(Core::System& system_); + ~INotificationServices() override; + +private: + Result RegisterAlarmSetting(Out<AlarmSettingId> out_alarm_setting_id, + InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, + InBuffer<BufferAttr_HipcMapAlias> application_parameter); + Result UpdateAlarmSetting(InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, + InBuffer<BufferAttr_HipcMapAlias> application_parameter); + Result ListAlarmSettings(Out<s32> out_count, + OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms); + Result LoadApplicationParameter(Out<u32> out_size, + OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter, + AlarmSettingId alarm_setting_id); + Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id); + Result Initialize(ClientAppletResourceUserId aruid); + Result OpenNotificationSystemEventAccessor(Out<SharedPointer<INotificationSystemEventAccessor>> + out_notification_system_event_accessor); + Result GetNotificationPresentationSetting( + Out<NotificationPresentationSetting> out_notification_presentation_setting, + NotificationChannel notification_channel); + + NotificationServiceImpl impl; +}; } // namespace Service::Glue diff --git a/src/core/hle/service/glue/time/static.cpp b/src/core/hle/service/glue/time/static.cpp index f56db76e1..f8c1218f3 100644 --- a/src/core/hle/service/glue/time/static.cpp +++ b/src/core/hle/service/glue/time/static.cpp @@ -200,7 +200,7 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( } Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( - Out<s64> out_time, Service::PSC::Time::SystemClockContext& context) { + Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context) { SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context)); @@ -216,8 +216,8 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, Result StaticService::GetClockSnapshotFromSystemClockContext( Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot, - Service::PSC::Time::SystemClockContext& user_context, - Service::PSC::Time::SystemClockContext& network_context) { + const Service::PSC::Time::SystemClockContext& user_context, + const Service::PSC::Time::SystemClockContext& network_context) { SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. type={} out_snapshot={} user_context={} network_context={}", type, diff --git a/src/core/hle/service/glue/time/static.h b/src/core/hle/service/glue/time/static.h index d3cc0fdd6..5d3623182 100644 --- a/src/core/hle/service/glue/time/static.h +++ b/src/core/hle/service/glue/time/static.h @@ -58,12 +58,12 @@ public: Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point); Result CalculateMonotonicSystemClockBaseTimePoint( - Out<s64> out_time, Service::PSC::Time::SystemClockContext& context); + Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context); Result GetClockSnapshot(OutClockSnapshot out_snapshot, Service::PSC::Time::TimeType type); Result GetClockSnapshotFromSystemClockContext( Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot, - Service::PSC::Time::SystemClockContext& user_context, - Service::PSC::Time::SystemClockContext& network_context); + const Service::PSC::Time::SystemClockContext& user_context, + const Service::PSC::Time::SystemClockContext& network_context); Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference, InClockSnapshot a, InClockSnapshot b); Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b); diff --git a/src/core/hle/service/glue/time/time_zone.cpp b/src/core/hle/service/glue/time/time_zone.cpp index 98d928697..36f163419 100644 --- a/src/core/hle/service/glue/time/time_zone.cpp +++ b/src/core/hle/service/glue/time/time_zone.cpp @@ -62,7 +62,8 @@ Result TimeZoneService::GetDeviceLocationName( R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name)); } -Result TimeZoneService::SetDeviceLocationName(Service::PSC::Time::LocationName& location_name) { +Result TimeZoneService::SetDeviceLocationName( + const Service::PSC::Time::LocationName& location_name) { LOG_DEBUG(Service_Time, "called. location_name={}", location_name); R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); @@ -110,7 +111,8 @@ Result TimeZoneService::LoadLocationNameList( R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index)); } -Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, Service::PSC::Time::LocationName& name) { +Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, + const Service::PSC::Time::LocationName& name) { LOG_DEBUG(Service_Time, "called. name={}", name); std::scoped_lock l{m_mutex}; @@ -139,7 +141,8 @@ Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( } Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule( - Service::PSC::Time::LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) { + const Service::PSC::Time::LocationName& location_name, + InBuffer<BufferAttr_HipcAutoSelect> binary) { LOG_DEBUG(Service_Time, "called. location_name={}", location_name); R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); diff --git a/src/core/hle/service/glue/time/time_zone.h b/src/core/hle/service/glue/time/time_zone.h index 9c1530966..beb54ddde 100644 --- a/src/core/hle/service/glue/time/time_zone.h +++ b/src/core/hle/service/glue/time/time_zone.h @@ -46,18 +46,20 @@ public: ~TimeZoneService() override; Result GetDeviceLocationName(Out<Service::PSC::Time::LocationName> out_location_name); - Result SetDeviceLocationName(Service::PSC::Time::LocationName& location_name); + Result SetDeviceLocationName(const Service::PSC::Time::LocationName& location_name); Result GetTotalLocationNameCount(Out<u32> out_count); Result LoadLocationNameList( Out<u32> out_count, OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index); - Result LoadTimeZoneRule(OutRule out_rule, Service::PSC::Time::LocationName& location_name); + Result LoadTimeZoneRule(OutRule out_rule, + const Service::PSC::Time::LocationName& location_name); Result GetTimeZoneRuleVersion(Out<Service::PSC::Time::RuleVersion> out_rule_version); Result GetDeviceLocationNameAndUpdatedTime( Out<Service::PSC::Time::LocationName> location_name, Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point); - Result SetDeviceLocationNameWithTimeZoneRule(Service::PSC::Time::LocationName& location_name, - InBuffer<BufferAttr_HipcAutoSelect> binary); + Result SetDeviceLocationNameWithTimeZoneRule( + const Service::PSC::Time::LocationName& location_name, + InBuffer<BufferAttr_HipcAutoSelect> binary); Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary); Result GetDeviceLocationNameOperationEventReadableHandle( OutCopyHandle<Kernel::KReadableEvent> out_event); diff --git a/src/core/hle/service/glue/time/time_zone_binary.cpp b/src/core/hle/service/glue/time/time_zone_binary.cpp index cc50b6b7b..18c6abd6b 100644 --- a/src/core/hle/service/glue/time/time_zone_binary.cpp +++ b/src/core/hle/service/glue/time/time_zone_binary.cpp @@ -65,6 +65,7 @@ Result MountTimeZoneBinary(Core::System& system) { // Validate that the romfs is readable, using invalid firmware keys can cause this to get // set but the files to be garbage. In that case, we want to hit the next path and // synthesise them instead. + g_time_zone_binary_mount_result = ResultSuccess; Service::PSC::Time::LocationName name{"Etc/GMT"}; if (!IsTimeZoneBinaryValid(name)) { ResetTimeZoneBinary(); @@ -98,7 +99,7 @@ void GetTimeZoneBinaryVersionPath(std::string& out_path) { out_path = "/version.txt"; } -void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) { +void GetTimeZoneZonePath(std::string& out_path, const Service::PSC::Time::LocationName& name) { if (g_time_zone_binary_mount_result != ResultSuccess) { return; } @@ -106,7 +107,7 @@ void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName out_path = fmt::format("/zoneinfo/{}", name.data()); } -bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) { +bool IsTimeZoneBinaryValid(const Service::PSC::Time::LocationName& name) { std::string path{}; GetTimeZoneZonePath(path, name); @@ -155,7 +156,7 @@ Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) { } Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, - Service::PSC::Time::LocationName& name) { + const Service::PSC::Time::LocationName& name) { std::string path{}; GetTimeZoneZonePath(path, name); diff --git a/src/core/hle/service/glue/time/time_zone_binary.h b/src/core/hle/service/glue/time/time_zone_binary.h index 461f4577e..9d0a8dfe9 100644 --- a/src/core/hle/service/glue/time/time_zone_binary.h +++ b/src/core/hle/service/glue/time/time_zone_binary.h @@ -19,12 +19,12 @@ void ResetTimeZoneBinary(); Result MountTimeZoneBinary(Core::System& system); void GetTimeZoneBinaryListPath(std::string& out_path); void GetTimeZoneBinaryVersionPath(std::string& out_path); -void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name); -bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name); +void GetTimeZoneZonePath(std::string& out_path, const Service::PSC::Time::LocationName& name); +bool IsTimeZoneBinaryValid(const Service::PSC::Time::LocationName& name); u32 GetTimeZoneCount(); Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version); Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, - Service::PSC::Time::LocationName& name); + const Service::PSC::Time::LocationName& name); Result GetTimeZoneLocationList(u32& out_count, std::span<Service::PSC::Time::LocationName> out_names, size_t max_names, u32 index); diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 5b28be577..b60fb9139 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -18,9 +18,10 @@ namespace Service::HID { void LoopProcess(Core::System& system) { auto server_manager = std::make_unique<ServerManager>(system); - std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system); std::shared_ptr<HidFirmwareSettings> firmware_settings = std::make_shared<HidFirmwareSettings>(system); + std::shared_ptr<ResourceManager> resource_manager = + std::make_shared<ResourceManager>(system, firmware_settings); // TODO: Remove this hack when am is emulated properly. resource_manager->Initialize(); @@ -31,7 +32,7 @@ void LoopProcess(Core::System& system) { server_manager->RegisterNamedService( "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService( - "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager)); + "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager, firmware_settings)); server_manager->RegisterNamedService( "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); diff --git a/src/core/hle/service/hid/hid_debug_server.cpp b/src/core/hle/service/hid/hid_debug_server.cpp index f2a767d37..4e2663672 100644 --- a/src/core/hle/service/hid/hid_debug_server.cpp +++ b/src/core/hle/service/hid/hid_debug_server.cpp @@ -1,27 +1,37 @@ // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later +#include <algorithm> + +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/hid/hid_debug_server.h" #include "core/hle/service/ipc_helpers.h" +#include "hid_core/hid_types.h" #include "hid_core/resource_manager.h" +#include "hid_core/resources/hid_firmware_settings.h" + +#include "hid_core/resources/touch_screen/gesture.h" +#include "hid_core/resources/touch_screen/touch_screen.h" namespace Service::HID { -IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) - : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} { +IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, + std::shared_ptr<HidFirmwareSettings> settings) + : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource}, firmware_settings{ + settings} { // clang-format off static const FunctionInfo functions[] = { {0, nullptr, "DeactivateDebugPad"}, {1, nullptr, "SetDebugPadAutoPilotState"}, {2, nullptr, "UnsetDebugPadAutoPilotState"}, - {10, nullptr, "DeactivateTouchScreen"}, - {11, nullptr, "SetTouchScreenAutoPilotState"}, - {12, nullptr, "UnsetTouchScreenAutoPilotState"}, - {13, nullptr, "GetTouchScreenConfiguration"}, - {14, nullptr, "ProcessTouchScreenAutoTune"}, - {15, nullptr, "ForceStopTouchScreenManagement"}, - {16, nullptr, "ForceRestartTouchScreenManagement"}, - {17, nullptr, "IsTouchScreenManaged"}, + {10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"}, + {11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"}, + {12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"}, + {13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"}, + {14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"}, + {15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"}, + {16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"}, + {17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"}, {20, nullptr, "DeactivateMouse"}, {21, nullptr, "SetMouseAutoPilotState"}, {22, nullptr, "UnsetMouseAutoPilotState"}, @@ -37,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource {60, nullptr, "ClearNpadSystemCommonPolicy"}, {61, nullptr, "DeactivateNpad"}, {62, nullptr, "ForceDisconnectNpad"}, - {91, nullptr, "DeactivateGesture"}, + {91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"}, {110, nullptr, "DeactivateHomeButton"}, {111, nullptr, "SetHomeButtonAutoPilotState"}, {112, nullptr, "UnsetHomeButtonAutoPilotState"}, @@ -151,6 +161,123 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource IHidDebugServer::~IHidDebugServer() = default; +Result IHidDebugServer::DeactivateTouchScreen() { + LOG_INFO(Service_HID, "called"); + + if (!firmware_settings->IsDeviceManaged()) { + R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate()); + } + + R_SUCCEED(); +} + +Result IHidDebugServer::SetTouchScreenAutoPilotState( + InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) { + AutoPilotState auto_pilot{}; + + auto_pilot.count = + static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size())); + memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(), + auto_pilot.count * sizeof(TouchState)); + + LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count); + + R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot)); +} + +Result IHidDebugServer::UnsetTouchScreenAutoPilotState() { + LOG_INFO(Service_HID, "called"); + R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState()); +} + +Result IHidDebugServer::GetTouchScreenConfiguration( + Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config, + ClientAppletResourceUserId aruid) { + LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid); + + R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration( + *out_touchscreen_config, aruid.pid)); + + if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 && + out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) { + out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; + } + + R_SUCCEED(); +} + +Result IHidDebugServer::ProcessTouchScreenAutoTune() { + LOG_INFO(Service_HID, "called"); + R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune()); +} + +Result IHidDebugServer::ForceStopTouchScreenManagement() { + LOG_INFO(Service_HID, "called"); + + if (!firmware_settings->IsDeviceManaged()) { + R_SUCCEED(); + } + + auto touch_screen = GetResourceManager()->GetTouchScreen(); + auto gesture = GetResourceManager()->GetGesture(); + + if (firmware_settings->IsTouchI2cManaged()) { + bool is_touch_active{}; + bool is_gesture_active{}; + R_TRY(touch_screen->IsActive(is_touch_active)); + R_TRY(gesture->IsActive(is_gesture_active)); + + if (is_touch_active) { + R_TRY(touch_screen->Deactivate()); + } + if (is_gesture_active) { + R_TRY(gesture->Deactivate()); + } + } + + R_SUCCEED(); +} + +Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id, + ClientAppletResourceUserId aruid) { + LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", + basic_gesture_id, aruid.pid); + + auto touch_screen = GetResourceManager()->GetTouchScreen(); + auto gesture = GetResourceManager()->GetGesture(); + + if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) { + R_TRY(gesture->Activate()); + R_TRY(gesture->Activate(aruid.pid, basic_gesture_id)); + R_TRY(touch_screen->Activate()); + R_TRY(touch_screen->Activate(aruid.pid)); + } + + R_SUCCEED(); +} + +Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) { + LOG_INFO(Service_HID, "called"); + + bool is_touch_active{}; + bool is_gesture_active{}; + R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active)); + R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active)); + + *out_is_managed = is_touch_active || is_gesture_active; + R_SUCCEED(); +} + +Result IHidDebugServer::DeactivateGesture() { + LOG_INFO(Service_HID, "called"); + + if (!firmware_settings->IsDeviceManaged()) { + R_RETURN(GetResourceManager()->GetGesture()->Deactivate()); + } + + R_SUCCEED(); +} + std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { resource_manager->Initialize(); return resource_manager; diff --git a/src/core/hle/service/hid/hid_debug_server.h b/src/core/hle/service/hid/hid_debug_server.h index 406db2211..3a483f07e 100644 --- a/src/core/hle/service/hid/hid_debug_server.h +++ b/src/core/hle/service/hid/hid_debug_server.h @@ -3,7 +3,9 @@ #pragma once +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" +#include "hid_core/resources/touch_screen/touch_types.h" namespace Core { class System; @@ -11,16 +13,33 @@ class System; namespace Service::HID { class ResourceManager; +class HidFirmwareSettings; class IHidDebugServer final : public ServiceFramework<IHidDebugServer> { public: - explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); + explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, + std::shared_ptr<HidFirmwareSettings> settings); ~IHidDebugServer() override; private: + Result DeactivateTouchScreen(); + Result SetTouchScreenAutoPilotState( + InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer); + Result UnsetTouchScreenAutoPilotState(); + Result GetTouchScreenConfiguration( + Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config, + ClientAppletResourceUserId aruid); + Result ProcessTouchScreenAutoTune(); + Result ForceStopTouchScreenManagement(); + Result ForceRestartTouchScreenManagement(u32 basic_gesture_id, + ClientAppletResourceUserId aruid); + Result IsTouchScreenManaged(Out<bool> out_is_managed); + Result DeactivateGesture(); + std::shared_ptr<ResourceManager> GetResourceManager(); std::shared_ptr<ResourceManager> resource_manager; + std::shared_ptr<HidFirmwareSettings> firmware_settings; }; } // namespace Service::HID diff --git a/src/core/hle/service/hid/hid_server.cpp b/src/core/hle/service/hid/hid_server.cpp index 938b93451..3603d8ccf 100644 --- a/src/core/hle/service/hid/hid_server.cpp +++ b/src/core/hle/service/hid/hid_server.cpp @@ -990,8 +990,7 @@ void IHidServer::ActivateGesture(HLERequestContext& ctx) { } if (result.IsSuccess()) { - // TODO: Use gesture id here - result = gesture->Activate(parameters.applet_resource_user_id); + result = gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id); } IPC::ResponseBuilder rb{ctx, 2}; @@ -2470,14 +2469,22 @@ void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) { void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; - const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; + auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", - touchscreen_mode.mode, applet_resource_user_id); + LOG_INFO(Service_HID, "called, touchscreen_config={}, applet_resource_user_id={}", + touchscreen_config.mode, applet_resource_user_id); + + if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && + touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { + touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; + } + + const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenConfiguration( + touchscreen_config, applet_resource_user_id); IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + rb.Push(result); } void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { @@ -2505,11 +2512,12 @@ void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) { const auto height{rp.Pop<u32>()}; const auto applet_resource_user_id{rp.Pop<u64>()}; - GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height); - LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height, applet_resource_user_id); + GetResourceManager()->GetTouchScreen()->SetTouchScreenResolution(width, height, + applet_resource_user_id); + IPC::ResponseBuilder rb{ctx, 2}; rb.Push(ResultSuccess); } diff --git a/src/core/hle/service/hid/hid_system_server.cpp b/src/core/hle/service/hid/hid_system_server.cpp index d1ec42edc..22471e9e2 100644 --- a/src/core/hle/service/hid/hid_system_server.cpp +++ b/src/core/hle/service/hid/hid_system_server.cpp @@ -155,9 +155,9 @@ IHidSystemServer::IHidSystemServer(Core::System& system_, std::shared_ptr<Resour {1133, nullptr, "StartUsbFirmwareUpdate"}, {1134, nullptr, "GetUsbFirmwareUpdateState"}, {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, - {1150, nullptr, "SetTouchScreenMagnification"}, - {1151, nullptr, "GetTouchScreenFirmwareVersion"}, - {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, + {1150, &IHidSystemServer::SetTouchScreenMagnification, "SetTouchScreenMagnification"}, + {1151, &IHidSystemServer::GetTouchScreenFirmwareVersion, "GetTouchScreenFirmwareVersion"}, + {1152, &IHidSystemServer::SetTouchScreenDefaultConfiguration, "SetTouchScreenDefaultConfiguration"}, {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, {1154, nullptr, "IsFirmwareAvailableForNotification"}, {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"}, @@ -845,12 +845,60 @@ void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContex rb.Push(ResultSuccess); } +void IHidSystemServer::SetTouchScreenMagnification(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto point1x{rp.Pop<f32>()}; + const auto point1y{rp.Pop<f32>()}; + const auto point2x{rp.Pop<f32>()}; + const auto point2y{rp.Pop<f32>()}; + + LOG_INFO(Service_HID, "called, point1=-({},{}), point2=({},{})", point1x, point1y, point2x, + point2y); + + const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenMagnification( + point1x, point1y, point2x, point2y); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + +void IHidSystemServer::GetTouchScreenFirmwareVersion(HLERequestContext& ctx) { + LOG_INFO(Service_HID, "called"); + + Core::HID::FirmwareVersion firmware{}; + const auto result = GetResourceManager()->GetTouchScreenFirmwareVersion(firmware); + + IPC::ResponseBuilder rb{ctx, 6}; + rb.Push(result); + rb.PushRaw(firmware); +} + +void IHidSystemServer::SetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; + + LOG_INFO(Service_HID, "called, touchscreen_config={}", touchscreen_config.mode); + + if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && + touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { + touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; + } + + const Result result = + GetResourceManager()->GetTouchScreen()->SetTouchScreenDefaultConfiguration( + touchscreen_config); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(result); +} + void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { - LOG_WARNING(Service_HID, "(STUBBED) called"); + LOG_INFO(Service_HID, "called"); - Core::HID::TouchScreenConfigurationForNx touchscreen_config{ - .mode = Core::HID::TouchScreenModeForNx::Finger, - }; + Core::HID::TouchScreenConfigurationForNx touchscreen_config{}; + const Result result = + GetResourceManager()->GetTouchScreen()->GetTouchScreenDefaultConfiguration( + touchscreen_config); if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { @@ -858,7 +906,7 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx } IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); + rb.Push(result); rb.PushRaw(touchscreen_config); } diff --git a/src/core/hle/service/hid/hid_system_server.h b/src/core/hle/service/hid/hid_system_server.h index 4ab4d3931..738313e08 100644 --- a/src/core/hle/service/hid/hid_system_server.h +++ b/src/core/hle/service/hid/hid_system_server.h @@ -71,6 +71,9 @@ private: void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx); void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); + void SetTouchScreenMagnification(HLERequestContext& ctx); + void GetTouchScreenFirmwareVersion(HLERequestContext& ctx); + void SetTouchScreenDefaultConfiguration(HLERequestContext& ctx); void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); void SetForceHandheldStyleVibration(HLERequestContext& ctx); void IsUsingCustomButtonConfig(HLERequestContext& ctx); diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp index 18e544f2f..7d7368ff9 100644 --- a/src/core/hle/service/hid/irs.cpp +++ b/src/core/hle/service/hid/irs.cpp @@ -9,6 +9,7 @@ #include "core/hle/kernel/k_shared_memory.h" #include "core/hle/kernel/k_transfer_memory.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/service/cmif_serialization.h" #include "core/hle/service/hid/irs.h" #include "core/hle/service/ipc_helpers.h" #include "core/memory.h" @@ -28,24 +29,24 @@ namespace Service::IRS { IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { // clang-format off static const FunctionInfo functions[] = { - {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, - {303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"}, - {304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"}, - {305, &IRS::StopImageProcessor, "StopImageProcessor"}, - {306, &IRS::RunMomentProcessor, "RunMomentProcessor"}, - {307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"}, - {308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"}, - {309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"}, - {310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"}, - {311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"}, - {312, &IRS::RunPointingProcessor, "RunPointingProcessor"}, - {313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"}, - {314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"}, - {315, &IRS::SetFunctionLevel, "SetFunctionLevel"}, - {316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"}, - {317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"}, - {318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"}, - {319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"}, + {302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"}, + {303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"}, + {304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"}, + {305, C<&IRS::StopImageProcessor>, "StopImageProcessor"}, + {306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"}, + {307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"}, + {308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"}, + {309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"}, + {310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"}, + {311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"}, + {312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"}, + {313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"}, + {314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"}, + {315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"}, + {316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"}, + {317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"}, + {318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"}, + {319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"}, }; // clang-format on @@ -57,489 +58,292 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { } IRS::~IRS() = default; -void IRS::ActivateIrsensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); +Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) { + LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); + R_SUCCEED(); } -void IRS::DeactivateIrsensor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", - applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); +Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) { + LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); + R_SUCCEED(); } -void IRS::GetIrsensorSharedMemoryHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - - LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); +Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory, + ClientAppletResourceUserId aruid) { + LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid); - IPC::ResponseBuilder rb{ctx, 2, 1}; - rb.Push(ResultSuccess); - rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem()); + *out_shared_memory = &system.Kernel().GetIrsSharedMem(); + R_SUCCEED(); } -void IRS::StopImageProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - +Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, + ClientAppletResourceUserId aruid) { LOG_WARNING(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.applet_resource_user_id); - - auto result = IsIrCameraHandleValid(parameters.camera_handle); - if (result.IsSuccess()) { - // TODO: Stop Image processor - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active); - result = ResultSuccess; - } + camera_handle.npad_type, camera_handle.npad_id, aruid.pid); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} - -void IRS::RunMomentProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Core::IrSensor::PackedMomentProcessorConfig processor_config; - }; - static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); + R_TRY(IsIrCameraHandleValid(camera_handle)); - const auto parameters{rp.PopRaw<Parameters>()}; + // TODO: Stop Image processor + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::Active); + R_SUCCEED(); +} +Result IRS::RunMomentProcessor( + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + const Core::IrSensor::PackedMomentProcessorConfig& processor_config) { LOG_WARNING(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.applet_resource_user_id); - - const auto result = IsIrCameraHandleValid(parameters.camera_handle); - - if (result.IsSuccess()) { - auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); - MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device); - auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); - image_transfer_processor.SetConfig(parameters.processor_config); - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::IR); - } + camera_handle.npad_type, camera_handle.npad_id, aruid.pid); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} + R_TRY(IsIrCameraHandleValid(camera_handle)); -void IRS::RunClusteringProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Core::IrSensor::PackedClusteringProcessorConfig processor_config; - }; - static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::IR); - const auto parameters{rp.PopRaw<Parameters>()}; + R_SUCCEED(); +} +Result IRS::RunClusteringProcessor( + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) { LOG_WARNING(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.applet_resource_user_id); - - auto result = IsIrCameraHandleValid(parameters.camera_handle); - - if (result.IsSuccess()) { - auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); - MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device); - auto& image_transfer_processor = - GetProcessor<ClusteringProcessor>(parameters.camera_handle); - image_transfer_processor.SetConfig(parameters.processor_config); - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::IR); - } + camera_handle.npad_type, camera_handle.npad_id, aruid.pid); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} + R_TRY(IsIrCameraHandleValid(camera_handle)); -void IRS::RunImageTransferProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Core::IrSensor::PackedImageTransferProcessorConfig processor_config; - u32 transfer_memory_size; - }; - static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::IR); - const auto parameters{rp.PopRaw<Parameters>()}; - const auto t_mem_handle{ctx.GetCopyHandle(0)}; + R_SUCCEED(); +} - auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle); +Result IRS::RunImageTransferProcessor( + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config, + u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) { - if (t_mem.IsNull()) { - LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultUnknown); - return; - } - - ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size"); + ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size"); LOG_INFO(Service_IRS, "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " "applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); - - const auto result = IsIrCameraHandleValid(parameters.camera_handle); - - if (result.IsSuccess()) { - auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); - MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); - auto& image_transfer_processor = - GetProcessor<ImageTransferProcessor>(parameters.camera_handle); - image_transfer_processor.SetConfig(parameters.processor_config); - image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::IR); - } + camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(), + aruid.pid); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} + R_TRY(IsIrCameraHandleValid(camera_handle)); -void IRS::GetImageTransferProcessorState(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::IR); - const auto parameters{rp.PopRaw<Parameters>()}; + R_SUCCEED(); +} +Result IRS::GetImageTransferProcessorState( + Out<Core::IrSensor::ImageTransferProcessorState> out_state, + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) { LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.applet_resource_user_id); - - const auto result = IsIrCameraHandleValid(parameters.camera_handle); - if (result.IsError()) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); - return; - } + camera_handle.npad_type, camera_handle.npad_id, aruid.pid); - const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); + R_TRY(IsIrCameraHandleValid(camera_handle)); - if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(InvalidProcessorState); - return; - } + const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + + R_TRY(IsIrCameraHandleValid(camera_handle)); + R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor, + InvalidProcessorState); - std::vector<u8> data{}; - const auto& image_transfer_processor = - GetProcessor<ImageTransferProcessor>(parameters.camera_handle); - const auto& state = image_transfer_processor.GetState(data); + *out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data); - ctx.WriteBuffer(data); - IPC::ResponseBuilder rb{ctx, 6}; - rb.Push(ResultSuccess); - rb.PushRaw(state); + R_SUCCEED(); } -void IRS::RunTeraPluginProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); +Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle, + Core::IrSensor::PackedTeraPluginProcessorConfig processor_config, + ClientAppletResourceUserId aruid) { + LOG_WARNING(Service_IRS, + "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " + "applet_resource_user_id={}", + camera_handle.npad_type, camera_handle.npad_id, processor_config.mode, + processor_config.required_mcu_version.major, + processor_config.required_mcu_version.minor, aruid.pid); - const auto parameters{rp.PopRaw<Parameters>()}; + R_TRY(IsIrCameraHandleValid(camera_handle)); - LOG_WARNING( - Service_IRS, - "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " - "applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, - parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id); - - const auto result = IsIrCameraHandleValid(parameters.camera_handle); - - if (result.IsSuccess()) { - auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); - MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device); - auto& image_transfer_processor = - GetProcessor<TeraPluginProcessor>(parameters.camera_handle); - image_transfer_processor.SetConfig(parameters.processor_config); - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::IR); - } + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<TeraPluginProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::IR); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_SUCCEED(); } -void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; - - if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && - npad_id != Core::HID::NpadIdType::Handheld) { - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(Service::HID::ResultInvalidNpadId); - return; - } +Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle, + Core::HID::NpadIdType npad_id) { + R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId); - Core::IrSensor::IrCameraHandle camera_handle{ + *out_camera_handle = { .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), .npad_type = Core::HID::NpadStyleIndex::None, }; LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, - camera_handle.npad_id, camera_handle.npad_type); + out_camera_handle->npad_id, out_camera_handle->npad_type); - IPC::ResponseBuilder rb{ctx, 3}; - rb.Push(ResultSuccess); - rb.PushRaw(camera_handle); + R_SUCCEED(); } -void IRS::RunPointingProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; - const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; - +Result IRS::RunPointingProcessor( + Core::IrSensor::IrCameraHandle camera_handle, + const Core::IrSensor::PackedPointingProcessorConfig& processor_config, + ClientAppletResourceUserId aruid) { LOG_WARNING( Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, - processor_config.required_mcu_version.minor, applet_resource_user_id); + processor_config.required_mcu_version.minor, aruid.pid); - auto result = IsIrCameraHandleValid(camera_handle); + R_TRY(IsIrCameraHandleValid(camera_handle)); - if (result.IsSuccess()) { - auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); - MakeProcessor<PointingProcessor>(camera_handle, device); - auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); - image_transfer_processor.SetConfig(processor_config); - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::IR); - } + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<PointingProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::IR); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_SUCCEED(); } -void IRS::SuspendImageProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - +Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, + ClientAppletResourceUserId aruid) { LOG_WARNING(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.applet_resource_user_id); + camera_handle.npad_type, camera_handle.npad_id, aruid.pid); - auto result = IsIrCameraHandleValid(parameters.camera_handle); - if (result.IsSuccess()) { - // TODO: Suspend image processor - result = ResultSuccess; - } + R_TRY(IsIrCameraHandleValid(camera_handle)); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} + // TODO: Suspend image processor -void IRS::CheckFirmwareVersion(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; - const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + R_SUCCEED(); +} +Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle, + Core::IrSensor::PackedMcuVersion mcu_version, + ClientAppletResourceUserId aruid) { LOG_WARNING( Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", - camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, + camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major, mcu_version.minor); - auto result = IsIrCameraHandleValid(camera_handle); - if (result.IsSuccess()) { - // TODO: Check firmware version - result = ResultSuccess; - } + R_TRY(IsIrCameraHandleValid(camera_handle)); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} + // TODO: Check firmware version -void IRS::SetFunctionLevel(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; - const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + R_SUCCEED(); +} +Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle, + Core::IrSensor::PackedFunctionLevel function_level, + ClientAppletResourceUserId aruid) { LOG_WARNING( Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", - camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, - applet_resource_user_id); + camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid); - auto result = IsIrCameraHandleValid(camera_handle); - if (result.IsSuccess()) { - // TODO: Set Function level - result = ResultSuccess; - } + R_TRY(IsIrCameraHandleValid(camera_handle)); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} + // TODO: Set Function level -void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - Core::IrSensor::PackedImageTransferProcessorExConfig processor_config; - u64 transfer_memory_size; - }; - static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); + R_SUCCEED(); +} - const auto parameters{rp.PopRaw<Parameters>()}; - const auto t_mem_handle{ctx.GetCopyHandle(0)}; +Result IRS::RunImageTransferExProcessor( + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config, + u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) { - auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle); + ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size"); LOG_INFO(Service_IRS, "called, npad_type={}, npad_id={}, transfer_memory_size={}, " "applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.transfer_memory_size, parameters.applet_resource_user_id); - - auto result = IsIrCameraHandleValid(parameters.camera_handle); - - if (result.IsSuccess()) { - auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); - MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); - auto& image_transfer_processor = - GetProcessor<ImageTransferProcessor>(parameters.camera_handle); - image_transfer_processor.SetConfig(parameters.processor_config); - image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::IR); - } + camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} + R_TRY(IsIrCameraHandleValid(camera_handle)); + + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::IR); -void IRS::RunIrLedProcessor(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; - const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()}; - const auto applet_resource_user_id{rp.Pop<u64>()}; + R_SUCCEED(); +} +Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle, + Core::IrSensor::PackedIrLedProcessorConfig processor_config, + ClientAppletResourceUserId aruid) { LOG_WARNING(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " "applet_resource_user_id={}", camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, processor_config.required_mcu_version.major, - processor_config.required_mcu_version.minor, applet_resource_user_id); + processor_config.required_mcu_version.minor, aruid.pid); - auto result = IsIrCameraHandleValid(camera_handle); + R_TRY(IsIrCameraHandleValid(camera_handle)); - if (result.IsSuccess()) { - auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); - MakeProcessor<IrLedProcessor>(camera_handle, device); - auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); - image_transfer_processor.SetConfig(processor_config); - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::IR); - } + auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); + MakeProcessor<IrLedProcessor>(camera_handle, device); + auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); + image_transfer_processor.SetConfig(processor_config); + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::IR); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); + R_SUCCEED(); } -void IRS::StopImageProcessorAsync(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::IrCameraHandle camera_handle; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); - - const auto parameters{rp.PopRaw<Parameters>()}; - +Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle, + ClientAppletResourceUserId aruid) { LOG_WARNING(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", - parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, - parameters.applet_resource_user_id); - - auto result = IsIrCameraHandleValid(parameters.camera_handle); - if (result.IsSuccess()) { - // TODO: Stop image processor async - npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, - Common::Input::PollingMode::Active); - result = ResultSuccess; - } + camera_handle.npad_type, camera_handle.npad_id, aruid.pid); - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(result); -} + R_TRY(IsIrCameraHandleValid(camera_handle)); -void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) { - IPC::RequestParser rp{ctx}; - struct Parameters { - Core::IrSensor::PackedFunctionLevel function_level; - INSERT_PADDING_WORDS_NOINIT(1); - u64 applet_resource_user_id; - }; - static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); + // TODO: Stop image processor async + npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, + Common::Input::PollingMode::Active); - const auto parameters{rp.PopRaw<Parameters>()}; + R_SUCCEED(); +} +Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level, + ClientAppletResourceUserId aruid) { LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", - parameters.function_level.function_level, parameters.applet_resource_user_id); - - IPC::ResponseBuilder rb{ctx, 2}; - rb.Push(ResultSuccess); + function_level.function_level, aruid.pid); + R_SUCCEED(); } Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { diff --git a/src/core/hle/service/hid/irs.h b/src/core/hle/service/hid/irs.h index 06b7279ee..58dfee6c3 100644 --- a/src/core/hle/service/hid/irs.h +++ b/src/core/hle/service/hid/irs.h @@ -4,6 +4,7 @@ #pragma once #include "core/core.h" +#include "core/hle/service/cmif_types.h" #include "core/hle/service/service.h" #include "hid_core/hid_types.h" #include "hid_core/irsensor/irs_types.h" @@ -35,26 +36,73 @@ private: }; static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); - void ActivateIrsensor(HLERequestContext& ctx); - void DeactivateIrsensor(HLERequestContext& ctx); - void GetIrsensorSharedMemoryHandle(HLERequestContext& ctx); - void StopImageProcessor(HLERequestContext& ctx); - void RunMomentProcessor(HLERequestContext& ctx); - void RunClusteringProcessor(HLERequestContext& ctx); - void RunImageTransferProcessor(HLERequestContext& ctx); - void GetImageTransferProcessorState(HLERequestContext& ctx); - void RunTeraPluginProcessor(HLERequestContext& ctx); - void GetNpadIrCameraHandle(HLERequestContext& ctx); - void RunPointingProcessor(HLERequestContext& ctx); - void SuspendImageProcessor(HLERequestContext& ctx); - void CheckFirmwareVersion(HLERequestContext& ctx); - void SetFunctionLevel(HLERequestContext& ctx); - void RunImageTransferExProcessor(HLERequestContext& ctx); - void RunIrLedProcessor(HLERequestContext& ctx); - void StopImageProcessorAsync(HLERequestContext& ctx); - void ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx); + Result ActivateIrsensor(ClientAppletResourceUserId aruid); + + Result DeactivateIrsensor(ClientAppletResourceUserId aruid); + + Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory, + ClientAppletResourceUserId aruid); + Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, + ClientAppletResourceUserId aruid); + + Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle, + ClientAppletResourceUserId aruid, + const Core::IrSensor::PackedMomentProcessorConfig& processor_config); + + Result RunClusteringProcessor( + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + const Core::IrSensor::PackedClusteringProcessorConfig& processor_config); + + Result RunImageTransferProcessor( + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config, + u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem); + + Result GetImageTransferProcessorState( + Out<Core::IrSensor::ImageTransferProcessorState> out_state, + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data); + + Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle, + Core::IrSensor::PackedTeraPluginProcessorConfig processor_config, + ClientAppletResourceUserId aruid); + + Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle, + Core::HID::NpadIdType npad_id); + + Result RunPointingProcessor( + Core::IrSensor::IrCameraHandle camera_handle, + const Core::IrSensor::PackedPointingProcessorConfig& processor_config, + ClientAppletResourceUserId aruid); + + Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, + ClientAppletResourceUserId aruid); + + Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle, + Core::IrSensor::PackedMcuVersion mcu_version, + ClientAppletResourceUserId aruid); + + Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle, + Core::IrSensor::PackedFunctionLevel function_level, + ClientAppletResourceUserId aruid); + + Result RunImageTransferExProcessor( + Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, + const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config, + u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem); + + Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle, + Core::IrSensor::PackedIrLedProcessorConfig processor_config, + ClientAppletResourceUserId aruid); + + Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle, + ClientAppletResourceUserId aruid); + + Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level, + ClientAppletResourceUserId aruid); Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; + Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( const Core::IrSensor::IrCameraHandle& camera_handle); diff --git a/src/core/hle/service/jit/jit.cpp b/src/core/hle/service/jit/jit.cpp index 1f2cbcb61..4941a71a0 100644 --- a/src/core/hle/service/jit/jit.cpp +++ b/src/core/hle/service/jit/jit.cpp @@ -126,7 +126,7 @@ public: R_THROW(ResultUnknown); } - Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem, + Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory> tmem, InBuffer<BufferAttr_HipcMapAlias> nrr, InBuffer<BufferAttr_HipcMapAlias> nro) { if (!tmem) { @@ -268,9 +268,9 @@ public: private: Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment, - u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process, - InCopyHandle<Kernel::KCodeMemory>& rx_mem, - InCopyHandle<Kernel::KCodeMemory>& ro_mem) { + u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess> process, + InCopyHandle<Kernel::KCodeMemory> rx_mem, + InCopyHandle<Kernel::KCodeMemory> ro_mem) { if (!process) { LOG_ERROR(Service_JIT, "process is null"); R_THROW(ResultUnknown); diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp index efb7f6e32..0086f82c5 100644 --- a/src/core/hle/service/mii/mii.cpp +++ b/src/core/hle/service/mii/mii.cpp @@ -189,7 +189,7 @@ private: R_RETURN(manager->Move(metadata, new_index, create_id)); } - Result AddOrReplace(StoreData& store_data) { + Result AddOrReplace(const StoreData& store_data) { LOG_INFO(Service_Mii, "called"); R_UNLESS(is_system, ResultPermissionDenied); diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index c1ebbd62d..abe95303e 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include <boost/container/small_vector.hpp> + #include "common/assert.h" #include "common/logging/log.h" #include "core/core.h" @@ -38,19 +40,30 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} void nvdisp_disp0::OnClose(DeviceFD fd) {} -void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, - u32 height, u32 stride, android::BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect, - std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { - const DAddr addr = nvmap.GetHandleAddress(buffer_handle); - LOG_TRACE(Service, - "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", - addr, offset, width, height, stride, format); +void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers) { + std::vector<Tegra::FramebufferConfig> output_layers; + std::vector<Service::Nvidia::NvFence> output_fences; + output_layers.reserve(sorted_layers.size()); + output_fences.reserve(sorted_layers.size()); + + for (auto& layer : sorted_layers) { + output_layers.emplace_back(Tegra::FramebufferConfig{ + .address = nvmap.GetHandleAddress(layer.buffer_handle), + .offset = layer.offset, + .width = layer.width, + .height = layer.height, + .stride = layer.stride, + .pixel_format = layer.format, + .transform_flags = layer.transform, + .crop_rect = layer.crop_rect, + }); - const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, - stride, format, transform, crop_rect}; + for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { + output_fences.push_back(layer.acquire_fence.fences[i]); + } + } - system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); + system.GPU().RequestComposite(std::move(output_layers), std::move(output_fences)); system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); system.GetPerfStats().EndSystemFrame(); system.GetPerfStats().BeginSystemFrame(); diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 5f13a50a2..1082b85c2 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -8,8 +8,7 @@ #include "common/common_types.h" #include "common/math_util.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" -#include "core/hle/service/nvnflinger/buffer_transform_flags.h" -#include "core/hle/service/nvnflinger/pixel_format.h" +#include "core/hle/service/nvnflinger/hwc_layer.h" namespace Service::Nvidia::NvCore { class Container; @@ -35,11 +34,8 @@ public: void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; void OnClose(DeviceFD fd) override; - /// Performs a screen flip, drawing the buffer pointed to by the handle. - void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, - u32 stride, android::BufferTransformFlags transform, - const Common::Rectangle<int>& crop_rect, - std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences); + /// Performs a screen flip, compositing each buffer. + void Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers); Kernel::KEvent* QueryEvent(u32 event_id) override; diff --git a/src/core/hle/service/nvnflinger/hardware_composer.cpp b/src/core/hle/service/nvnflinger/hardware_composer.cpp new file mode 100644 index 000000000..ba2b5c28c --- /dev/null +++ b/src/core/hle/service/nvnflinger/hardware_composer.cpp @@ -0,0 +1,215 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <boost/container/small_vector.hpp> + +#include "common/microprofile.h" +#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" +#include "core/hle/service/nvnflinger/buffer_item.h" +#include "core/hle/service/nvnflinger/buffer_item_consumer.h" +#include "core/hle/service/nvnflinger/hardware_composer.h" +#include "core/hle/service/nvnflinger/hwc_layer.h" +#include "core/hle/service/nvnflinger/ui/graphic_buffer.h" +#include "core/hle/service/vi/display/vi_display.h" +#include "core/hle/service/vi/layer/vi_layer.h" + +namespace Service::Nvnflinger { + +namespace { + +s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) { + if (swap_interval <= 0) { + // As an extension, treat nonpositive swap interval as speed multiplier. + if (out_speed_scale) { + *out_speed_scale = 2.f * static_cast<f32>(1 - swap_interval); + } + + swap_interval = 1; + } + + if (swap_interval >= 5) { + // As an extension, treat high swap interval as precise speed control. + if (out_speed_scale) { + *out_speed_scale = static_cast<f32>(swap_interval) / 100.f; + } + + swap_interval = 1; + } + + return swap_interval; +} + +} // namespace + +HardwareComposer::HardwareComposer() = default; +HardwareComposer::~HardwareComposer() = default; + +u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, + Nvidia::Devices::nvdisp_disp0& nvdisp) { + boost::container::small_vector<HwcLayer, 2> composition_stack; + + // Set default speed limit to 100%. + *out_speed_scale = 1.0f; + + // Determine the number of vsync periods to wait before composing again. + std::optional<s32> swap_interval{}; + bool has_acquired_buffer{}; + + // Acquire all necessary framebuffers. + for (size_t i = 0; i < display.GetNumLayers(); i++) { + auto& layer = display.GetLayer(i); + auto layer_id = layer.GetLayerId(); + + // Try to fetch the framebuffer (either new or stale). + const auto result = this->CacheFramebufferLocked(layer, layer_id); + + // If we failed, skip this layer. + if (result == CacheStatus::NoBufferAvailable) { + continue; + } + + // If we acquired a new buffer, we need to present. + if (result == CacheStatus::BufferAcquired) { + has_acquired_buffer = true; + } + + const auto& buffer = m_framebuffers[layer_id]; + const auto& item = buffer.item; + const auto& igbp_buffer = *item.graphic_buffer; + + // TODO: get proper Z-index from layer + composition_stack.emplace_back(HwcLayer{ + .buffer_handle = igbp_buffer.BufferId(), + .offset = igbp_buffer.Offset(), + .format = igbp_buffer.ExternalFormat(), + .width = igbp_buffer.Width(), + .height = igbp_buffer.Height(), + .stride = igbp_buffer.Stride(), + .z_index = 0, + .transform = static_cast<android::BufferTransformFlags>(item.transform), + .crop_rect = item.crop, + .acquire_fence = item.fence, + }); + + // We need to compose again either before this frame is supposed to + // be released, or exactly on the vsync period it should be released. + const s32 item_swap_interval = NormalizeSwapInterval(out_speed_scale, item.swap_interval); + + // TODO: handle cases where swap intervals are relatively prime. So far, + // only swap intervals of 0, 1 and 2 have been observed, but if 3 were + // to be introduced, this would cause an issue. + if (swap_interval) { + swap_interval = std::min(*swap_interval, item_swap_interval); + } else { + swap_interval = item_swap_interval; + } + } + + // If any new buffers were acquired, we can present. + if (has_acquired_buffer) { + // Sort by Z-index. + std::stable_sort(composition_stack.begin(), composition_stack.end(), + [&](auto& l, auto& r) { return l.z_index < r.z_index; }); + + // Composite. + nvdisp.Composite(composition_stack); + } + + // Render MicroProfile. + MicroProfileFlip(); + + // Advance by at least one frame. + const u32 frame_advance = swap_interval.value_or(1); + m_frame_number += frame_advance; + + // Release any necessary framebuffers. + for (auto& [layer_id, framebuffer] : m_framebuffers) { + if (framebuffer.release_frame_number > m_frame_number) { + // Not yet ready to release this framebuffer. + continue; + } + + if (!framebuffer.is_acquired) { + // Already released. + continue; + } + + if (auto* layer = display.FindLayer(layer_id); layer != nullptr) { + // TODO: support release fence + // This is needed to prevent screen tearing + layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); + framebuffer.is_acquired = false; + } + } + + return frame_advance; +} + +void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) { + // Check if we are tracking a slot with this layer_id. + const auto it = m_framebuffers.find(layer_id); + if (it == m_framebuffers.end()) { + return; + } + + // Try to release the buffer item. + auto* const layer = display.FindLayer(layer_id); + if (layer && it->second.is_acquired) { + layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence()); + } + + // Erase the slot. + m_framebuffers.erase(it); +} + +bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) { + // Attempt the update. + const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false); + if (status != android::Status::NoError) { + return false; + } + + // We succeeded, so set the new release frame info. + framebuffer.release_frame_number = + NormalizeSwapInterval(nullptr, framebuffer.item.swap_interval); + framebuffer.is_acquired = true; + + return true; +} + +HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer, + LayerId layer_id) { + // Check if this framebuffer is already present. + const auto it = m_framebuffers.find(layer_id); + if (it != m_framebuffers.end()) { + // If it's currently still acquired, we are done. + if (it->second.is_acquired) { + return CacheStatus::CachedBufferReused; + } + + // Try to acquire a new item. + if (this->TryAcquireFramebufferLocked(layer, it->second)) { + // We got a new item. + return CacheStatus::BufferAcquired; + } else { + // We didn't acquire a new item, but we can reuse the slot. + return CacheStatus::CachedBufferReused; + } + } + + // Framebuffer is not present, so try to create it. + Framebuffer framebuffer{}; + + if (this->TryAcquireFramebufferLocked(layer, framebuffer)) { + // Move the buffer item into a new slot. + m_framebuffers.emplace(layer_id, std::move(framebuffer)); + + // We succeeded. + return CacheStatus::BufferAcquired; + } + + // We couldn't acquire the buffer item, so don't create a slot. + return CacheStatus::NoBufferAvailable; +} + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/hardware_composer.h b/src/core/hle/service/nvnflinger/hardware_composer.h new file mode 100644 index 000000000..28392c512 --- /dev/null +++ b/src/core/hle/service/nvnflinger/hardware_composer.h @@ -0,0 +1,59 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <memory> +#include <boost/container/flat_map.hpp> + +#include "core/hle/service/nvnflinger/buffer_item.h" + +namespace Service::Nvidia::Devices { +class nvdisp_disp0; +} + +namespace Service::VI { +class Display; +class Layer; +} // namespace Service::VI + +namespace Service::Nvnflinger { + +using LayerId = u64; + +class HardwareComposer { +public: + explicit HardwareComposer(); + ~HardwareComposer(); + + u32 ComposeLocked(f32* out_speed_scale, VI::Display& display, + Nvidia::Devices::nvdisp_disp0& nvdisp); + void RemoveLayerLocked(VI::Display& display, LayerId layer_id); + +private: + // TODO: do we want to track frame number in vi instead? + u64 m_frame_number{0}; + +private: + using ReleaseFrameNumber = u64; + + struct Framebuffer { + android::BufferItem item{}; + ReleaseFrameNumber release_frame_number{}; + bool is_acquired{false}; + }; + + enum class CacheStatus : u32 { + NoBufferAvailable, + BufferAcquired, + CachedBufferReused, + }; + + boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{}; + +private: + bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer); + CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id); +}; + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/hwc_layer.h b/src/core/hle/service/nvnflinger/hwc_layer.h new file mode 100644 index 000000000..3af668a25 --- /dev/null +++ b/src/core/hle/service/nvnflinger/hwc_layer.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/math_util.h" +#include "core/hle/service/nvdrv/nvdata.h" +#include "core/hle/service/nvnflinger/buffer_transform_flags.h" +#include "core/hle/service/nvnflinger/pixel_format.h" +#include "core/hle/service/nvnflinger/ui/fence.h" + +namespace Service::Nvnflinger { + +struct HwcLayer { + u32 buffer_handle; + u32 offset; + android::PixelFormat format; + u32 width; + u32 height; + u32 stride; + s32 z_index; + android::BufferTransformFlags transform; + Common::Rectangle<int> crop_rect; + android::Fence acquire_fence; +}; + +} // namespace Service::Nvnflinger diff --git a/src/core/hle/service/nvnflinger/nvnflinger.cpp b/src/core/hle/service/nvnflinger/nvnflinger.cpp index 51133853c..d8ba89d43 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.cpp +++ b/src/core/hle/service/nvnflinger/nvnflinger.cpp @@ -18,6 +18,7 @@ #include "core/hle/service/nvnflinger/buffer_item_consumer.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h" #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" +#include "core/hle/service/nvnflinger/hardware_composer.h" #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" #include "core/hle/service/nvnflinger/nvnflinger.h" #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" @@ -279,45 +280,18 @@ void Nvnflinger::Compose() { SCOPE_EXIT({ display.SignalVSyncEvent(); }); // Don't do anything for displays without layers. - if (!display.HasLayers()) - continue; - - // TODO(Subv): Support more than 1 layer. - VI::Layer& layer = display.GetLayer(0); - - android::BufferItem buffer{}; - const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); - - if (status != android::Status::NoError) { + if (!display.HasLayers()) { continue; } - const auto& igbp_buffer = *buffer.graphic_buffer; - if (!system.IsPoweredOn()) { return; // We are likely shutting down } - // Now send the buffer to the GPU for drawing. - // TODO(Subv): Support more than just disp0. The display device selection is probably based - // on which display we're drawing (Default, Internal, External, etc) auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); ASSERT(nvdisp); - Common::Rectangle<int> crop_rect{ - static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), - static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; - - nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), - igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), - static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect, - buffer.fence.fences, buffer.fence.num_fences); - - MicroProfileFlip(); - - swap_interval = buffer.swap_interval; - - layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence()); + swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp); } } @@ -334,15 +308,16 @@ s64 Nvnflinger::GetNextTicks() const { speed_scale = 0.01f; } } + + // Adjust by speed limit determined during composition. + speed_scale /= compose_speed_scale; + if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { // Run at intended presentation rate during video playback. speed_scale = 1.f; } - // As an extension, treat nonpositive swap interval as framerate multiplier. - const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval) - : 60.f / static_cast<f32>(swap_interval); - + const f32 effective_fps = 60.f / static_cast<f32>(swap_interval); return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); } diff --git a/src/core/hle/service/nvnflinger/nvnflinger.h b/src/core/hle/service/nvnflinger/nvnflinger.h index 369439142..c984d55a0 100644 --- a/src/core/hle/service/nvnflinger/nvnflinger.h +++ b/src/core/hle/service/nvnflinger/nvnflinger.h @@ -46,6 +46,7 @@ class BufferQueueProducer; namespace Service::Nvnflinger { class FbShareBufferManager; +class HardwareComposer; class HosBinderDriverServer; class Nvnflinger final { @@ -143,6 +144,7 @@ private: u32 next_buffer_queue_id = 1; s32 swap_interval = 1; + f32 compose_speed_scale = 1.0f; bool is_abandoned = false; diff --git a/src/core/hle/service/psc/time/clocks/context_writers.cpp b/src/core/hle/service/psc/time/clocks/context_writers.cpp index ac8700f76..a44486b43 100644 --- a/src/core/hle/service/psc/time/clocks/context_writers.cpp +++ b/src/core/hle/service/psc/time/clocks/context_writers.cpp @@ -22,7 +22,7 @@ LocalSystemClockContextWriter::LocalSystemClockContextWriter(Core::System& syste SharedMemory& shared_memory) : m_system{system}, m_shared_memory{shared_memory} {} -Result LocalSystemClockContextWriter::Write(SystemClockContext& context) { +Result LocalSystemClockContextWriter::Write(const SystemClockContext& context) { if (m_in_use) { R_SUCCEED_IF(context == m_context); m_context = context; @@ -43,7 +43,7 @@ NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& s SystemClockCore& system_clock) : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {} -Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) { +Result NetworkSystemClockContextWriter::Write(const SystemClockContext& context) { s64 time{}; [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time); @@ -66,7 +66,7 @@ EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWrit Core::System& system) : m_system{system} {} -Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) { +Result EphemeralNetworkSystemClockContextWriter::Write(const SystemClockContext& context) { if (m_in_use) { R_SUCCEED_IF(context == m_context); m_context = context; diff --git a/src/core/hle/service/psc/time/clocks/context_writers.h b/src/core/hle/service/psc/time/clocks/context_writers.h index afd3725d4..6643fc9f2 100644 --- a/src/core/hle/service/psc/time/clocks/context_writers.h +++ b/src/core/hle/service/psc/time/clocks/context_writers.h @@ -24,7 +24,7 @@ private: public: virtual ~ContextWriter() = default; - virtual Result Write(SystemClockContext& context) = 0; + virtual Result Write(const SystemClockContext& context) = 0; void SignalAllNodes(); void Link(OperationEvent& operation_event); @@ -37,7 +37,7 @@ class LocalSystemClockContextWriter : public ContextWriter { public: explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory); - Result Write(SystemClockContext& context) override; + Result Write(const SystemClockContext& context) override; private: Core::System& m_system; @@ -52,7 +52,7 @@ public: explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory, SystemClockCore& system_clock); - Result Write(SystemClockContext& context) override; + Result Write(const SystemClockContext& context) override; private: Core::System& m_system; @@ -67,7 +67,7 @@ class EphemeralNetworkSystemClockContextWriter : public ContextWriter { public: EphemeralNetworkSystemClockContextWriter(Core::System& system); - Result Write(SystemClockContext& context) override; + Result Write(const SystemClockContext& context) override; private: Core::System& m_system; diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp index 36dca6689..6a74d4594 100644 --- a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.cpp @@ -5,7 +5,7 @@ namespace Service::PSC::Time { -void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) { +void StandardLocalSystemClockCore::Initialize(const SystemClockContext& context, s64 time) { SteadyClockTimePoint time_point{}; if (GetCurrentTimePoint(time_point) == ResultSuccess && context.steady_time_point.IdMatches(time_point)) { diff --git a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h index 176ba3e94..5722d8e96 100644 --- a/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/standard_local_system_clock_core.h @@ -17,7 +17,7 @@ public: : SystemClockCore{steady_clock} {} ~StandardLocalSystemClockCore() override = default; - void Initialize(SystemClockContext& context, s64 time); + void Initialize(const SystemClockContext& context, s64 time); }; } // namespace Service::PSC::Time diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp index 8d6cb7db1..6938d369f 100644 --- a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.cpp @@ -5,7 +5,7 @@ namespace Service::PSC::Time { -void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) { +void StandardNetworkSystemClockCore::Initialize(const SystemClockContext& context, s64 accuracy) { if (SetContextAndWrite(context) != ResultSuccess) { LOG_ERROR(Service_Time, "Failed to SetContext"); } diff --git a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h index 933d2c8e3..bfafc7d71 100644 --- a/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/standard_network_system_clock_core.h @@ -19,7 +19,7 @@ public: : SystemClockCore{steady_clock} {} ~StandardNetworkSystemClockCore() override = default; - void Initialize(SystemClockContext& context, s64 accuracy); + void Initialize(const SystemClockContext& context, s64 accuracy); bool IsAccuracySufficient(); private: diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp index 9e9be05d6..31ed27396 100644 --- a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.cpp @@ -46,7 +46,7 @@ Result StandardUserSystemClockCore::GetContext(SystemClockContext& out_context) R_RETURN(m_local_system_clock.GetContext(out_context)); } -Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) { +Result StandardUserSystemClockCore::SetContext(const SystemClockContext& context) { R_RETURN(ResultNotImplemented); } diff --git a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h index a7fe7648d..32b8bc3bc 100644 --- a/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/standard_user_system_clock_core.h @@ -36,7 +36,7 @@ public: Result SetAutomaticCorrection(bool automatic_correction); Result GetContext(SystemClockContext& out_context) const override; - Result SetContext(SystemClockContext& context) override; + Result SetContext(const SystemClockContext& context) override; Result GetTimePoint(SteadyClockTimePoint& out_time_point); void SetTimePointAndSignal(SteadyClockTimePoint& time_point); diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp index c507ef517..2b7466831 100644 --- a/src/core/hle/service/psc/time/clocks/system_clock_core.cpp +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.cpp @@ -51,12 +51,12 @@ Result SystemClockCore::GetContext(SystemClockContext& out_context) const { R_SUCCEED(); } -Result SystemClockCore::SetContext(SystemClockContext& context) { +Result SystemClockCore::SetContext(const SystemClockContext& context) { m_context = context; R_SUCCEED(); } -Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) { +Result SystemClockCore::SetContextAndWrite(const SystemClockContext& context) { R_TRY(SetContext(context)); if (m_context_writer) { diff --git a/src/core/hle/service/psc/time/clocks/system_clock_core.h b/src/core/hle/service/psc/time/clocks/system_clock_core.h index 73811712e..0b928432f 100644 --- a/src/core/hle/service/psc/time/clocks/system_clock_core.h +++ b/src/core/hle/service/psc/time/clocks/system_clock_core.h @@ -41,8 +41,8 @@ public: } virtual Result GetContext(SystemClockContext& out_context) const; - virtual Result SetContext(SystemClockContext& context); - Result SetContextAndWrite(SystemClockContext& context); + virtual Result SetContext(const SystemClockContext& context); + Result SetContextAndWrite(const SystemClockContext& context); void LinkOperationEvent(OperationEvent& operation_event); diff --git a/src/core/hle/service/psc/time/service_manager.cpp b/src/core/hle/service/psc/time/service_manager.cpp index 4e1643fcb..ed9fb32cd 100644 --- a/src/core/hle/service/psc/time/service_manager.cpp +++ b/src/core/hle/service/psc/time/service_manager.cpp @@ -78,8 +78,9 @@ Result ServiceManager::GetStaticServiceAsServiceManager(OutInterface<StaticServi } Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected, - Common::UUID& clock_source_id, s64 rtc_offset, - s64 internal_offset, s64 test_offset) { + const Common::UUID& clock_source_id, + s64 rtc_offset, s64 internal_offset, + s64 test_offset) { LOG_DEBUG(Service_Time, "called. is_rtc_reset_detected={} clock_source_id={} rtc_offset={} " "internal_offset={} test_offset={}", @@ -102,7 +103,8 @@ Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected, R_SUCCEED(); } -Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) { +Result ServiceManager::SetupStandardLocalSystemClockCore(const SystemClockContext& context, + s64 time) { LOG_DEBUG(Service_Time, "called. context={} context.steady_time_point.clock_source_id={} time={}", context, context.steady_time_point.clock_source_id.RawString(), time); @@ -114,7 +116,7 @@ Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& con R_SUCCEED(); } -Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context, +Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext context, s64 accuracy) { LOG_DEBUG(Service_Time, "called. context={} steady_time_point.clock_source_id={} accuracy={}", context, context.steady_time_point.clock_source_id.RawString(), accuracy); @@ -131,7 +133,7 @@ Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& c } Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correction, - SteadyClockTimePoint& time_point) { + SteadyClockTimePoint time_point) { LOG_DEBUG(Service_Time, "called. automatic_correction={} time_point={} clock_source_id={}", automatic_correction, time_point, time_point.clock_source_id.RawString()); @@ -144,9 +146,9 @@ Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correctio R_SUCCEED(); } -Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name, RuleVersion& rule_version, - u32 location_count, - SteadyClockTimePoint& time_point, +Result ServiceManager::SetupTimeZoneServiceCore(const LocationName& name, + const RuleVersion& rule_version, u32 location_count, + const SteadyClockTimePoint& time_point, InBuffer<BufferAttr_HipcAutoSelect> rule_buffer) { LOG_DEBUG(Service_Time, "called. name={} rule_version={} location_count={} time_point={} " diff --git a/src/core/hle/service/psc/time/service_manager.h b/src/core/hle/service/psc/time/service_manager.h index 25d361d4f..22720e161 100644 --- a/src/core/hle/service/psc/time/service_manager.h +++ b/src/core/hle/service/psc/time/service_manager.h @@ -34,14 +34,15 @@ public: Result GetStaticServiceAsAdmin(OutInterface<StaticService> out_service); Result GetStaticServiceAsRepair(OutInterface<StaticService> out_service); Result GetStaticServiceAsServiceManager(OutInterface<StaticService> out_service); - Result SetupStandardSteadyClockCore(bool is_rtc_reset_detected, Common::UUID& clock_source_id, - s64 rtc_offset, s64 internal_offset, s64 test_offset); - Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time); - Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy); + Result SetupStandardSteadyClockCore(bool is_rtc_reset_detected, + const Common::UUID& clock_source_id, s64 rtc_offset, + s64 internal_offset, s64 test_offset); + Result SetupStandardLocalSystemClockCore(const SystemClockContext& context, s64 time); + Result SetupStandardNetworkSystemClockCore(SystemClockContext context, s64 accuracy); Result SetupStandardUserSystemClockCore(bool automatic_correction, - SteadyClockTimePoint& time_point); - Result SetupTimeZoneServiceCore(LocationName& name, RuleVersion& rule_version, - u32 location_count, SteadyClockTimePoint& time_point, + SteadyClockTimePoint time_point); + Result SetupTimeZoneServiceCore(const LocationName& name, const RuleVersion& rule_version, + u32 location_count, const SteadyClockTimePoint& time_point, InBuffer<BufferAttr_HipcAutoSelect> rule_buffer); Result SetupEphemeralNetworkSystemClockCore(); Result GetStandardLocalClockOperationEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); diff --git a/src/core/hle/service/psc/time/shared_memory.cpp b/src/core/hle/service/psc/time/shared_memory.cpp index defaceebe..adef6bcd8 100644 --- a/src/core/hle/service/psc/time/shared_memory.cpp +++ b/src/core/hle/service/psc/time/shared_memory.cpp @@ -51,11 +51,11 @@ SharedMemory::SharedMemory(Core::System& system) std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr)); } -void SharedMemory::SetLocalSystemContext(SystemClockContext& context) { +void SharedMemory::SetLocalSystemContext(const SystemClockContext& context) { WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context); } -void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) { +void SharedMemory::SetNetworkSystemContext(const SystemClockContext& context) { WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context); } @@ -64,7 +64,7 @@ void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 ti {time_point, clock_source_id}); } -void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) { +void SharedMemory::SetContinuousAdjustment(const ContinuousAdjustmentTimePoint& time_point) { WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point); } diff --git a/src/core/hle/service/psc/time/shared_memory.h b/src/core/hle/service/psc/time/shared_memory.h index f9bf97d5c..b7bd00fc1 100644 --- a/src/core/hle/service/psc/time/shared_memory.h +++ b/src/core/hle/service/psc/time/shared_memory.h @@ -54,10 +54,10 @@ public: return m_k_shared_memory; } - void SetLocalSystemContext(SystemClockContext& context); - void SetNetworkSystemContext(SystemClockContext& context); + void SetLocalSystemContext(const SystemClockContext& context); + void SetNetworkSystemContext(const SystemClockContext& context); void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff); - void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point); + void SetContinuousAdjustment(const ContinuousAdjustmentTimePoint& time_point); void SetAutomaticCorrection(bool automatic_correction); void UpdateBaseTime(s64 time); diff --git a/src/core/hle/service/psc/time/static.cpp b/src/core/hle/service/psc/time/static.cpp index 3ca3311af..24b85cc61 100644 --- a/src/core/hle/service/psc/time/static.cpp +++ b/src/core/hle/service/psc/time/static.cpp @@ -198,8 +198,8 @@ Result StaticService::GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( R_SUCCEED(); } -Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(Out<s64> out_time, - SystemClockContext& context) { +Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( + Out<s64> out_time, const SystemClockContext& context) { SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized); @@ -231,10 +231,9 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType t R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); } -Result StaticService::GetClockSnapshotFromSystemClockContext(TimeType type, - OutClockSnapshot out_snapshot, - SystemClockContext& user_context, - SystemClockContext& network_context) { +Result StaticService::GetClockSnapshotFromSystemClockContext( + TimeType type, OutClockSnapshot out_snapshot, const SystemClockContext& user_context, + const SystemClockContext& network_context) { SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. type={} user_context={} network_context={} out_snapshot={}", type, @@ -294,8 +293,9 @@ Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, } Result StaticService::GetClockSnapshotImpl(OutClockSnapshot out_snapshot, - SystemClockContext& user_context, - SystemClockContext& network_context, TimeType type) { + const SystemClockContext& user_context, + const SystemClockContext& network_context, + TimeType type) { out_snapshot->user_context = user_context; out_snapshot->network_context = network_context; diff --git a/src/core/hle/service/psc/time/static.h b/src/core/hle/service/psc/time/static.h index 120bab259..e11db8093 100644 --- a/src/core/hle/service/psc/time/static.h +++ b/src/core/hle/service/psc/time/static.h @@ -55,18 +55,19 @@ public: Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( Out<SteadyClockTimePoint> out_time_point); Result CalculateMonotonicSystemClockBaseTimePoint(Out<s64> out_time, - SystemClockContext& context); + const SystemClockContext& context); Result GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type); Result GetClockSnapshotFromSystemClockContext(TimeType type, OutClockSnapshot out_snapshot, - SystemClockContext& user_context, - SystemClockContext& network_context); + const SystemClockContext& user_context, + const SystemClockContext& network_context); Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference, InClockSnapshot a, InClockSnapshot b); Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b); private: - Result GetClockSnapshotImpl(OutClockSnapshot out_snapshot, SystemClockContext& user_context, - SystemClockContext& network_context, TimeType type); + Result GetClockSnapshotImpl(OutClockSnapshot out_snapshot, + const SystemClockContext& user_context, + const SystemClockContext& network_context, TimeType type); Core::System& m_system; StaticServiceSetupInfo m_setup_info; diff --git a/src/core/hle/service/psc/time/system_clock.cpp b/src/core/hle/service/psc/time/system_clock.cpp index 0695502d5..b4e9264d8 100644 --- a/src/core/hle/service/psc/time/system_clock.cpp +++ b/src/core/hle/service/psc/time/system_clock.cpp @@ -53,7 +53,7 @@ Result SystemClock::GetSystemClockContext(Out<SystemClockContext> out_context) { R_RETURN(m_clock_core.GetContext(*out_context)); } -Result SystemClock::SetSystemClockContext(SystemClockContext& context) { +Result SystemClock::SetSystemClockContext(const SystemClockContext& context) { LOG_DEBUG(Service_Time, "called. context={}", context); R_UNLESS(m_can_write_clock, ResultPermissionDenied); diff --git a/src/core/hle/service/psc/time/system_clock.h b/src/core/hle/service/psc/time/system_clock.h index b40d73595..3c11fb2f8 100644 --- a/src/core/hle/service/psc/time/system_clock.h +++ b/src/core/hle/service/psc/time/system_clock.h @@ -26,7 +26,7 @@ public: Result GetCurrentTime(Out<s64> out_time); Result SetCurrentTime(s64 time); Result GetSystemClockContext(Out<SystemClockContext> out_context); - Result SetSystemClockContext(SystemClockContext& context); + Result SetSystemClockContext(const SystemClockContext& context); Result GetOperationEventReadableHandle(OutCopyHandle<Kernel::KReadableEvent> out_event); private: diff --git a/src/core/hle/service/psc/time/time_zone.cpp b/src/core/hle/service/psc/time/time_zone.cpp index cc855c763..81bfb9092 100644 --- a/src/core/hle/service/psc/time/time_zone.cpp +++ b/src/core/hle/service/psc/time/time_zone.cpp @@ -55,7 +55,7 @@ constexpr bool GetTimeZoneTime(s64& out_time, const Tz::Rule& rule, s64 time, s3 } } // namespace -void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) { +void TimeZone::SetTimePoint(const SteadyClockTimePoint& time_point) { std::scoped_lock l{m_mutex}; m_steady_clock_time_point = time_point; } @@ -65,7 +65,7 @@ void TimeZone::SetTotalLocationNameCount(u32 count) { m_total_location_name_count = count; } -void TimeZone::SetRuleVersion(RuleVersion& rule_version) { +void TimeZone::SetRuleVersion(const RuleVersion& rule_version) { std::scoped_lock l{m_mutex}; m_rule_version = rule_version; } @@ -123,7 +123,7 @@ Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time, R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule)); } -Result TimeZone::ParseBinary(LocationName& name, std::span<const u8> binary) { +Result TimeZone::ParseBinary(const LocationName& name, std::span<const u8> binary) { std::scoped_lock l{m_mutex}; Tz::Rule tmp_rule{}; diff --git a/src/core/hle/service/psc/time/time_zone.h b/src/core/hle/service/psc/time/time_zone.h index 6248e45f9..0e4ed6ed0 100644 --- a/src/core/hle/service/psc/time/time_zone.h +++ b/src/core/hle/service/psc/time/time_zone.h @@ -23,9 +23,9 @@ public: m_initialized = true; } - void SetTimePoint(SteadyClockTimePoint& time_point); + void SetTimePoint(const SteadyClockTimePoint& time_point); void SetTotalLocationNameCount(u32 count); - void SetRuleVersion(RuleVersion& rule_version); + void SetRuleVersion(const RuleVersion& rule_version); Result GetLocationName(LocationName& out_name); Result GetTotalLocationCount(u32& out_count); Result GetRuleVersion(RuleVersion& out_rule_version); @@ -36,7 +36,7 @@ public: const Tz::Rule& rule); Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time, CalendarAdditionalInfo& calendar_additional, s64 time); - Result ParseBinary(LocationName& name, std::span<const u8> binary); + Result ParseBinary(const LocationName& name, std::span<const u8> binary); Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary); Result ToPosixTime(u32& out_count, std::span<s64> out_times, size_t out_times_max_count, const CalendarTime& calendar, const Tz::Rule& rule); diff --git a/src/core/hle/service/psc/time/time_zone_service.cpp b/src/core/hle/service/psc/time/time_zone_service.cpp index eb81f5b03..2f80030a4 100644 --- a/src/core/hle/service/psc/time/time_zone_service.cpp +++ b/src/core/hle/service/psc/time/time_zone_service.cpp @@ -42,7 +42,7 @@ Result TimeZoneService::GetDeviceLocationName(Out<LocationName> out_location_nam R_RETURN(m_time_zone.GetLocationName(*out_location_name)); } -Result TimeZoneService::SetDeviceLocationName(LocationName& location_name) { +Result TimeZoneService::SetDeviceLocationName(const LocationName& location_name) { LOG_DEBUG(Service_Time, "called. This function is not implemented!"); R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied); @@ -62,7 +62,7 @@ Result TimeZoneService::LoadLocationNameList( R_RETURN(ResultNotImplemented); } -Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, LocationName& location_name) { +Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, const LocationName& location_name) { LOG_DEBUG(Service_Time, "called. This function is not implemented!"); R_RETURN(ResultNotImplemented); @@ -86,7 +86,7 @@ Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( } Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule( - LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) { + const LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) { LOG_DEBUG(Service_Time, "called. location_name={}", location_name); R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied); diff --git a/src/core/hle/service/psc/time/time_zone_service.h b/src/core/hle/service/psc/time/time_zone_service.h index 6eb9ddc4b..79b6073e5 100644 --- a/src/core/hle/service/psc/time/time_zone_service.h +++ b/src/core/hle/service/psc/time/time_zone_service.h @@ -31,16 +31,16 @@ public: ~TimeZoneService() override = default; Result GetDeviceLocationName(Out<LocationName> out_location_name); - Result SetDeviceLocationName(LocationName& location_name); + Result SetDeviceLocationName(const LocationName& location_name); Result GetTotalLocationNameCount(Out<u32> out_count); Result LoadLocationNameList(Out<u32> out_count, OutArray<LocationName, BufferAttr_HipcMapAlias> out_names, u32 index); - Result LoadTimeZoneRule(OutRule out_rule, LocationName& location_name); + Result LoadTimeZoneRule(OutRule out_rule, const LocationName& location_name); Result GetTimeZoneRuleVersion(Out<RuleVersion> out_rule_version); Result GetDeviceLocationNameAndUpdatedTime(Out<LocationName> location_name, Out<SteadyClockTimePoint> out_time_point); - Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name, + Result SetDeviceLocationNameWithTimeZoneRule(const LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary); Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary); Result GetDeviceLocationNameOperationEventReadableHandle( diff --git a/src/core/hle/service/ro/ro.cpp b/src/core/hle/service/ro/ro.cpp index 51196170a..3d3ad2d62 100644 --- a/src/core/hle/service/ro/ro.cpp +++ b/src/core/hle/service/ro/ro.cpp @@ -549,13 +549,13 @@ public: } Result RegisterProcessHandle(ClientProcessId client_pid, - InCopyHandle<Kernel::KProcess>& process) { + InCopyHandle<Kernel::KProcess> process) { // Register the process. R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.Get(), *client_pid)); } Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, - InCopyHandle<Kernel::KProcess>& process) { + InCopyHandle<Kernel::KProcess> process) { // Validate the process. R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); diff --git a/src/core/hle/service/set/setting_formats/system_settings.h b/src/core/hle/service/set/setting_formats/system_settings.h index ebc373da5..40230182a 100644 --- a/src/core/hle/service/set/setting_formats/system_settings.h +++ b/src/core/hle/service/set/setting_formats/system_settings.h @@ -12,6 +12,7 @@ #include "common/vector_math.h" #include "core/hle/service/set/setting_formats/private_settings.h" #include "core/hle/service/set/settings_types.h" +#include "hid_core/resources/touch_screen/touch_types.h" namespace Service::Set { @@ -257,8 +258,7 @@ struct SystemSettings { std::array<u8, 0x10> analog_stick_user_calibration_left; std::array<u8, 0x10> analog_stick_user_calibration_right; - // nn::settings::system::TouchScreenMode - s32 touch_screen_mode; + TouchScreenMode touch_screen_mode; INSERT_PADDING_BYTES(0x14); // Reserved TvSettings tv_settings; diff --git a/src/core/hle/service/set/system_settings_server.cpp b/src/core/hle/service/set/system_settings_server.cpp index d3d0fb112..7ef4a0ded 100644 --- a/src/core/hle/service/set/system_settings_server.cpp +++ b/src/core/hle/service/set/system_settings_server.cpp @@ -275,8 +275,8 @@ ISystemSettingsServer::ISystemSettingsServer(Core::System& system_) {184, nullptr, "SetPlatformRegion"}, {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, {186, nullptr, "GetMemoryUsageRateFlag"}, - {187, nullptr, "GetTouchScreenMode"}, - {188, nullptr, "SetTouchScreenMode"}, + {187, &ISystemSettingsServer::GetTouchScreenMode, "GetTouchScreenMode"}, + {188, &ISystemSettingsServer::SetTouchScreenMode, "SetTouchScreenMode"}, {189, nullptr, "GetButtonConfigSettingsFull"}, {190, nullptr, "SetButtonConfigSettingsFull"}, {191, nullptr, "GetButtonConfigSettingsEmbedded"}, @@ -1395,6 +1395,28 @@ void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) { rb.Push(0); } +void ISystemSettingsServer::GetTouchScreenMode(HLERequestContext& ctx) { + TouchScreenMode touch_screen_mode{}; + auto res = GetTouchScreenMode(touch_screen_mode); + + LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode); + + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(res); + rb.PushEnum(touch_screen_mode); +} + +void ISystemSettingsServer::SetTouchScreenMode(HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const auto touch_screen_mode = rp.PopEnum<TouchScreenMode>(); + auto res = SetTouchScreenMode(touch_screen_mode); + + LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode); + + IPC::ResponseBuilder rb{ctx, 2}; + rb.Push(res); +} + void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) { LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag); @@ -1670,4 +1692,15 @@ Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( R_SUCCEED(); } +Result ISystemSettingsServer::GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const { + touch_screen_mode = m_system_settings.touch_screen_mode; + R_SUCCEED(); +} + +Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) { + m_system_settings.touch_screen_mode = touch_screen_mode; + SetSaveNeeded(); + R_SUCCEED(); +} + } // namespace Service::Set diff --git a/src/core/hle/service/set/system_settings_server.h b/src/core/hle/service/set/system_settings_server.h index 1982b9723..9a3b36f0c 100644 --- a/src/core/hle/service/set/system_settings_server.h +++ b/src/core/hle/service/set/system_settings_server.h @@ -74,6 +74,8 @@ public: Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; Result SetUserSystemClockAutomaticCorrectionUpdatedTime( const Service::PSC::Time::SteadyClockTimePoint& time_point); + Result GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const; + Result SetTouchScreenMode(TouchScreenMode touch_screen_mode); private: void SetLanguageCode(HLERequestContext& ctx); @@ -154,6 +156,8 @@ private: void GetChineseTraditionalInputMethod(HLERequestContext& ctx); void GetHomeMenuScheme(HLERequestContext& ctx); void GetHomeMenuSchemeModel(HLERequestContext& ctx); + void GetTouchScreenMode(HLERequestContext& ctx); + void SetTouchScreenMode(HLERequestContext& ctx); void GetFieldTestingFlag(HLERequestContext& ctx); void GetPanelCrcMode(HLERequestContext& ctx); void SetPanelCrcMode(HLERequestContext& ctx); diff --git a/src/core/hle/service/vi/display/vi_display.cpp b/src/core/hle/service/vi/display/vi_display.cpp index dab1905cc..7f2af9acc 100644 --- a/src/core/hle/service/vi/display/vi_display.cpp +++ b/src/core/hle/service/vi/display/vi_display.cpp @@ -16,6 +16,7 @@ #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" #include "core/hle/service/nvnflinger/buffer_queue_core.h" #include "core/hle/service/nvnflinger/buffer_queue_producer.h" +#include "core/hle/service/nvnflinger/hardware_composer.h" #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" #include "core/hle/service/vi/display/vi_display.h" #include "core/hle/service/vi/layer/vi_layer.h" @@ -43,6 +44,7 @@ Display::Display(u64 id, std::string name_, KernelHelpers::ServiceContext& service_context_, Core::System& system_) : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, service_context{service_context_} { + hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>(); vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); } @@ -81,8 +83,6 @@ void Display::SignalVSyncEvent() { void Display::CreateLayer(u64 layer_id, u32 binder_id, Service::Nvidia::NvCore::Container& nv_core) { - ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); - auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); diff --git a/src/core/hle/service/vi/display/vi_display.h b/src/core/hle/service/vi/display/vi_display.h index 8eb8a5155..220292cff 100644 --- a/src/core/hle/service/vi/display/vi_display.h +++ b/src/core/hle/service/vi/display/vi_display.h @@ -11,9 +11,14 @@ #include "common/common_types.h" #include "core/hle/result.h" +namespace Core { +class System; +} + namespace Kernel { class KEvent; -} +class KReadableEvent; +} // namespace Kernel namespace Service::android { class BufferQueueProducer; @@ -24,8 +29,9 @@ class ServiceContext; } namespace Service::Nvnflinger { +class HardwareComposer; class HosBinderDriverServer; -} +} // namespace Service::Nvnflinger namespace Service::Nvidia::NvCore { class Container; @@ -118,6 +124,10 @@ public: /// const Layer* FindLayer(u64 layer_id) const; + Nvnflinger::HardwareComposer& GetComposer() const { + return *hardware_composer; + } + private: u64 display_id; std::string name; @@ -125,6 +135,7 @@ private: KernelHelpers::ServiceContext& service_context; std::vector<std::unique_ptr<Layer>> layers; + std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer; Kernel::KEvent* vsync_event{}; bool is_abandoned{}; }; diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 73058db9a..d508ed28c 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -195,8 +195,9 @@ private: void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; const u64 buffer_id = rp.PopRaw<u64>(); + const u64 aruid = ctx.GetPID(); - LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id); + LOG_INFO(Service_VI, "called. buffer_id={:#x}, aruid={:#x}", buffer_id, aruid); struct OutputParameters { s32 nvmap_handle; @@ -206,7 +207,7 @@ private: OutputParameters out{}; Nvnflinger::SharedMemoryPoolLayout layout{}; const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( - &out.size, &out.nvmap_handle, &layout, buffer_id, 0); + &out.size, &out.nvmap_handle, &layout, buffer_id, aruid); ctx.WriteBuffer(&layout, sizeof(layout)); diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp index 96fa7fa3a..14d1a3840 100644 --- a/src/core/memory/cheat_engine.cpp +++ b/src/core/memory/cheat_engine.cpp @@ -9,6 +9,7 @@ #include "core/core_timing.h" #include "core/hle/kernel/k_page_table.h" #include "core/hle/kernel/k_process.h" +#include "core/hle/kernel/k_process_page_table.h" #include "core/hle/service/hid/hid_server.h" #include "core/hle/service/sm/sm.h" #include "core/memory.h" @@ -46,12 +47,23 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta StandardVmCallbacks::~StandardVmCallbacks() = default; -void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { - system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size); +void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) { + // Return zero on invalid address + if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) { + std::memset(data, 0, size); + return; + } + + system.ApplicationMemory().ReadBlock(address, data, size); } -void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { - system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size); +void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) { + // Skip invalid memory write address + if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) { + return; + } + + system.ApplicationMemory().WriteBlock(address, data, size); } u64 StandardVmCallbacks::HidKeysDown() { @@ -81,21 +93,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) { data.back() == '\n' ? data.substr(0, data.size() - 1) : data); } -VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const { +bool StandardVmCallbacks::IsAddressInRange(VAddr in) const { if ((in < metadata.main_nso_extents.base || in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && (in < metadata.heap_extents.base || - in >= metadata.heap_extents.base + metadata.heap_extents.size)) { - LOG_ERROR(CheatEngine, + in >= metadata.heap_extents.base + metadata.heap_extents.size) && + (in < metadata.alias_extents.base || + in >= metadata.heap_extents.base + metadata.alias_extents.size) && + (in < metadata.aslr_extents.base || + in >= metadata.heap_extents.base + metadata.aslr_extents.size)) { + LOG_DEBUG(CheatEngine, "Cheat attempting to access memory at invalid address={:016X}, if this " "persists, " "the cheat may be incorrect. However, this may be normal early in execution if " "the game has not properly set up yet.", in); - return 0; ///< Invalid addresses will hard crash + return false; ///< Invalid addresses will hard crash } - return in; + return true; } CheatParser::~CheatParser() = default; @@ -211,16 +227,14 @@ void CheatEngine::Initialize() { .base = GetInteger(page_table.GetHeapRegionStart()), .size = page_table.GetHeapRegionSize(), }; - - metadata.address_space_extents = { - .base = GetInteger(page_table.GetAddressSpaceStart()), - .size = page_table.GetAddressSpaceSize(), - }; - - metadata.alias_extents = { + metadata.aslr_extents = { .base = GetInteger(page_table.GetAliasCodeRegionStart()), .size = page_table.GetAliasCodeRegionSize(), }; + metadata.alias_extents = { + .base = GetInteger(page_table.GetAliasRegionStart()), + .size = page_table.GetAliasRegionSize(), + }; is_pending_reload.exchange(true); } diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h index ced2168d1..619cabaa2 100644 --- a/src/core/memory/cheat_engine.h +++ b/src/core/memory/cheat_engine.h @@ -27,17 +27,17 @@ public: StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_); ~StandardVmCallbacks() override; - void MemoryRead(VAddr address, void* data, u64 size) override; - void MemoryWrite(VAddr address, const void* data, u64 size) override; + void MemoryReadUnsafe(VAddr address, void* data, u64 size) override; + void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override; u64 HidKeysDown() override; void DebugLog(u8 id, u64 value) override; void CommandLog(std::string_view data) override; private: - VAddr SanitizeAddress(VAddr address) const; + bool IsAddressInRange(VAddr address) const; const CheatProcessMetadata& metadata; - System& system; + Core::System& system; }; // Intermediary class that parses a text file or other disk format for storing cheats into a diff --git a/src/core/memory/dmnt_cheat_types.h b/src/core/memory/dmnt_cheat_types.h index c6b40e505..64c072d3d 100644 --- a/src/core/memory/dmnt_cheat_types.h +++ b/src/core/memory/dmnt_cheat_types.h @@ -18,7 +18,7 @@ struct CheatProcessMetadata { MemoryRegionExtents main_nso_extents{}; MemoryRegionExtents heap_extents{}; MemoryRegionExtents alias_extents{}; - MemoryRegionExtents address_space_extents{}; + MemoryRegionExtents aslr_extents{}; std::array<u8, 0x20> main_nso_build_id{}; }; diff --git a/src/core/memory/dmnt_cheat_vm.cpp b/src/core/memory/dmnt_cheat_vm.cpp index 31ffc4fbb..8bc81e72d 100644 --- a/src/core/memory/dmnt_cheat_vm.cpp +++ b/src/core/memory/dmnt_cheat_vm.cpp @@ -322,8 +322,9 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { } break; case CheatVmOpcodeType::EndConditionalBlock: { // 20000000 - // There's actually nothing left to process here! - opcode.opcode = EndConditionalOpcode{}; + opcode.opcode = EndConditionalOpcode{ + .is_else = ((first_dword >> 24) & 0xf) == 1, + }; } break; case CheatVmOpcodeType::ControlLoop: { // 300R0000 VVVVVVVV @@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { .idx = first_dword & 0xF, }; } break; + case CheatVmOpcodeType::PauseProcess: { + /* FF0????? */ + /* FF0 = opcode 0xFF0 */ + /* Pauses the current process. */ + opcode.opcode = PauseProcessOpcode{}; + } break; + case CheatVmOpcodeType::ResumeProcess: { + /* FF0????? */ + /* FF0 = opcode 0xFF0 */ + /* Pauses the current process. */ + opcode.opcode = ResumeProcessOpcode{}; + } break; case CheatVmOpcodeType::DebugLog: { // FFFTIX## // FFFTI0Ma aaaaaaaa @@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { return valid; } -void DmntCheatVm::SkipConditionalBlock() { +void DmntCheatVm::SkipConditionalBlock(bool is_if) { if (condition_depth > 0) { // We want to continue until we're out of the current block. const std::size_t desired_depth = condition_depth - 1; @@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() { // We also support nesting of conditional blocks, and Gateway does not. if (skip_opcode.begin_conditional_block) { condition_depth++; - } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { - condition_depth--; + } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) { + if (!end_cond->is_else) { + condition_depth--; + } else if (is_if && condition_depth - 1 == desired_depth) { + break; + } } } } else { @@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata, return metadata.main_nso_extents.base + rel_address; case MemoryAccessType::Heap: return metadata.heap_extents.base + rel_address; + case MemoryAccessType::Alias: + return metadata.alias_extents.base + rel_address; + case MemoryAccessType::Aslr: + return metadata.aslr_extents.base + rel_address; } } @@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() { registers.fill(0); saved_values.fill(0); loop_tops.fill(0); - static_registers.fill(0); instruction_ptr = 0; condition_depth = 0; decode_success = true; @@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { case 2: case 4: case 8: - callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); + callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width); break; } } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { @@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { case 2: case 4: case 8: - callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); + callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width); break; } // Check against condition. @@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { } // Skip conditional block if condition not met. if (!cond_met) { - SkipConditionalBlock(); + SkipConditionalBlock(true); } - } else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) { - // Decrement the condition depth. - // We will assume, graciously, that mismatched conditional block ends are a nop. - if (condition_depth > 0) { - condition_depth--; + } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) { + if (end_cond->is_else) { + /* Skip to the end of the conditional block. */ + this->SkipConditionalBlock(false); + } else { + /* Decrement the condition depth. */ + /* We will assume, graciously, that mismatched conditional block ends are a nop. */ + if (condition_depth > 0) { + condition_depth--; + } } } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { if (ctrl_loop->start_loop) { @@ -832,8 +857,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { case 2: case 4: case 8: - callbacks->MemoryRead(src_address, ®isters[ldr_memory->reg_index], - ldr_memory->bit_width); + callbacks->MemoryReadUnsafe(src_address, ®isters[ldr_memory->reg_index], + ldr_memory->bit_width); break; } } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { @@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { case 2: case 4: case 8: - callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); + callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width); break; } // Increment register if relevant. @@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { // Check for keypress. if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { // Keys not pressed. Skip conditional block. - SkipConditionalBlock(); + SkipConditionalBlock(true); } } else if (auto perform_math_reg = std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { @@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { case 2: case 4: case 8: - callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); + callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width); break; } @@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { case 2: case 4: case 8: - callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); + callbacks->MemoryReadUnsafe(cond_address, &cond_value, + begin_reg_cond->bit_width); break; } } @@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { // Skip conditional block if condition not met. if (!cond_met) { - SkipConditionalBlock(); + SkipConditionalBlock(true); } } else if (auto save_restore_reg = std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { @@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { // Store a register to a static register. static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; } + } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) { + // TODO: Pause cheat process + } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) { + // TODO: Resume cheat process } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { // Read value from memory. u64 log_value = 0; @@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { case 2: case 4: case 8: - callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); + callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width); break; } } diff --git a/src/core/memory/dmnt_cheat_vm.h b/src/core/memory/dmnt_cheat_vm.h index 641cb09c4..fed6a24ad 100644 --- a/src/core/memory/dmnt_cheat_vm.h +++ b/src/core/memory/dmnt_cheat_vm.h @@ -42,12 +42,16 @@ enum class CheatVmOpcodeType : u32 { DoubleExtendedWidth = 0xF0, // Double-extended width opcodes. + PauseProcess = 0xFF0, + ResumeProcess = 0xFF1, DebugLog = 0xFFF, }; enum class MemoryAccessType : u32 { MainNso = 0, Heap = 1, + Alias = 2, + Aslr = 3, }; enum class ConditionalComparisonType : u32 { @@ -131,7 +135,9 @@ struct BeginConditionalOpcode { VmInt value{}; }; -struct EndConditionalOpcode {}; +struct EndConditionalOpcode { + bool is_else; +}; struct ControlLoopOpcode { bool start_loop{}; @@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode { u32 idx{}; }; +struct PauseProcessOpcode {}; + +struct ResumeProcessOpcode {}; + struct DebugLogOpcode { u32 bit_width{}; u32 log_id{}; @@ -244,8 +254,8 @@ struct CheatVmOpcode { PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, - SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode, - UnrecognizedInstruction> + SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode, + ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction> opcode{}; }; @@ -256,8 +266,8 @@ public: public: virtual ~Callbacks(); - virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; - virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; + virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0; + virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0; virtual u64 HidKeysDown() = 0; @@ -296,7 +306,7 @@ private: std::array<std::size_t, NumRegisters> loop_tops{}; bool DecodeNextOpcode(CheatVmOpcode& out); - void SkipConditionalBlock(); + void SkipConditionalBlock(bool is_if); void ResetState(); // For implementing the DebugLog opcode. diff --git a/src/frontend_common/config.cpp b/src/frontend_common/config.cpp index 905f35118..d34624d28 100644 --- a/src/frontend_common/config.cpp +++ b/src/frontend_common/config.cpp @@ -190,9 +190,9 @@ void Config::ReadTouchscreenValues() { Settings::values.touchscreen.rotation_angle = static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); Settings::values.touchscreen.diameter_x = - static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15)); + static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 90)); Settings::values.touchscreen.diameter_y = - static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15)); + static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 90)); } void Config::ReadAudioValues() { @@ -478,9 +478,9 @@ void Config::SaveTouchscreenValues() { WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, std::make_optional(static_cast<u32>(0))); WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, - std::make_optional(static_cast<u32>(15))); + std::make_optional(static_cast<u32>(90))); WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, - std::make_optional(static_cast<u32>(15))); + std::make_optional(static_cast<u32>(90))); } void Config::SaveMotionTouchValues() { diff --git a/src/hid_core/CMakeLists.txt b/src/hid_core/CMakeLists.txt index 64cd6e726..2699e1599 100644 --- a/src/hid_core/CMakeLists.txt +++ b/src/hid_core/CMakeLists.txt @@ -99,9 +99,14 @@ add_library(hid_core STATIC resources/system_buttons/system_button_types.h resources/touch_screen/gesture.cpp resources/touch_screen/gesture.h - resources/touch_screen/gesture_types.h + resources/touch_screen/gesture_handler.cpp + resources/touch_screen/gesture_handler.h resources/touch_screen/touch_screen.cpp resources/touch_screen/touch_screen.h + resources/touch_screen/touch_screen_driver.cpp + resources/touch_screen/touch_screen_driver.h + resources/touch_screen/touch_screen_resource.cpp + resources/touch_screen/touch_screen_resource.h resources/touch_screen/touch_types.h resources/unique_pad/unique_pad.cpp resources/unique_pad/unique_pad.h diff --git a/src/hid_core/hid_result.h b/src/hid_core/hid_result.h index df9b28c9a..c8dd07bfe 100644 --- a/src/hid_core/hid_result.h +++ b/src/hid_core/hid_result.h @@ -8,6 +8,10 @@ namespace Service::HID { constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; + +constexpr Result ResultTouchNotInitialized{ErrorModule::HID, 41}; +constexpr Result ResultTouchOverflow{ErrorModule::HID, 42}; + constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; @@ -23,6 +27,10 @@ constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461}; constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464}; constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501}; + +constexpr Result ResultGestureOverflow{ErrorModule::HID, 522}; +constexpr Result ResultGestureNotInitialized{ErrorModule::HID, 523}; + constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541}; constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; diff --git a/src/hid_core/hid_types.h b/src/hid_core/hid_types.h index ffb5f1926..1b2fc6295 100644 --- a/src/hid_core/hid_types.h +++ b/src/hid_core/hid_types.h @@ -299,12 +299,6 @@ enum class GyroscopeZeroDriftMode : u32 { Tight = 2, }; -// This is nn::settings::system::TouchScreenMode -enum class TouchScreenMode : u32 { - Stylus = 0, - Standard = 1, -}; - // This is nn::hid::TouchScreenModeForNx enum class TouchScreenModeForNx : u8 { UseSystemSetting, @@ -354,18 +348,6 @@ struct TouchAttribute { }; static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); -// This is nn::hid::TouchState -struct TouchState { - u64 delta_time{}; - TouchAttribute attribute{}; - u32 finger{}; - Common::Point<u32> position{}; - u32 diameter_x{}; - u32 diameter_y{}; - u32 rotation_angle{}; -}; -static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); - struct TouchFinger { u64 last_touch{}; Common::Point<float> position{}; @@ -756,4 +738,14 @@ struct UniquePadId { }; static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); +// This is nn::hid::system::FirmwareVersion +struct FirmwareVersion { + u8 major; + u8 minor; + u8 micro; + u8 revision; + std::array<char, 0xc> device_identifier; +}; +static_assert(sizeof(FirmwareVersion) == 0x10, "FirmwareVersion is an invalid size"); + } // namespace Core::HID diff --git a/src/hid_core/irsensor/image_transfer_processor.cpp b/src/hid_core/irsensor/image_transfer_processor.cpp index d6573f8dc..2b5a50ef6 100644 --- a/src/hid_core/irsensor/image_transfer_processor.cpp +++ b/src/hid_core/irsensor/image_transfer_processor.cpp @@ -145,9 +145,8 @@ void ImageTransferProcessor::SetTransferMemoryAddress(Common::ProcessAddress t_m } Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( - std::vector<u8>& data) const { - const auto size = GetDataSize(current_config.trimming_format); - data.resize(size); + std::span<u8> data) const { + const auto size = std::min(GetDataSize(current_config.trimming_format), data.size()); system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); return processor_state; } diff --git a/src/hid_core/irsensor/image_transfer_processor.h b/src/hid_core/irsensor/image_transfer_processor.h index 4e0117084..df1c9d920 100644 --- a/src/hid_core/irsensor/image_transfer_processor.h +++ b/src/hid_core/irsensor/image_transfer_processor.h @@ -3,6 +3,8 @@ #pragma once +#include <span> + #include "common/typed_address.h" #include "hid_core/irsensor/irs_types.h" #include "hid_core/irsensor/processor_base.h" @@ -39,7 +41,7 @@ public: // Transfer memory where the image data will be stored void SetTransferMemoryAddress(Common::ProcessAddress t_mem); - Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; + Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const; private: // This is nn::irsensor::ImageTransferProcessorConfig diff --git a/src/hid_core/resource_manager.cpp b/src/hid_core/resource_manager.cpp index e78665d31..245da582e 100644 --- a/src/hid_core/resource_manager.cpp +++ b/src/hid_core/resource_manager.cpp @@ -15,6 +15,7 @@ #include "hid_core/resources/applet_resource.h" #include "hid_core/resources/debug_pad/debug_pad.h" #include "hid_core/resources/digitizer/digitizer.h" +#include "hid_core/resources/hid_firmware_settings.h" #include "hid_core/resources/keyboard/keyboard.h" #include "hid_core/resources/mouse/debug_mouse.h" #include "hid_core/resources/mouse/mouse.h" @@ -29,6 +30,8 @@ #include "hid_core/resources/system_buttons/sleep_button.h" #include "hid_core/resources/touch_screen/gesture.h" #include "hid_core/resources/touch_screen/touch_screen.h" +#include "hid_core/resources/touch_screen/touch_screen_driver.h" +#include "hid_core/resources/touch_screen/touch_screen_resource.h" #include "hid_core/resources/unique_pad/unique_pad.h" #include "hid_core/resources/vibration/gc_vibration_device.h" #include "hid_core/resources/vibration/n64_vibration_device.h" @@ -45,12 +48,49 @@ constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) -ResourceManager::ResourceManager(Core::System& system_) - : system{system_}, service_context{system_, "hid"} { +ResourceManager::ResourceManager(Core::System& system_, + std::shared_ptr<HidFirmwareSettings> settings) + : firmware_settings{settings}, system{system_}, service_context{system_, "hid"} { applet_resource = std::make_shared<AppletResource>(system); + + // Register update callbacks + npad_update_event = Core::Timing::CreateEvent("HID::UpdatePadCallback", + [this](s64 time, std::chrono::nanoseconds ns_late) + -> std::optional<std::chrono::nanoseconds> { + UpdateNpad(ns_late); + return std::nullopt; + }); + default_update_event = Core::Timing::CreateEvent( + "HID::UpdateDefaultCallback", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + UpdateControllers(ns_late); + return std::nullopt; + }); + mouse_keyboard_update_event = Core::Timing::CreateEvent( + "HID::UpdateMouseKeyboardCallback", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + UpdateMouseKeyboard(ns_late); + return std::nullopt; + }); + motion_update_event = Core::Timing::CreateEvent( + "HID::UpdateMotionCallback", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + UpdateMotion(ns_late); + return std::nullopt; + }); } -ResourceManager::~ResourceManager() = default; +ResourceManager::~ResourceManager() { + system.CoreTiming().UnscheduleEvent(npad_update_event); + system.CoreTiming().UnscheduleEvent(default_update_event); + system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event); + system.CoreTiming().UnscheduleEvent(motion_update_event); + system.CoreTiming().UnscheduleEvent(touch_update_event); + input_event->Finalize(); +}; void ResourceManager::Initialize() { if (is_initialized) { @@ -59,7 +99,9 @@ void ResourceManager::Initialize() { system.HIDCore().ReloadInputDevices(); - handheld_config = std::make_shared<HandheldConfig>(); + input_event = service_context.CreateEvent("ResourceManager:InputEvent"); + + InitializeHandheldConfig(); InitializeHidCommonSampler(); InitializeTouchScreenSampler(); InitializeConsoleSixAxisSampler(); @@ -154,6 +196,7 @@ Result ResourceManager::CreateAppletResource(u64 aruid) { npad->Activate(); six_axis->Activate(); touch_screen->Activate(); + gesture->Activate(); return GetNpad()->ActivateNpadResource(aruid); } @@ -163,6 +206,17 @@ Result ResourceManager::CreateAppletResourceImpl(u64 aruid) { return applet_resource->CreateAppletResource(aruid); } +void ResourceManager::InitializeHandheldConfig() { + handheld_config = std::make_shared<HandheldConfig>(); + handheld_config->is_handheld_hid_enabled = true; + handheld_config->is_joycon_rail_enabled = true; + handheld_config->is_force_handheld_style_vibration = false; + handheld_config->is_force_handheld = false; + if (firmware_settings->IsHandheldForced()) { + handheld_config->is_joycon_rail_enabled = false; + } +} + void ResourceManager::InitializeHidCommonSampler() { debug_pad = std::make_shared<DebugPad>(system.HIDCore()); mouse = std::make_shared<Mouse>(system.HIDCore()); @@ -170,7 +224,6 @@ void ResourceManager::InitializeHidCommonSampler() { keyboard = std::make_shared<Keyboard>(system.HIDCore()); unique_pad = std::make_shared<UniquePad>(system.HIDCore()); npad = std::make_shared<NPad>(system.HIDCore(), service_context); - gesture = std::make_shared<Gesture>(system.HIDCore()); home_button = std::make_shared<HomeButton>(system.HIDCore()); sleep_button = std::make_shared<SleepButton>(system.HIDCore()); capture_button = std::make_shared<CaptureButton>(system.HIDCore()); @@ -181,11 +234,13 @@ void ResourceManager::InitializeHidCommonSampler() { debug_pad->SetAppletResource(applet_resource, &shared_mutex); digitizer->SetAppletResource(applet_resource, &shared_mutex); + unique_pad->SetAppletResource(applet_resource, &shared_mutex); keyboard->SetAppletResource(applet_resource, &shared_mutex); const auto settings = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); - npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings); + npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, input_event, + &input_mutex, settings); six_axis->SetAppletResource(applet_resource, &shared_mutex); mouse->SetAppletResource(applet_resource, &shared_mutex); @@ -193,14 +248,36 @@ void ResourceManager::InitializeHidCommonSampler() { home_button->SetAppletResource(applet_resource, &shared_mutex); sleep_button->SetAppletResource(applet_resource, &shared_mutex); capture_button->SetAppletResource(applet_resource, &shared_mutex); + + system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); + system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, + default_update_event); + system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, + mouse_keyboard_update_event); + system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, + motion_update_event); } void ResourceManager::InitializeTouchScreenSampler() { - gesture = std::make_shared<Gesture>(system.HIDCore()); - touch_screen = std::make_shared<TouchScreen>(system.HIDCore()); + // This is nn.hid.TouchScreenSampler + touch_resource = std::make_shared<TouchResource>(system); + touch_driver = std::make_shared<TouchDriver>(system.HIDCore()); + touch_screen = std::make_shared<TouchScreen>(touch_resource); + gesture = std::make_shared<Gesture>(touch_resource); + + touch_update_event = Core::Timing::CreateEvent( + "HID::TouchUpdateCallback", + [this](s64 time, + std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { + touch_resource->OnTouchUpdate(time); + return std::nullopt; + }); - touch_screen->SetAppletResource(applet_resource, &shared_mutex); - gesture->SetAppletResource(applet_resource, &shared_mutex); + touch_resource->SetTouchDriver(touch_driver); + touch_resource->SetAppletResource(applet_resource, &shared_mutex); + touch_resource->SetInputEvent(input_event, &input_mutex); + touch_resource->SetHandheldConfig(handheld_config); + touch_resource->SetTimerEvent(touch_update_event); } void ResourceManager::InitializeConsoleSixAxisSampler() { @@ -388,13 +465,15 @@ Result ResourceManager::SendVibrationValue(u64 aruid, return result; } +Result ResourceManager::GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const { + return ResultSuccess; +} + void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) { auto& core_timing = system.CoreTiming(); debug_pad->OnUpdate(core_timing); digitizer->OnUpdate(core_timing); unique_pad->OnUpdate(core_timing); - gesture->OnUpdate(core_timing); - touch_screen->OnUpdate(core_timing); palma->OnUpdate(core_timing); home_button->OnUpdate(core_timing); sleep_button->OnUpdate(core_timing); @@ -428,55 +507,9 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, }; RegisterHandlers(functions); - - // Register update callbacks - npad_update_event = Core::Timing::CreateEvent( - "HID::UpdatePadCallback", - [this, resource]( - s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - resource->UpdateNpad(ns_late); - return std::nullopt; - }); - default_update_event = Core::Timing::CreateEvent( - "HID::UpdateDefaultCallback", - [this, resource]( - s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - resource->UpdateControllers(ns_late); - return std::nullopt; - }); - mouse_keyboard_update_event = Core::Timing::CreateEvent( - "HID::UpdateMouseKeyboardCallback", - [this, resource]( - s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - resource->UpdateMouseKeyboard(ns_late); - return std::nullopt; - }); - motion_update_event = Core::Timing::CreateEvent( - "HID::UpdateMotionCallback", - [this, resource]( - s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { - const auto guard = LockService(); - resource->UpdateMotion(ns_late); - return std::nullopt; - }); - - system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); - system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, - default_update_event); - system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, - mouse_keyboard_update_event); - system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, - motion_update_event); } IAppletResource::~IAppletResource() { - system.CoreTiming().UnscheduleEvent(npad_update_event); - system.CoreTiming().UnscheduleEvent(default_update_event); - system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event); - system.CoreTiming().UnscheduleEvent(motion_update_event); resource_manager->FreeAppletResourceId(aruid); } diff --git a/src/hid_core/resource_manager.h b/src/hid_core/resource_manager.h index 128e00125..dc3ff01f8 100644 --- a/src/hid_core/resource_manager.h +++ b/src/hid_core/resource_manager.h @@ -11,6 +11,7 @@ class System; } namespace Core::HID { +struct FirmwareVersion; struct VibrationDeviceHandle; struct VibrationValue; struct VibrationDeviceInfo; @@ -21,8 +22,9 @@ struct EventType; } namespace Kernel { +class KEvent; class KSharedMemory; -} +} // namespace Kernel namespace Service::HID { class AppletResource; @@ -33,6 +35,7 @@ class DebugMouse; class DebugPad; class Digitizer; class Gesture; +class HidFirmwareSettings; class HomeButton; class Keyboard; class Mouse; @@ -42,6 +45,8 @@ class SevenSixAxis; class SixAxis; class SleepButton; class TouchScreen; +class TouchDriver; +class TouchResource; class UniquePad; class NpadVibrationBase; class NpadN64VibrationDevice; @@ -52,7 +57,7 @@ struct HandheldConfig; class ResourceManager { public: - explicit ResourceManager(Core::System& system_); + explicit ResourceManager(Core::System& system_, std::shared_ptr<HidFirmwareSettings> settings); ~ResourceManager(); void Initialize(); @@ -102,6 +107,8 @@ public: Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle, const Core::HID::VibrationValue& value); + Result GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const; + void UpdateControllers(std::chrono::nanoseconds ns_late); void UpdateNpad(std::chrono::nanoseconds ns_late); void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late); @@ -109,6 +116,7 @@ public: private: Result CreateAppletResourceImpl(u64 aruid); + void InitializeHandheldConfig(); void InitializeHidCommonSampler(); void InitializeTouchScreenSampler(); void InitializeConsoleSixAxisSampler(); @@ -117,37 +125,50 @@ private: bool is_initialized{false}; mutable std::recursive_mutex shared_mutex; - std::shared_ptr<AppletResource> applet_resource = nullptr; - - std::shared_ptr<CaptureButton> capture_button = nullptr; - std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr; - std::shared_ptr<DebugMouse> debug_mouse = nullptr; - std::shared_ptr<DebugPad> debug_pad = nullptr; - std::shared_ptr<Digitizer> digitizer = nullptr; - std::shared_ptr<Gesture> gesture = nullptr; - std::shared_ptr<HomeButton> home_button = nullptr; - std::shared_ptr<Keyboard> keyboard = nullptr; - std::shared_ptr<Mouse> mouse = nullptr; - std::shared_ptr<NPad> npad = nullptr; - std::shared_ptr<Palma> palma = nullptr; - std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr; - std::shared_ptr<SixAxis> six_axis = nullptr; - std::shared_ptr<SleepButton> sleep_button = nullptr; - std::shared_ptr<TouchScreen> touch_screen = nullptr; - std::shared_ptr<UniquePad> unique_pad = nullptr; - - std::shared_ptr<HandheldConfig> handheld_config = nullptr; + std::shared_ptr<AppletResource> applet_resource{nullptr}; + + mutable std::mutex input_mutex; + Kernel::KEvent* input_event{nullptr}; + + std::shared_ptr<HandheldConfig> handheld_config{nullptr}; + std::shared_ptr<HidFirmwareSettings> firmware_settings{nullptr}; + + std::shared_ptr<CaptureButton> capture_button{nullptr}; + std::shared_ptr<ConsoleSixAxis> console_six_axis{nullptr}; + std::shared_ptr<DebugMouse> debug_mouse{nullptr}; + std::shared_ptr<DebugPad> debug_pad{nullptr}; + std::shared_ptr<Digitizer> digitizer{nullptr}; + std::shared_ptr<HomeButton> home_button{nullptr}; + std::shared_ptr<Keyboard> keyboard{nullptr}; + std::shared_ptr<Mouse> mouse{nullptr}; + std::shared_ptr<NPad> npad{nullptr}; + std::shared_ptr<Palma> palma{nullptr}; + std::shared_ptr<SevenSixAxis> seven_six_axis{nullptr}; + std::shared_ptr<SixAxis> six_axis{nullptr}; + std::shared_ptr<SleepButton> sleep_button{nullptr}; + std::shared_ptr<UniquePad> unique_pad{nullptr}; + std::shared_ptr<Core::Timing::EventType> npad_update_event; + std::shared_ptr<Core::Timing::EventType> default_update_event; + std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; + std::shared_ptr<Core::Timing::EventType> motion_update_event; // TODO: Create these resources - // std::shared_ptr<AudioControl> audio_control = nullptr; - // std::shared_ptr<ButtonConfig> button_config = nullptr; - // std::shared_ptr<Config> config = nullptr; - // std::shared_ptr<Connection> connection = nullptr; - // std::shared_ptr<CustomConfig> custom_config = nullptr; - // std::shared_ptr<Digitizer> digitizer = nullptr; - // std::shared_ptr<Hdls> hdls = nullptr; - // std::shared_ptr<PlayReport> play_report = nullptr; - // std::shared_ptr<Rail> rail = nullptr; + // std::shared_ptr<AudioControl> audio_control{nullptr}; + // std::shared_ptr<ButtonConfig> button_config{nullptr}; + // std::shared_ptr<Config> config{nullptr}; + // std::shared_ptr<Connection> connection{nullptr}; + // std::shared_ptr<CustomConfig> custom_config{nullptr}; + // std::shared_ptr<Digitizer> digitizer{nullptr}; + // std::shared_ptr<Hdls> hdls{nullptr}; + // std::shared_ptr<PlayReport> play_report{nullptr}; + // std::shared_ptr<Rail> rail{nullptr}; + + // Touch Resources + std::shared_ptr<Gesture> gesture{nullptr}; + std::shared_ptr<TouchScreen> touch_screen{nullptr}; + std::shared_ptr<TouchResource> touch_resource{nullptr}; + std::shared_ptr<TouchDriver> touch_driver{nullptr}; + std::shared_ptr<Core::Timing::EventType> touch_update_event{nullptr}; Core::System& system; KernelHelpers::ServiceContext service_context; @@ -162,12 +183,7 @@ public: private: void GetSharedMemoryHandle(HLERequestContext& ctx); - std::shared_ptr<Core::Timing::EventType> npad_update_event; - std::shared_ptr<Core::Timing::EventType> default_update_event; - std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; - std::shared_ptr<Core::Timing::EventType> motion_update_event; - - u64 aruid; + u64 aruid{}; std::shared_ptr<ResourceManager> resource_manager; }; diff --git a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp index 6d759298e..0dde244ef 100644 --- a/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp +++ b/src/hid_core/resources/abstracted_pad/abstract_sixaxis_handler.cpp @@ -57,7 +57,7 @@ Result NpadAbstractSixAxisHandler::UpdateSixAxisState() { Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); for (std::size_t i = 0; i < AruidIndexMax; i++) { auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); - if (data->flag.is_assigned) { + if (data == nullptr || !data->flag.is_assigned) { continue; } auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; diff --git a/src/hid_core/resources/applet_resource.cpp b/src/hid_core/resources/applet_resource.cpp index db4134037..243beb1c7 100644 --- a/src/hid_core/resources/applet_resource.cpp +++ b/src/hid_core/resources/applet_resource.cpp @@ -118,6 +118,12 @@ void AppletResource::UnregisterAppletResourceUserId(u64 aruid) { data[index].aruid = 0; registration_list.flag[index] = RegistrationStatus::PendingDelete; + + for (std::size_t i = 0; i < AruidIndexMax; i++) { + if (registration_list.flag[i] == RegistrationStatus::Initialized) { + active_aruid = registration_list.aruid[i]; + } + } } void AppletResource::FreeAppletResourceId(u64 aruid) { diff --git a/src/hid_core/resources/applet_resource.h b/src/hid_core/resources/applet_resource.h index e9710d306..4a5416fb2 100644 --- a/src/hid_core/resources/applet_resource.h +++ b/src/hid_core/resources/applet_resource.h @@ -13,11 +13,12 @@ namespace Core { class System; -} +} // namespace Core namespace Kernel { +class KEvent; class KSharedMemory; -} +} // namespace Kernel namespace Service::HID { struct SharedMemoryFormat; @@ -73,7 +74,8 @@ struct AppletResourceHolder { std::recursive_mutex* shared_mutex{nullptr}; NPadResource* shared_npad_resource{nullptr}; std::shared_ptr<HandheldConfig> handheld_config{nullptr}; - long* handle_1; + Kernel::KEvent* input_event{nullptr}; + std::mutex* input_mutex{nullptr}; }; class AppletResource { diff --git a/src/hid_core/resources/digitizer/digitizer.cpp b/src/hid_core/resources/digitizer/digitizer.cpp index cd72fd6e5..5d7dcadfe 100644 --- a/src/hid_core/resources/digitizer/digitizer.cpp +++ b/src/hid_core/resources/digitizer/digitizer.cpp @@ -17,10 +17,6 @@ void Digitizer::OnInit() {} void Digitizer::OnRelease() {} void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!smart_update) { - return; - } - std::scoped_lock shared_lock{*shared_mutex}; const u64 aruid = applet_resource->GetActiveAruid(); auto* data = applet_resource->GetAruidData(aruid); diff --git a/src/hid_core/resources/digitizer/digitizer.h b/src/hid_core/resources/digitizer/digitizer.h index e031a16b0..68b03111c 100644 --- a/src/hid_core/resources/digitizer/digitizer.h +++ b/src/hid_core/resources/digitizer/digitizer.h @@ -20,8 +20,5 @@ public: // When the controller is requesting an update for the shared memory void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - bool smart_update{}; }; } // namespace Service::HID diff --git a/src/hid_core/resources/npad/npad.cpp b/src/hid_core/resources/npad/npad.cpp index 2823be348..053625b55 100644 --- a/src/hid_core/resources/npad/npad.cpp +++ b/src/hid_core/resources/npad/npad.cpp @@ -102,6 +102,8 @@ Result NPad::Activate(u64 aruid) { for (std::size_t i = 0; i < 19; ++i) { WriteEmptyEntry(npad); } + + controller.is_active = true; } return ResultSuccess; @@ -129,7 +131,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); - if (!data->flag.is_assigned) { + if (data == nullptr || !data->flag.is_assigned) { continue; } @@ -461,9 +463,16 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { std::scoped_lock lock{*applet_resource_holder.shared_mutex}; for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); - const auto aruid = data->aruid; - if (!data->flag.is_assigned) { + if (data == nullptr || !data->flag.is_assigned) { + continue; + } + + bool is_set{}; + const auto aruid = data->aruid; + npad_resource.IsSupportedNpadStyleSet(is_set, aruid); + // Wait until style is defined + if (!is_set) { continue; } @@ -484,6 +493,10 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { continue; } + if (!controller.is_active) { + continue; + } + RequestPadStateUpdate(aruid, controller.device->GetNpadIdType()); auto& pad_state = controller.npad_pad_state; auto& libnx_state = controller.npad_libnx_state; @@ -592,7 +605,9 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; libnx_state.l_stick = pad_state.l_stick; libnx_state.r_stick = pad_state.r_stick; - npad->system_ext_lifo.WriteNextEntry(pad_state); + libnx_state.sampling_number = + npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1; + npad->system_ext_lifo.WriteNextEntry(libnx_state); press_state |= static_cast<u64>(pad_state.npad_buttons.raw); } @@ -1060,6 +1075,7 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) { // TODO: Remove this once abstract pad is emulated properly const auto aruid_index = npad_resource.GetIndexFromAruid(aruid); for (auto& controller : controller_data[aruid_index]) { + controller.is_active = false; controller.is_connected = false; controller.shared_memory = nullptr; } @@ -1070,11 +1086,14 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) { void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, std::recursive_mutex* shared_mutex, std::shared_ptr<HandheldConfig> handheld_config, + Kernel::KEvent* input_event, std::mutex* input_mutex, std::shared_ptr<Service::Set::ISystemSettingsServer> settings) { applet_resource_holder.applet_resource = resource; applet_resource_holder.shared_mutex = shared_mutex; applet_resource_holder.shared_npad_resource = &npad_resource; applet_resource_holder.handheld_config = handheld_config; + applet_resource_holder.input_event = input_event; + applet_resource_holder.input_mutex = input_mutex; vibration_handler.SetSettingsService(settings); diff --git a/src/hid_core/resources/npad/npad.h b/src/hid_core/resources/npad/npad.h index 3b1a69e7f..c63488346 100644 --- a/src/hid_core/resources/npad/npad.h +++ b/src/hid_core/resources/npad/npad.h @@ -131,6 +131,7 @@ public: void SetNpadExternals(std::shared_ptr<AppletResource> resource, std::recursive_mutex* shared_mutex, std::shared_ptr<HandheldConfig> handheld_config, + Kernel::KEvent* input_event, std::mutex* input_mutex, std::shared_ptr<Service::Set::ISystemSettingsServer> settings); AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); @@ -163,6 +164,7 @@ private: NpadInternalState* shared_memory = nullptr; Core::HID::EmulatedController* device = nullptr; + bool is_active{}; bool is_connected{}; // Dual joycons can have only one side connected @@ -204,9 +206,6 @@ private: std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads; NpadVibration vibration_handler{}; - Kernel::KEvent* input_event{nullptr}; - std::mutex* input_mutex{nullptr}; - std::atomic<u64> press_state{}; std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax> controller_data{}; diff --git a/src/hid_core/resources/npad/npad_resource.cpp b/src/hid_core/resources/npad/npad_resource.cpp index ea9fc14ed..8dd86b58e 100644 --- a/src/hid_core/resources/npad/npad_resource.cpp +++ b/src/hid_core/resources/npad/npad_resource.cpp @@ -72,6 +72,12 @@ void NPadResource::UnregisterAppletResourceUserId(u64 aruid) { state[aruid_index] = {}; registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete; } + + for (std::size_t i = 0; i < AruidIndexMax; i++) { + if (registration_list.flag[i] == RegistrationStatus::Initialized) { + active_data_aruid = registration_list.aruid[i]; + } + } } void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) { diff --git a/src/hid_core/resources/six_axis/six_axis.cpp b/src/hid_core/resources/six_axis/six_axis.cpp index abb6fd152..b407a5c76 100644 --- a/src/hid_core/resources/six_axis/six_axis.cpp +++ b/src/hid_core/resources/six_axis/six_axis.cpp @@ -28,142 +28,148 @@ void SixAxis::OnRelease() {} void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { std::scoped_lock shared_lock{*shared_mutex}; - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - if (data == nullptr || !data->flag.is_assigned) { - return; - } - - if (!IsControllerActivated()) { - return; - } + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { + const auto* data = applet_resource->GetAruidDataByIndex(aruid_index); - for (std::size_t i = 0; i < controller_data.size(); ++i) { - NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i]; - auto& controller = controller_data[i]; - const auto& controller_type = controller.device->GetNpadStyleIndex(); - - if (controller_type == Core::HID::NpadStyleIndex::None || - !controller.device->IsConnected()) { + if (data == nullptr || !data->flag.is_assigned) { continue; } - const auto& motion_state = controller.device->GetMotions(); - auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; - auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; - auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; - auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; - auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; - auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; - - auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo; - auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo; - auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo; - auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo; - auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo; - auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo; - - // Clear previous state - sixaxis_fullkey_state = {}; - sixaxis_handheld_state = {}; - sixaxis_dual_left_state = {}; - sixaxis_dual_right_state = {}; - sixaxis_left_lifo_state = {}; - sixaxis_right_lifo_state = {}; - - if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { - controller.sixaxis_at_rest = true; - for (std::size_t e = 0; e < motion_state.size(); ++e) { - controller.sixaxis_at_rest = - controller.sixaxis_at_rest && motion_state[e].is_at_rest; - } + if (!IsControllerActivated()) { + return; } - const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, - const Core::HID::ControllerMotion& hid_state) { - using namespace std::literals::chrono_literals; - static constexpr Core::HID::SixAxisSensorState default_motion_state = { - .delta_time = std::chrono::nanoseconds(5ms).count(), - .accel = {0, 0, -1.0f}, - .orientation = - { - Common::Vec3f{1.0f, 0, 0}, - Common::Vec3f{0, 1.0f, 0}, - Common::Vec3f{0, 0, 1.0f}, - }, - .attribute = {1}, + for (std::size_t i = 0; i < controller_data.size(); ++i) { + NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i]; + auto& controller = controller_data[i]; + const auto& controller_type = controller.device->GetNpadStyleIndex(); + + if (!data->flag.enable_six_axis_sensor) { + continue; + } + + if (controller_type == Core::HID::NpadStyleIndex::None || + !controller.device->IsConnected()) { + continue; + } + + const auto& motion_state = controller.device->GetMotions(); + auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; + auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; + auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; + auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; + auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; + auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; + + auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo; + auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo; + auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo; + auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo; + auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo; + auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo; + + // Clear previous state + sixaxis_fullkey_state = {}; + sixaxis_handheld_state = {}; + sixaxis_dual_left_state = {}; + sixaxis_dual_right_state = {}; + sixaxis_left_lifo_state = {}; + sixaxis_right_lifo_state = {}; + + if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { + controller.sixaxis_at_rest = true; + for (std::size_t e = 0; e < motion_state.size(); ++e) { + controller.sixaxis_at_rest = + controller.sixaxis_at_rest && motion_state[e].is_at_rest; + } + } + + const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, + const Core::HID::ControllerMotion& hid_state) { + using namespace std::literals::chrono_literals; + static constexpr Core::HID::SixAxisSensorState default_motion_state = { + .delta_time = std::chrono::nanoseconds(5ms).count(), + .accel = {0, 0, -1.0f}, + .orientation = + { + Common::Vec3f{1.0f, 0, 0}, + Common::Vec3f{0, 1.0f, 0}, + Common::Vec3f{0, 0, 1.0f}, + }, + .attribute = {1}, + }; + if (!controller.sixaxis_sensor_enabled) { + state = default_motion_state; + return; + } + if (!Settings::values.motion_enabled.GetValue()) { + state = default_motion_state; + return; + } + state.attribute.is_connected.Assign(1); + state.delta_time = std::chrono::nanoseconds(5ms).count(); + state.accel = hid_state.accel; + state.gyro = hid_state.gyro; + state.rotation = hid_state.rotation; + state.orientation = hid_state.orientation; }; - if (!controller.sixaxis_sensor_enabled) { - state = default_motion_state; - return; + + switch (controller_type) { + case Core::HID::NpadStyleIndex::None: + ASSERT(false); + break; + case Core::HID::NpadStyleIndex::Fullkey: + set_motion_state(sixaxis_fullkey_state, motion_state[0]); + break; + case Core::HID::NpadStyleIndex::Handheld: + set_motion_state(sixaxis_handheld_state, motion_state[0]); + break; + case Core::HID::NpadStyleIndex::JoyconDual: + set_motion_state(sixaxis_dual_left_state, motion_state[0]); + set_motion_state(sixaxis_dual_right_state, motion_state[1]); + break; + case Core::HID::NpadStyleIndex::JoyconLeft: + set_motion_state(sixaxis_left_lifo_state, motion_state[0]); + break; + case Core::HID::NpadStyleIndex::JoyconRight: + set_motion_state(sixaxis_right_lifo_state, motion_state[1]); + break; + case Core::HID::NpadStyleIndex::Pokeball: + using namespace std::literals::chrono_literals; + set_motion_state(sixaxis_fullkey_state, motion_state[0]); + sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); + break; + default: + break; } - if (!Settings::values.motion_enabled.GetValue()) { - state = default_motion_state; - return; + + sixaxis_fullkey_state.sampling_number = + sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_handheld_state.sampling_number = + sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_dual_left_state.sampling_number = + sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_dual_right_state.sampling_number = + sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_left_lifo_state.sampling_number = + sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; + sixaxis_right_lifo_state.sampling_number = + sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; + + if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { + // This buffer only is updated on handheld on HW + sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state); + } else { + // Handheld doesn't update this buffer on HW + sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state); } - state.attribute.is_connected.Assign(1); - state.delta_time = std::chrono::nanoseconds(5ms).count(); - state.accel = hid_state.accel; - state.gyro = hid_state.gyro; - state.rotation = hid_state.rotation; - state.orientation = hid_state.orientation; - }; - - switch (controller_type) { - case Core::HID::NpadStyleIndex::None: - ASSERT(false); - break; - case Core::HID::NpadStyleIndex::Fullkey: - set_motion_state(sixaxis_fullkey_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::Handheld: - set_motion_state(sixaxis_handheld_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::JoyconDual: - set_motion_state(sixaxis_dual_left_state, motion_state[0]); - set_motion_state(sixaxis_dual_right_state, motion_state[1]); - break; - case Core::HID::NpadStyleIndex::JoyconLeft: - set_motion_state(sixaxis_left_lifo_state, motion_state[0]); - break; - case Core::HID::NpadStyleIndex::JoyconRight: - set_motion_state(sixaxis_right_lifo_state, motion_state[1]); - break; - case Core::HID::NpadStyleIndex::Pokeball: - using namespace std::literals::chrono_literals; - set_motion_state(sixaxis_fullkey_state, motion_state[0]); - sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); - break; - default: - break; - } - sixaxis_fullkey_state.sampling_number = - sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_handheld_state.sampling_number = - sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_dual_left_state.sampling_number = - sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_dual_right_state.sampling_number = - sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_left_lifo_state.sampling_number = - sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - sixaxis_right_lifo_state.sampling_number = - sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; - - if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { - // This buffer only is updated on handheld on HW - sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state); - } else { - // Handheld doesn't update this buffer on HW - sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state); + sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state); + sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state); + sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state); + sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state); } - - sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state); - sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state); - sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state); - sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state); } } diff --git a/src/hid_core/resources/touch_screen/gesture.cpp b/src/hid_core/resources/touch_screen/gesture.cpp index 0ecc0941f..eaa0cc7d0 100644 --- a/src/hid_core/resources/touch_screen/gesture.cpp +++ b/src/hid_core/resources/touch_screen/gesture.cpp @@ -1,366 +1,53 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later -#include "common/math_util.h" -#include "common/settings.h" -#include "core/frontend/emu_window.h" -#include "hid_core/frontend/emulated_console.h" -#include "hid_core/hid_core.h" -#include "hid_core/resources/applet_resource.h" -#include "hid_core/resources/shared_memory_format.h" #include "hid_core/resources/touch_screen/gesture.h" +#include "hid_core/resources/touch_screen/touch_screen_resource.h" namespace Service::HID { -// HW is around 700, value is set to 400 to make it easier to trigger with mouse -constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s -constexpr f32 angle_threshold = 0.015f; // Threshold in radians -constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels -constexpr f32 press_delay = 0.5f; // Time in seconds -constexpr f32 double_tap_delay = 0.35f; // Time in seconds -constexpr f32 Square(s32 num) { - return static_cast<f32>(num * num); -} +Gesture::Gesture(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {} -Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { - console = hid_core.GetEmulatedConsole(); -} Gesture::~Gesture() = default; -void Gesture::OnInit() { - std::scoped_lock shared_lock{*shared_mutex}; - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr || !data->flag.is_assigned) { - return; - } - - shared_memory = &data->shared_memory_format->gesture; - shared_memory->gesture_lifo.buffer_count = 0; - shared_memory->gesture_lifo.buffer_tail = 0; - force_update = true; -} - -void Gesture::OnRelease() {} - -void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - std::scoped_lock shared_lock{*shared_mutex}; - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); - - if (data == nullptr || !data->flag.is_assigned) { - return; - } - - shared_memory = &data->shared_memory_format->gesture; - - if (!IsControllerActivated()) { - shared_memory->gesture_lifo.buffer_count = 0; - shared_memory->gesture_lifo.buffer_tail = 0; - return; - } - - ReadTouchInput(); - - GestureProperties gesture = GetGestureProperties(); - f32 time_difference = - static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / - (1000 * 1000 * 1000); - - // Only update if necessary - if (!ShouldUpdateGesture(gesture, time_difference)) { - return; - } - - last_update_timestamp = shared_memory->gesture_lifo.timestamp; - UpdateGestureSharedMemory(gesture, time_difference); -} - -void Gesture::ReadTouchInput() { - if (!Settings::values.touchscreen.enabled) { - fingers = {}; - return; - } - - const auto touch_status = console->GetTouch(); - for (std::size_t id = 0; id < fingers.size(); ++id) { - fingers[id] = touch_status[id]; - } -} - -bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); - if (force_update) { - force_update = false; - return true; - } - - // Update if coordinates change - for (size_t id = 0; id < MAX_POINTS; id++) { - if (gesture.points[id] != last_gesture.points[id]) { - return true; - } - } - - // Update on press and hold event after 0.5 seconds - if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 && - time_difference > press_delay) { - return enable_press_and_tap; - } - - return false; -} - -void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { - GestureType type = GestureType::Idle; - GestureAttribute attributes{}; - - const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; - - // Reset next state to default - next_state.sampling_number = last_entry.sampling_number + 1; - next_state.delta = {}; - next_state.vel_x = 0; - next_state.vel_y = 0; - next_state.direction = GestureDirection::None; - next_state.rotation_angle = 0; - next_state.scale = 0; - - if (gesture.active_points > 0) { - if (last_gesture.active_points == 0) { - NewGesture(gesture, type, attributes); - } else { - UpdateExistingGesture(gesture, type, time_difference); - } - } else { - EndGesture(gesture, last_gesture, type, attributes, time_difference); - } - - // Apply attributes - next_state.detection_count = gesture.detection_count; - next_state.type = type; - next_state.attributes = attributes; - next_state.pos = gesture.mid_point; - next_state.point_count = static_cast<s32>(gesture.active_points); - next_state.points = gesture.points; - last_gesture = gesture; - - shared_memory->gesture_lifo.WriteNextEntry(next_state); -} - -void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, - GestureAttribute& attributes) { - const auto& last_entry = GetLastGestureEntry(); - - gesture.detection_count++; - type = GestureType::Touch; - - // New touch after cancel is not considered new - if (last_entry.type != GestureType::Cancel) { - attributes.is_new_touch.Assign(1); - enable_press_and_tap = true; - } -} - -void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, - f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); +Result Gesture::Activate() { + std::scoped_lock lock{mutex}; - // Promote to pan type if touch moved - for (size_t id = 0; id < MAX_POINTS; id++) { - if (gesture.points[id] != last_gesture.points[id]) { - type = GestureType::Pan; - break; - } + // TODO: Result result = CreateThread(); + Result result = ResultSuccess; + if (result.IsError()) { + return result; } - // Number of fingers changed cancel the last event and clear data - if (gesture.active_points != last_gesture.active_points) { - type = GestureType::Cancel; - enable_press_and_tap = false; - gesture.active_points = 0; - gesture.mid_point = {}; - gesture.points.fill({}); - return; - } - - // Calculate extra parameters of panning - if (type == GestureType::Pan) { - UpdatePanEvent(gesture, last_gesture, type, time_difference); - return; - } + result = touch_resource->ActivateGesture(); - // Promote to press type - if (last_entry.type == GestureType::Touch) { - type = GestureType::Press; + if (result.IsError()) { + // TODO: StopThread(); } -} - -void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, GestureAttribute& attributes, f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); - if (last_gesture_props.active_points != 0) { - switch (last_entry.type) { - case GestureType::Touch: - if (enable_press_and_tap) { - SetTapEvent(gesture, last_gesture_props, type, attributes); - return; - } - type = GestureType::Cancel; - force_update = true; - break; - case GestureType::Press: - case GestureType::Tap: - case GestureType::Swipe: - case GestureType::Pinch: - case GestureType::Rotate: - type = GestureType::Complete; - force_update = true; - break; - case GestureType::Pan: - EndPanEvent(gesture, last_gesture_props, type, time_difference); - break; - default: - break; - } - return; - } - if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) { - gesture.detection_count++; - } + return result; } -void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, GestureAttribute& attributes) { - type = GestureType::Tap; - gesture = last_gesture_props; - force_update = true; - f32 tap_time_difference = - static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); - last_tap_timestamp = last_update_timestamp; - if (tap_time_difference < double_tap_delay) { - attributes.is_double_tap.Assign(1); - } +Result Gesture::Activate(u64 aruid, u32 basic_gesture_id) { + std::scoped_lock lock{mutex}; + return touch_resource->ActivateGesture(aruid, basic_gesture_id); } -void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); +Result Gesture::Deactivate() { + std::scoped_lock lock{mutex}; + const auto result = touch_resource->DeactivateGesture(); - next_state.delta = gesture.mid_point - last_entry.pos; - next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; - next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; - last_pan_time_difference = time_difference; - - // Promote to pinch type - if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > - pinch_threshold) { - type = GestureType::Pinch; - next_state.scale = gesture.average_distance / last_gesture_props.average_distance; + if (result.IsError()) { + return result; } - const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / - (1 + (gesture.angle * last_gesture_props.angle))); - // Promote to rotate type - if (std::abs(angle_between_two_lines) > angle_threshold) { - type = GestureType::Rotate; - next_state.scale = 0; - next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; - } + // TODO: return StopThread(); + return ResultSuccess; } -void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, f32 time_difference) { - const auto& last_entry = GetLastGestureEntry(); - next_state.vel_x = - static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); - next_state.vel_y = - static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); - const f32 curr_vel = - std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); - - // Set swipe event with parameters - if (curr_vel > swipe_threshold) { - SetSwipeEvent(gesture, last_gesture_props, type); - return; - } - - // End panning without swipe - type = GestureType::Complete; - next_state.vel_x = 0; - next_state.vel_y = 0; - force_update = true; -} - -void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type) { - const auto& last_entry = GetLastGestureEntry(); - - type = GestureType::Swipe; - gesture = last_gesture_props; - force_update = true; - next_state.delta = last_entry.delta; - - if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { - if (next_state.delta.x > 0) { - next_state.direction = GestureDirection::Right; - return; - } - next_state.direction = GestureDirection::Left; - return; - } - if (next_state.delta.y > 0) { - next_state.direction = GestureDirection::Down; - return; - } - next_state.direction = GestureDirection::Up; -} - -const GestureState& Gesture::GetLastGestureEntry() const { - return shared_memory->gesture_lifo.ReadCurrentEntry().state; -} - -GestureProperties Gesture::GetGestureProperties() { - GestureProperties gesture; - std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; - const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), - [](const auto& finger) { return finger.pressed; }); - gesture.active_points = - static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); - - for (size_t id = 0; id < gesture.active_points; ++id) { - const auto& [active_x, active_y] = active_fingers[id].position; - gesture.points[id] = { - .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), - .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), - }; - - // Hack: There is no touch in docked but games still allow it - if (Settings::IsDockedMode()) { - gesture.points[id] = { - .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), - .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), - }; - } - - gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); - gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); - } - - for (size_t id = 0; id < gesture.active_points; ++id) { - const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + - Square(gesture.mid_point.y - gesture.points[id].y)); - gesture.average_distance += distance / static_cast<f32>(gesture.active_points); - } - - gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), - static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); - - gesture.detection_count = last_gesture.detection_count; - - return gesture; +Result Gesture::IsActive(bool& out_is_active) const { + out_is_active = touch_resource->IsGestureActive(); + return ResultSuccess; } } // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/gesture.h b/src/hid_core/resources/touch_screen/gesture.h index 32e9a8690..d92912bb6 100644 --- a/src/hid_core/resources/touch_screen/gesture.h +++ b/src/hid_core/resources/touch_screen/gesture.h @@ -1,87 +1,32 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once -#include <array> +#include <mutex> #include "common/common_types.h" -#include "hid_core/resources/controller_base.h" -#include "hid_core/resources/touch_screen/touch_types.h" - -namespace Core::HID { -class EmulatedConsole; -} +#include "core/hle/result.h" namespace Service::HID { -struct GestureSharedMemoryFormat; +class TouchResource; -class Gesture final : public ControllerBase { +/// Handles gesture request from HID interfaces +class Gesture { public: - explicit Gesture(Core::HID::HIDCore& hid_core_); - ~Gesture() override; + Gesture(std::shared_ptr<TouchResource> resource); + ~Gesture(); - // Called when the controller is initialized - void OnInit() override; + Result Activate(); + Result Activate(u64 aruid, u32 basic_gesture_id); - // When the controller is released - void OnRelease() override; + Result Deactivate(); - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + Result IsActive(bool& out_is_active) const; private: - // Reads input from all available input engines - void ReadTouchInput(); - - // Returns true if gesture state needs to be updated - bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); - - // Updates the shared memory to the next state - void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); - - // Initializes new gesture - void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); - - // Updates existing gesture state - void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference); - - // Terminates exiting gesture - void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, GestureAttribute& attributes, f32 time_difference); - - // Set current event to a tap event - void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, GestureAttribute& attributes); - - // Calculates and set the extra parameters related to a pan event - void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, f32 time_difference); - - // Terminates the pan event - void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type, f32 time_difference); - - // Set current event to a swipe event - void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, - GestureType& type); - - // Retrieves the last gesture entry, as indicated by shared memory indices. - [[nodiscard]] const GestureState& GetLastGestureEntry() const; - - // Returns the average distance, angle and middle point of the active fingers - GestureProperties GetGestureProperties(); - - GestureState next_state{}; - GestureSharedMemoryFormat* shared_memory; - Core::HID::EmulatedConsole* console = nullptr; - - std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; - GestureProperties last_gesture{}; - s64 last_update_timestamp{}; - s64 last_tap_timestamp{}; - f32 last_pan_time_difference{}; - bool force_update{false}; - bool enable_press_and_tap{false}; + mutable std::mutex mutex; + std::shared_ptr<TouchResource> touch_resource; }; + } // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/gesture_handler.cpp b/src/hid_core/resources/touch_screen/gesture_handler.cpp new file mode 100644 index 000000000..4fcaf6ecf --- /dev/null +++ b/src/hid_core/resources/touch_screen/gesture_handler.cpp @@ -0,0 +1,260 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/math_util.h" +#include "hid_core/resources/touch_screen/gesture_handler.h" + +namespace Service::HID { + +constexpr f32 Square(s32 num) { + return static_cast<f32>(num * num); +} + +GestureHandler::GestureHandler() {} + +GestureHandler::~GestureHandler() {} + +void GestureHandler::SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp) { + gesture = {}; + gesture.active_points = std::min(MaxPoints, static_cast<std::size_t>(count)); + + for (size_t id = 0; id < gesture.active_points; ++id) { + const auto& [active_x, active_y] = touch_state[id].position; + gesture.points[id] = { + .x = static_cast<s32>(active_x), + .y = static_cast<s32>(active_y), + }; + + gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); + gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); + } + + for (size_t id = 0; id < gesture.active_points; ++id) { + const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + + Square(gesture.mid_point.y - gesture.points[id].y)); + gesture.average_distance += distance / static_cast<f32>(gesture.active_points); + } + + gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), + static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); + + gesture.detection_count = last_gesture.detection_count; + + if (last_update_timestamp > timestamp) { + timestamp = last_tap_timestamp; + } + + time_difference = static_cast<f32>(timestamp - last_update_timestamp) / (1000 * 1000 * 1000); +} + +bool GestureHandler::NeedsUpdate() { + if (force_update) { + force_update = false; + return true; + } + + // Update if coordinates change + for (size_t id = 0; id < MaxPoints; id++) { + if (gesture.points[id] != last_gesture.points[id]) { + return true; + } + } + + // Update on press and hold event after 0.5 seconds + if (last_gesture_state.type == GestureType::Touch && last_gesture_state.point_count == 1 && + time_difference > PressDelay) { + return enable_press_and_tap; + } + + return false; +} + +void GestureHandler::UpdateGestureState(GestureState& next_state, s64 timestamp) { + last_update_timestamp = timestamp; + + GestureType type = GestureType::Idle; + GestureAttribute attributes{}; + + // Reset next state to default + next_state.sampling_number = last_gesture_state.sampling_number + 1; + next_state.delta = {}; + next_state.vel_x = 0; + next_state.vel_y = 0; + next_state.direction = GestureDirection::None; + next_state.rotation_angle = 0; + next_state.scale = 0; + + if (gesture.active_points > 0) { + if (last_gesture.active_points == 0) { + NewGesture(type, attributes); + } else { + UpdateExistingGesture(next_state, type); + } + } else { + EndGesture(next_state, type, attributes); + } + + // Apply attributes + next_state.detection_count = gesture.detection_count; + next_state.type = type; + next_state.attributes = attributes; + next_state.pos = gesture.mid_point; + next_state.point_count = static_cast<s32>(gesture.active_points); + next_state.points = gesture.points; + last_gesture = gesture; + last_gesture_state = next_state; +} + +void GestureHandler::NewGesture(GestureType& type, GestureAttribute& attributes) { + gesture.detection_count++; + type = GestureType::Touch; + + // New touch after cancel is not considered new + if (last_gesture_state.type != GestureType::Cancel) { + attributes.is_new_touch.Assign(1); + enable_press_and_tap = true; + } +} + +void GestureHandler::UpdateExistingGesture(GestureState& next_state, GestureType& type) { + // Promote to pan type if touch moved + for (size_t id = 0; id < MaxPoints; id++) { + if (gesture.points[id] != last_gesture.points[id]) { + type = GestureType::Pan; + break; + } + } + + // Number of fingers changed cancel the last event and clear data + if (gesture.active_points != last_gesture.active_points) { + type = GestureType::Cancel; + enable_press_and_tap = false; + gesture.active_points = 0; + gesture.mid_point = {}; + gesture.points.fill({}); + return; + } + + // Calculate extra parameters of panning + if (type == GestureType::Pan) { + UpdatePanEvent(next_state, type); + return; + } + + // Promote to press type + if (last_gesture_state.type == GestureType::Touch) { + type = GestureType::Press; + } +} + +void GestureHandler::EndGesture(GestureState& next_state, GestureType& type, + GestureAttribute& attributes) { + if (last_gesture.active_points != 0) { + switch (last_gesture_state.type) { + case GestureType::Touch: + if (enable_press_and_tap) { + SetTapEvent(type, attributes); + return; + } + type = GestureType::Cancel; + force_update = true; + break; + case GestureType::Press: + case GestureType::Tap: + case GestureType::Swipe: + case GestureType::Pinch: + case GestureType::Rotate: + type = GestureType::Complete; + force_update = true; + break; + case GestureType::Pan: + EndPanEvent(next_state, type); + break; + default: + break; + } + return; + } + if (last_gesture_state.type == GestureType::Complete || + last_gesture_state.type == GestureType::Cancel) { + gesture.detection_count++; + } +} + +void GestureHandler::SetTapEvent(GestureType& type, GestureAttribute& attributes) { + type = GestureType::Tap; + gesture = last_gesture; + force_update = true; + f32 tap_time_difference = + static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); + last_tap_timestamp = last_update_timestamp; + if (tap_time_difference < DoubleTapDelay) { + attributes.is_double_tap.Assign(1); + } +} + +void GestureHandler::UpdatePanEvent(GestureState& next_state, GestureType& type) { + next_state.delta = gesture.mid_point - last_gesture_state.pos; + next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; + next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; + last_pan_time_difference = time_difference; + + // Promote to pinch type + if (std::abs(gesture.average_distance - last_gesture.average_distance) > PinchThreshold) { + type = GestureType::Pinch; + next_state.scale = gesture.average_distance / last_gesture.average_distance; + } + + const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture.angle) / + (1 + (gesture.angle * last_gesture.angle))); + // Promote to rotate type + if (std::abs(angle_between_two_lines) > AngleThreshold) { + type = GestureType::Rotate; + next_state.scale = 0; + next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; + } +} + +void GestureHandler::EndPanEvent(GestureState& next_state, GestureType& type) { + next_state.vel_x = + static_cast<f32>(last_gesture_state.delta.x) / (last_pan_time_difference + time_difference); + next_state.vel_y = + static_cast<f32>(last_gesture_state.delta.y) / (last_pan_time_difference + time_difference); + const f32 curr_vel = + std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); + + // Set swipe event with parameters + if (curr_vel > SwipeThreshold) { + SetSwipeEvent(next_state, type); + return; + } + + // End panning without swipe + type = GestureType::Complete; + next_state.vel_x = 0; + next_state.vel_y = 0; + force_update = true; +} + +void GestureHandler::SetSwipeEvent(GestureState& next_state, GestureType& type) { + type = GestureType::Swipe; + gesture = last_gesture; + force_update = true; + next_state.delta = last_gesture_state.delta; + + if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { + if (next_state.delta.x > 0) { + next_state.direction = GestureDirection::Right; + return; + } + next_state.direction = GestureDirection::Left; + return; + } + if (next_state.delta.y > 0) { + next_state.direction = GestureDirection::Down; + return; + } + next_state.direction = GestureDirection::Up; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/gesture_handler.h b/src/hid_core/resources/touch_screen/gesture_handler.h new file mode 100644 index 000000000..fda2040c9 --- /dev/null +++ b/src/hid_core/resources/touch_screen/gesture_handler.h @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <span> + +#include "hid_core/resources/touch_screen/touch_types.h" + +namespace Service::HID { + +class GestureHandler { +public: + GestureHandler(); + ~GestureHandler(); + + void SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp); + + bool NeedsUpdate(); + void UpdateGestureState(GestureState& next_state, s64 timestamp); + +private: + // Initializes new gesture + void NewGesture(GestureType& type, GestureAttribute& attributes); + + // Updates existing gesture state + void UpdateExistingGesture(GestureState& next_state, GestureType& type); + + // Terminates exiting gesture + void EndGesture(GestureState& next_state, GestureType& type, GestureAttribute& attributes); + + // Set current event to a tap event + void SetTapEvent(GestureType& type, GestureAttribute& attributes); + + // Calculates and set the extra parameters related to a pan event + void UpdatePanEvent(GestureState& next_state, GestureType& type); + + // Terminates the pan event + void EndPanEvent(GestureState& next_state, GestureType& type); + + // Set current event to a swipe event + void SetSwipeEvent(GestureState& next_state, GestureType& type); + + GestureProperties gesture{}; + GestureProperties last_gesture{}; + GestureState last_gesture_state{}; + s64 last_update_timestamp{}; + s64 last_tap_timestamp{}; + f32 last_pan_time_difference{}; + f32 time_difference{}; + bool force_update{true}; + bool enable_press_and_tap{false}; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/gesture_types.h b/src/hid_core/resources/touch_screen/gesture_types.h deleted file mode 100644 index b4f034cd3..000000000 --- a/src/hid_core/resources/touch_screen/gesture_types.h +++ /dev/null @@ -1,77 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include <array> -#include "common/bit_field.h" -#include "common/common_types.h" -#include "common/point.h" - -namespace Service::HID { -static constexpr size_t MAX_FINGERS = 16; -static constexpr size_t MAX_POINTS = 4; - -// This is nn::hid::GestureType -enum class GestureType : u32 { - Idle, // Nothing touching the screen - Complete, // Set at the end of a touch event - Cancel, // Set when the number of fingers change - Touch, // A finger just touched the screen - Press, // Set if last type is touch and the finger hasn't moved - Tap, // Fast press then release - Pan, // All points moving together across the screen - Swipe, // Fast press movement and release of a single point - Pinch, // All points moving away/closer to the midpoint - Rotate, // All points rotating from the midpoint -}; - -// This is nn::hid::GestureDirection -enum class GestureDirection : u32 { - None, - Left, - Up, - Right, - Down, -}; - -// This is nn::hid::GestureAttribute -struct GestureAttribute { - union { - u32 raw{}; - - BitField<4, 1, u32> is_new_touch; - BitField<8, 1, u32> is_double_tap; - }; -}; -static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); - -// This is nn::hid::GestureState -struct GestureState { - s64 sampling_number{}; - s64 detection_count{}; - GestureType type{GestureType::Idle}; - GestureDirection direction{GestureDirection::None}; - Common::Point<s32> pos{}; - Common::Point<s32> delta{}; - f32 vel_x{}; - f32 vel_y{}; - GestureAttribute attributes{}; - f32 scale{}; - f32 rotation_angle{}; - s32 point_count{}; - std::array<Common::Point<s32>, 4> points{}; -}; -static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); - -struct GestureProperties { - std::array<Common::Point<s32>, MAX_POINTS> points{}; - std::size_t active_points{}; - Common::Point<s32> mid_point{}; - s64 detection_count{}; - u64 delta_time{}; - f32 average_distance{}; - f32 angle{}; -}; - -} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_screen.cpp b/src/hid_core/resources/touch_screen/touch_screen.cpp index 48d956c51..35efb1786 100644 --- a/src/hid_core/resources/touch_screen/touch_screen.cpp +++ b/src/hid_core/resources/touch_screen/touch_screen.cpp @@ -1,132 +1,119 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include <algorithm> -#include "common/common_types.h" -#include "common/settings.h" -#include "core/core_timing.h" -#include "core/frontend/emu_window.h" -#include "hid_core/frontend/emulated_console.h" -#include "hid_core/hid_core.h" -#include "hid_core/resources/applet_resource.h" -#include "hid_core/resources/shared_memory_format.h" +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "hid_core/hid_types.h" #include "hid_core/resources/touch_screen/touch_screen.h" +#include "hid_core/resources/touch_screen/touch_screen_resource.h" namespace Service::HID { -TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_) - : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width), - touchscreen_height(Layout::ScreenUndocked::Height) { - console = hid_core.GetEmulatedConsole(); -} +TouchScreen::TouchScreen(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {} TouchScreen::~TouchScreen() = default; -void TouchScreen::OnInit() {} +Result TouchScreen::Activate() { + std::scoped_lock lock{mutex}; -void TouchScreen::OnRelease() {} - -void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - const u64 aruid = applet_resource->GetActiveAruid(); - auto* data = applet_resource->GetAruidData(aruid); + // TODO: Result result = CreateThread(); + Result result = ResultSuccess; + if (result.IsError()) { + return result; + } - if (data == nullptr || !data->flag.is_assigned) { - return; + result = touch_resource->ActivateTouch(); + if (result.IsError()) { + // TODO: StopThread(); } - TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen; - shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); + return result; +} - if (!IsControllerActivated()) { - shared_memory.touch_screen_lifo.buffer_count = 0; - shared_memory.touch_screen_lifo.buffer_tail = 0; - return; - } +Result TouchScreen::Activate(u64 aruid) { + std::scoped_lock lock{mutex}; + return touch_resource->ActivateTouch(aruid); +} - const auto touch_status = console->GetTouch(); - for (std::size_t id = 0; id < MAX_FINGERS; id++) { - const auto& current_touch = touch_status[id]; - auto& finger = fingers[id]; - finger.id = current_touch.id; - - if (finger.attribute.start_touch) { - finger.attribute.raw = 0; - continue; - } - - if (finger.attribute.end_touch) { - finger.attribute.raw = 0; - finger.pressed = false; - continue; - } - - if (!finger.pressed && current_touch.pressed) { - // Ignore all touch fingers if disabled - if (!Settings::values.touchscreen.enabled) { - continue; - } - - finger.attribute.start_touch.Assign(1); - finger.pressed = true; - finger.position = current_touch.position; - continue; - } - - if (finger.pressed && !current_touch.pressed) { - finger.attribute.raw = 0; - finger.attribute.end_touch.Assign(1); - continue; - } - - // Only update position if touch is not on a special frame - finger.position = current_touch.position; - } +Result TouchScreen::Deactivate() { + std::scoped_lock lock{mutex}; + const auto result = touch_resource->DeactivateTouch(); - std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; - const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), - [](const auto& finger) { return finger.pressed; }); - const auto active_fingers_count = - static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); - - const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count()); - const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state; - - next_state.sampling_number = last_entry.sampling_number + 1; - next_state.entry_count = static_cast<s32>(active_fingers_count); - - for (std::size_t id = 0; id < MAX_FINGERS; ++id) { - auto& touch_entry = next_state.states[id]; - if (id < active_fingers_count) { - const auto& [active_x, active_y] = active_fingers[id].position; - touch_entry.position = { - .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)), - .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)), - }; - touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; - touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; - touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; - touch_entry.delta_time = timestamp - active_fingers[id].last_touch; - fingers[active_fingers[id].id].last_touch = timestamp; - touch_entry.finger = active_fingers[id].id; - touch_entry.attribute.raw = active_fingers[id].attribute.raw; - } else { - // Clear touch entry - touch_entry.attribute.raw = 0; - touch_entry.position = {}; - touch_entry.diameter_x = 0; - touch_entry.diameter_y = 0; - touch_entry.rotation_angle = 0; - touch_entry.delta_time = 0; - touch_entry.finger = 0; - } + if (result.IsError()) { + return result; } - shared_memory.touch_screen_lifo.WriteNextEntry(next_state); + // TODO: return StopThread(); + return ResultSuccess; +} + +Result TouchScreen::IsActive(bool& out_is_active) const { + out_is_active = touch_resource->IsTouchActive(); + return ResultSuccess; +} + +Result TouchScreen::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) { + std::scoped_lock lock{mutex}; + return touch_resource->SetTouchScreenAutoPilotState(auto_pilot_state); +} + +Result TouchScreen::UnsetTouchScreenAutoPilotState() { + std::scoped_lock lock{mutex}; + return touch_resource->UnsetTouchScreenAutoPilotState(); +} + +Result TouchScreen::RequestNextTouchInput() { + std::scoped_lock lock{mutex}; + return touch_resource->RequestNextTouchInput(); +} + +Result TouchScreen::RequestNextDummyInput() { + std::scoped_lock lock{mutex}; + return touch_resource->RequestNextDummyInput(); +} + +Result TouchScreen::ProcessTouchScreenAutoTune() { + std::scoped_lock lock{mutex}; + return touch_resource->ProcessTouchScreenAutoTune(); +} + +Result TouchScreen::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, + f32 point2_y) { + std::scoped_lock lock{mutex}; + touch_resource->SetTouchScreenMagnification(point1_x, point1_y, point2_x, point2_y); + return ResultSuccess; +} + +Result TouchScreen::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) { + std::scoped_lock lock{mutex}; + return touch_resource->SetTouchScreenResolution(width, height, aruid); +} + +Result TouchScreen::SetTouchScreenConfiguration( + const Core::HID::TouchScreenConfigurationForNx& mode, u64 aruid) { + std::scoped_lock lock{mutex}; + return touch_resource->SetTouchScreenConfiguration(mode, aruid); +} + +Result TouchScreen::GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode, + u64 aruid) const { + std::scoped_lock lock{mutex}; + return touch_resource->GetTouchScreenConfiguration(out_mode, aruid); +} + +Result TouchScreen::SetTouchScreenDefaultConfiguration( + const Core::HID::TouchScreenConfigurationForNx& mode) { + std::scoped_lock lock{mutex}; + return touch_resource->SetTouchScreenDefaultConfiguration(mode); +} + +Result TouchScreen::GetTouchScreenDefaultConfiguration( + Core::HID::TouchScreenConfigurationForNx& out_mode) const { + std::scoped_lock lock{mutex}; + return touch_resource->GetTouchScreenDefaultConfiguration(out_mode); } -void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { - touchscreen_width = width; - touchscreen_height = height; +void TouchScreen::OnTouchUpdate(u64 timestamp) { + std::scoped_lock lock{mutex}; + touch_resource->OnTouchUpdate(timestamp); } } // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_screen.h b/src/hid_core/resources/touch_screen/touch_screen.h index 4b3824742..2fcb6247f 100644 --- a/src/hid_core/resources/touch_screen/touch_screen.h +++ b/src/hid_core/resources/touch_screen/touch_screen.h @@ -1,43 +1,64 @@ -// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later #pragma once -#include <array> +#include <mutex> -#include "hid_core/hid_types.h" -#include "hid_core/resources/controller_base.h" -#include "hid_core/resources/touch_screen/touch_types.h" +#include "common/common_types.h" +#include "core/hle/result.h" namespace Core::HID { -class EmulatedConsole; -} // namespace Core::HID +struct TouchScreenConfigurationForNx; +} + +namespace Core::Timing { +struct EventType; +} namespace Service::HID { -struct TouchScreenSharedMemoryFormat; +class TouchResource; +struct AutoPilotState; -class TouchScreen final : public ControllerBase { +/// Handles touch request from HID interfaces +class TouchScreen { public: - explicit TouchScreen(Core::HID::HIDCore& hid_core_); - ~TouchScreen() override; + TouchScreen(std::shared_ptr<TouchResource> resource); + ~TouchScreen(); - // Called when the controller is initialized - void OnInit() override; + Result Activate(); + Result Activate(u64 aruid); - // When the controller is released - void OnRelease() override; + Result Deactivate(); - // When the controller is requesting an update for the shared memory - void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; + Result IsActive(bool& out_is_active) const; - void SetTouchscreenDimensions(u32 width, u32 height); + Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state); + Result UnsetTouchScreenAutoPilotState(); -private: - TouchScreenState next_state{}; - Core::HID::EmulatedConsole* console = nullptr; + Result RequestNextTouchInput(); + Result RequestNextDummyInput(); + + Result ProcessTouchScreenAutoTune(); + + Result SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y); + Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid); + + Result SetTouchScreenConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode, + u64 aruid); + Result GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode, + u64 aruid) const; - std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; - u32 touchscreen_width; - u32 touchscreen_height; + Result SetTouchScreenDefaultConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode); + Result GetTouchScreenDefaultConfiguration( + Core::HID::TouchScreenConfigurationForNx& out_mode) const; + + void OnTouchUpdate(u64 timestamp); + +private: + mutable std::mutex mutex; + std::shared_ptr<TouchResource> touch_resource; + std::shared_ptr<Core::Timing::EventType> touch_update_event; }; + } // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_screen_driver.cpp b/src/hid_core/resources/touch_screen/touch_screen_driver.cpp new file mode 100644 index 000000000..6a64c75b3 --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_screen_driver.cpp @@ -0,0 +1,114 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include <algorithm> +#include "common/settings.h" +#include "core/frontend/emu_window.h" +#include "hid_core/hid_core.h" +#include "hid_core/resources/touch_screen/touch_screen_driver.h" + +namespace Service::HID { + +TouchDriver::TouchDriver(Core::HID::HIDCore& hid_core) { + console = hid_core.GetEmulatedConsole(); +} + +TouchDriver::~TouchDriver() = default; + +Result TouchDriver::StartTouchSensor() { + is_running = true; + return ResultSuccess; +} + +Result TouchDriver::StopTouchSensor() { + is_running = false; + return ResultSuccess; +} + +bool TouchDriver::IsRunning() const { + return is_running; +} + +void TouchDriver::ProcessTouchScreenAutoTune() const { + // TODO +} + +Result TouchDriver::WaitForDummyInput() { + touch_status = {}; + return ResultSuccess; +} + +Result TouchDriver::WaitForInput() { + touch_status = {}; + const auto touch_input = console->GetTouch(); + for (std::size_t id = 0; id < touch_status.states.size(); id++) { + const auto& current_touch = touch_input[id]; + auto& finger = fingers[id]; + finger.id = current_touch.id; + + if (finger.attribute.start_touch) { + finger.attribute.raw = 0; + continue; + } + + if (finger.attribute.end_touch) { + finger.attribute.raw = 0; + finger.pressed = false; + continue; + } + + if (!finger.pressed && current_touch.pressed) { + finger.attribute.start_touch.Assign(1); + finger.pressed = true; + finger.position = current_touch.position; + continue; + } + + if (finger.pressed && !current_touch.pressed) { + finger.attribute.raw = 0; + finger.attribute.end_touch.Assign(1); + continue; + } + + // Only update position if touch is not on a special frame + finger.position = current_touch.position; + } + + std::array<Core::HID::TouchFinger, MaxFingers> active_fingers; + const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), + [](const auto& finger) { return finger.pressed; }); + const auto active_fingers_count = + static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); + + touch_status.entry_count = static_cast<s32>(active_fingers_count); + for (std::size_t id = 0; id < MaxFingers; ++id) { + auto& touch_entry = touch_status.states[id]; + if (id < active_fingers_count) { + const auto& [active_x, active_y] = active_fingers[id].position; + touch_entry.position = { + .x = static_cast<u16>(active_x * TouchSensorWidth), + .y = static_cast<u16>(active_y * TouchSensorHeight), + }; + touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; + touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; + touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; + touch_entry.finger = active_fingers[id].id; + touch_entry.attribute.raw = active_fingers[id].attribute.raw; + } + } + return ResultSuccess; +} + +void TouchDriver::GetNextTouchState(TouchScreenState& out_state) const { + out_state = touch_status; +} + +void TouchDriver::SetTouchMode(Core::HID::TouchScreenModeForNx mode) { + touch_mode = mode; +} + +Core::HID::TouchScreenModeForNx TouchDriver::GetTouchMode() const { + return touch_mode; +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_screen_driver.h b/src/hid_core/resources/touch_screen/touch_screen_driver.h new file mode 100644 index 000000000..ca7e4970e --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_screen_driver.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include "common/common_types.h" +#include "core/hle/result.h" +#include "hid_core/frontend/emulated_console.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/touch_screen/touch_types.h" + +namespace Core::HID { +class HIDCore; +} // namespace Core::HID + +namespace Service::HID { + +/// This handles all request to Ftm3bd56(TouchPanel) hardware +class TouchDriver { +public: + explicit TouchDriver(Core::HID::HIDCore& hid_core); + ~TouchDriver(); + + Result StartTouchSensor(); + Result StopTouchSensor(); + bool IsRunning() const; + + void ProcessTouchScreenAutoTune() const; + + Result WaitForDummyInput(); + Result WaitForInput(); + + void GetNextTouchState(TouchScreenState& out_state) const; + + void SetTouchMode(Core::HID::TouchScreenModeForNx mode); + Core::HID::TouchScreenModeForNx GetTouchMode() const; + +private: + bool is_running{}; + TouchScreenState touch_status{}; + Core::HID::TouchFingerState fingers{}; + Core::HID::TouchScreenModeForNx touch_mode{}; + + Core::HID::EmulatedConsole* console = nullptr; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.cpp b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp new file mode 100644 index 000000000..c39321915 --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_screen_resource.cpp @@ -0,0 +1,579 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "common/logging/log.h" +#include "core/core_timing.h" +#include "core/hle/kernel/k_event.h" +#include "core/hle/kernel/k_shared_memory.h" +#include "core/hle/service/set/system_settings_server.h" +#include "core/hle/service/sm/sm.h" +#include "hid_core/hid_result.h" +#include "hid_core/resources/applet_resource.h" +#include "hid_core/resources/shared_memory_format.h" +#include "hid_core/resources/touch_screen/touch_screen_driver.h" +#include "hid_core/resources/touch_screen/touch_screen_resource.h" + +namespace Service::HID { +constexpr auto GestureUpdatePeriod = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) + +TouchResource::TouchResource(Core::System& system_) : system{system_} { + m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys"); +} + +TouchResource::~TouchResource() { + Finalize(); +}; + +Result TouchResource::ActivateTouch() { + if (global_ref_counter == std::numeric_limits<s32>::max() - 1 || + touch_ref_counter == std::numeric_limits<s32>::max() - 1) { + return ResultTouchOverflow; + } + + if (global_ref_counter == 0) { + std::scoped_lock lock{*shared_mutex}; + + const auto result = touch_driver->StartTouchSensor(); + if (result.IsError()) { + return result; + } + + is_initalized = true; + system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod, + timer_event); + current_touch_state = {}; + ReadTouchInput(); + gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, + 0); + } + + Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard}; + m_set_sys->GetTouchScreenMode(touch_mode); + default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode); + + global_ref_counter++; + touch_ref_counter++; + return ResultSuccess; +} + +Result TouchResource::ActivateTouch(u64 aruid) { + std::scoped_lock lock{*shared_mutex}; + + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); + TouchAruidData& touch_data = aruid_data[aruid_index]; + + if (applet_data == nullptr || !applet_data->flag.is_assigned) { + touch_data = {}; + continue; + } + + const u64 aruid_id = applet_data->aruid; + if (touch_data.aruid != aruid_id) { + touch_data = {}; + touch_data.aruid = aruid_id; + } + + if (aruid != aruid_id) { + continue; + } + + auto& touch_shared = applet_data->shared_memory_format->touch_screen; + + if (touch_shared.touch_screen_lifo.buffer_count == 0) { + StorePreviousTouchState(previous_touch_state, touch_data.finger_map, + current_touch_state, + applet_data->flag.enable_touchscreen.Value() != 0); + touch_shared.touch_screen_lifo.WriteNextEntry(previous_touch_state); + } + } + return ResultSuccess; +} + +Result TouchResource::ActivateGesture() { + if (global_ref_counter == std::numeric_limits<s32>::max() - 1 || + gesture_ref_counter == std::numeric_limits<s32>::max() - 1) { + return ResultGestureOverflow; + } + + // Initialize first instance + if (global_ref_counter == 0) { + const auto result = touch_driver->StartTouchSensor(); + if (result.IsError()) { + return result; + } + + is_initalized = true; + system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod, + timer_event); + current_touch_state = {}; + ReadTouchInput(); + gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, + 0); + } + + global_ref_counter++; + gesture_ref_counter++; + return ResultSuccess; +} + +Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) { + std::scoped_lock lock{*shared_mutex}; + + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); + TouchAruidData& touch_data = aruid_data[aruid_index]; + + if (applet_data == nullptr || !applet_data->flag.is_assigned) { + touch_data = {}; + continue; + } + + const u64 aruid_id = applet_data->aruid; + if (touch_data.aruid != aruid_id) { + touch_data = {}; + touch_data.aruid = aruid_id; + } + + if (aruid != aruid_id) { + continue; + } + + auto& gesture_shared = applet_data->shared_memory_format->gesture; + if (touch_data.basic_gesture_id != basic_gesture_id) { + gesture_shared.gesture_lifo.buffer_count = 0; + } + + if (gesture_shared.gesture_lifo.buffer_count == 0) { + touch_data.basic_gesture_id = basic_gesture_id; + + gesture_shared.gesture_lifo.WriteNextEntry(gesture_state); + } + } + + return ResultSuccess; +} + +Result TouchResource::DeactivateTouch() { + if (touch_ref_counter == 0 || global_ref_counter == 0) { + return ResultTouchNotInitialized; + } + + global_ref_counter--; + touch_ref_counter--; + + if (touch_ref_counter + global_ref_counter != 0) { + return ResultSuccess; + } + + return Finalize(); +} + +Result TouchResource::DeactivateGesture() { + if (gesture_ref_counter == 0 || global_ref_counter == 0) { + return ResultGestureNotInitialized; + } + + global_ref_counter--; + gesture_ref_counter--; + + if (touch_ref_counter + global_ref_counter != 0) { + return ResultSuccess; + } + + return Finalize(); +} + +bool TouchResource::IsTouchActive() const { + return touch_ref_counter != 0; +} + +bool TouchResource::IsGestureActive() const { + return gesture_ref_counter != 0; +} + +void TouchResource::SetTouchDriver(std::shared_ptr<TouchDriver> driver) { + touch_driver = driver; +} + +void TouchResource::SetAppletResource(std::shared_ptr<AppletResource> shared, + std::recursive_mutex* mutex) { + applet_resource = shared; + shared_mutex = mutex; +} + +void TouchResource::SetInputEvent(Kernel::KEvent* event, std::mutex* mutex) { + input_event = event; + input_mutex = mutex; +} + +void TouchResource::SetHandheldConfig(std::shared_ptr<HandheldConfig> config) { + handheld_config = config; +} + +void TouchResource::SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event) { + timer_event = event; +} + +Result TouchResource::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) { + if (global_ref_counter == 0) { + return ResultTouchNotInitialized; + } + + if (!is_auto_pilot_initialized) { + is_auto_pilot_initialized = true; + auto_pilot = {}; + } + + TouchScreenState state = { + .entry_count = static_cast<s32>(auto_pilot_state.count), + .states = auto_pilot_state.state, + }; + + SanitizeInput(state); + + auto_pilot.count = state.entry_count; + auto_pilot.state = state.states; + return ResultSuccess; +} + +Result TouchResource::UnsetTouchScreenAutoPilotState() { + if (global_ref_counter == 0) { + return ResultTouchNotInitialized; + } + + is_auto_pilot_initialized = false; + auto_pilot = {}; + return ResultSuccess; +} + +Result TouchResource::RequestNextTouchInput() { + if (global_ref_counter == 0) { + return ResultTouchNotInitialized; + } + + if (handheld_config->is_handheld_hid_enabled) { + const Result result = touch_driver->WaitForInput(); + if (result.IsError()) { + return result; + } + } + + is_initalized = true; + return ResultSuccess; +} + +Result TouchResource::RequestNextDummyInput() { + if (global_ref_counter == 0) { + return ResultTouchNotInitialized; + } + + if (handheld_config->is_handheld_hid_enabled) { + const Result result = touch_driver->WaitForDummyInput(); + if (result.IsError()) { + return result; + } + } + + is_initalized = false; + return ResultSuccess; +} + +Result TouchResource::ProcessTouchScreenAutoTune() { + touch_driver->ProcessTouchScreenAutoTune(); + return ResultSuccess; +} + +void TouchResource::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, + f32 point2_y) { + offset = { + .x = point1_x, + .y = point1_y, + }; + magnification = { + .x = point2_x, + .y = point2_y, + }; +} + +Result TouchResource::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) { + std::scoped_lock lock{*shared_mutex}; + + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); + TouchAruidData& data = aruid_data[aruid_index]; + + if (!applet_data->flag.is_assigned) { + continue; + } + if (aruid != data.aruid) { + continue; + } + data.resolution_width = static_cast<u16>(width); + data.resolution_height = static_cast<u16>(height); + } + + return ResultSuccess; +} + +Result TouchResource::SetTouchScreenConfiguration( + const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid) { + std::scoped_lock lock{*shared_mutex}; + + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); + TouchAruidData& data = aruid_data[aruid_index]; + + if (applet_data == nullptr || !applet_data->flag.is_assigned) { + continue; + } + if (aruid != data.aruid) { + continue; + } + data.finger_map.touch_mode = touch_configuration.mode; + } + + return ResultSuccess; +} + +Result TouchResource::GetTouchScreenConfiguration( + Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const { + std::scoped_lock lock{*shared_mutex}; + + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); + const TouchAruidData& data = aruid_data[aruid_index]; + + if (applet_data == nullptr || !applet_data->flag.is_assigned) { + continue; + } + if (aruid != data.aruid) { + continue; + } + out_touch_configuration.mode = data.finger_map.touch_mode; + } + + return ResultSuccess; +} + +Result TouchResource::SetTouchScreenDefaultConfiguration( + const Core::HID::TouchScreenConfigurationForNx& touch_configuration) { + default_touch_screen_mode = touch_configuration.mode; + return ResultSuccess; +} + +Result TouchResource::GetTouchScreenDefaultConfiguration( + Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const { + out_touch_configuration.mode = default_touch_screen_mode; + return ResultSuccess; +} + +Result TouchResource::Finalize() { + is_auto_pilot_initialized = false; + auto_pilot = {}; + system.CoreTiming().UnscheduleEvent(timer_event); + + const auto result = touch_driver->StopTouchSensor(); + if (result.IsError()) { + return result; + } + + is_initalized = false; + return ResultSuccess; +} + +void TouchResource::StorePreviousTouchState(TouchScreenState& out_previous_touch, + TouchFingerMap& out_finger_map, + const TouchScreenState& current_touch, + bool is_touch_enabled) const { + s32 finger_count{}; + + if (is_touch_enabled) { + finger_count = current_touch.entry_count; + if (finger_count < 1) { + out_finger_map.finger_count = 0; + out_finger_map.finger_ids = {}; + out_previous_touch.sampling_number = current_touch.sampling_number; + out_previous_touch.entry_count = 0; + out_previous_touch.states = {}; + return; + } + for (std::size_t i = 0; i < static_cast<u32>(finger_count); i++) { + out_finger_map.finger_ids[i] = current_touch.states[i].finger; + out_previous_touch.states[i] = current_touch.states[i]; + } + out_finger_map.finger_count = finger_count; + return; + } + + if (!is_touch_enabled && out_finger_map.finger_count > 0 && current_touch.entry_count > 0) { + // TODO + } + + // Zero out unused entries + for (std::size_t i = finger_count; i < MaxFingers; i++) { + out_finger_map.finger_ids[i] = 0; + out_previous_touch.states[i] = {}; + } + + out_previous_touch.sampling_number = current_touch.sampling_number; + out_previous_touch.entry_count = finger_count; +} + +void TouchResource::ReadTouchInput() { + previous_touch_state = current_touch_state; + + if (!is_initalized || !handheld_config->is_handheld_hid_enabled || !touch_driver->IsRunning()) { + touch_driver->WaitForDummyInput(); + } else { + touch_driver->WaitForInput(); + } + + touch_driver->GetNextTouchState(current_touch_state); + SanitizeInput(current_touch_state); + current_touch_state.sampling_number = sample_number; + sample_number++; + + if (is_auto_pilot_initialized && current_touch_state.entry_count == 0) { + const std::size_t finger_count = static_cast<std::size_t>(auto_pilot.count); + current_touch_state.entry_count = static_cast<s32>(finger_count); + for (std::size_t i = 0; i < finger_count; i++) { + current_touch_state.states[i] = auto_pilot.state[i]; + } + + std::size_t index = 0; + for (std::size_t i = 0; i < finger_count; i++) { + if (auto_pilot.state[i].attribute.end_touch) { + continue; + } + auto_pilot.state[i].attribute.raw = 0; + auto_pilot.state[index] = auto_pilot.state[i]; + index++; + } + + auto_pilot.count = index; + for (std::size_t i = index; i < auto_pilot.state.size(); i++) { + auto_pilot.state[i] = {}; + } + } + + for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) { + auto& state = current_touch_state.states[i]; + state.position.x = static_cast<u32>((magnification.y * static_cast<f32>(state.position.x)) + + (offset.x * static_cast<f32>(TouchSensorWidth))); + state.position.y = static_cast<u32>((magnification.y * static_cast<f32>(state.position.y)) + + (offset.x * static_cast<f32>(TouchSensorHeight))); + state.diameter_x = static_cast<u32>(magnification.x * static_cast<f32>(state.diameter_x)); + state.diameter_y = static_cast<u32>(magnification.y * static_cast<f32>(state.diameter_y)); + } + + std::size_t index = 0; + for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) { + const auto& old_state = current_touch_state.states[i]; + auto& state = current_touch_state.states[index]; + if ((TouchSensorWidth <= old_state.position.x) || + (TouchSensorHeight <= old_state.position.y)) { + continue; + } + state = old_state; + index++; + } + current_touch_state.entry_count = static_cast<s32>(index); + + SanitizeInput(current_touch_state); + + std::scoped_lock lock{*input_mutex}; + if (current_touch_state.entry_count == previous_touch_state.entry_count) { + if (current_touch_state.entry_count < 1) { + return; + } + bool has_moved = false; + for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); + i++) { + s32 delta_x = std::abs(static_cast<s32>(current_touch_state.states[i].position.x) - + static_cast<s32>(previous_touch_state.states[i].position.x)); + s32 delta_y = std::abs(static_cast<s32>(current_touch_state.states[i].position.y) - + static_cast<s32>(previous_touch_state.states[i].position.y)); + if (delta_x > 1 || delta_y > 1) { + has_moved = true; + } + } + if (!has_moved) { + return; + } + } + + input_event->Signal(); +} + +void TouchResource::OnTouchUpdate(s64 timestamp) { + if (global_ref_counter == 0) { + return; + } + + ReadTouchInput(); + gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, + timestamp); + + std::scoped_lock lock{*shared_mutex}; + + for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { + const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); + TouchAruidData& data = aruid_data[aruid_index]; + + if (applet_data == nullptr || !applet_data->flag.is_assigned) { + data = {}; + continue; + } + + if (data.aruid != applet_data->aruid) { + data = {}; + data.aruid = applet_data->aruid; + } + + if (gesture_ref_counter != 0) { + if (!applet_data->flag.enable_touchscreen) { + gesture_state = {}; + } + if (gesture_handler.NeedsUpdate()) { + gesture_handler.UpdateGestureState(gesture_state, timestamp); + auto& gesture_shared = applet_data->shared_memory_format->gesture; + gesture_shared.gesture_lifo.WriteNextEntry(gesture_state); + } + } + + if (touch_ref_counter != 0) { + auto touch_mode = data.finger_map.touch_mode; + if (touch_mode == Core::HID::TouchScreenModeForNx::UseSystemSetting) { + touch_mode = default_touch_screen_mode; + } + + if (applet_resource->GetActiveAruid() == applet_data->aruid && + touch_mode != Core::HID::TouchScreenModeForNx::UseSystemSetting && is_initalized && + handheld_config->is_handheld_hid_enabled && touch_driver->IsRunning()) { + touch_driver->SetTouchMode(touch_mode); + } + + auto& touch_shared = applet_data->shared_memory_format->touch_screen; + StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, + applet_data->flag.enable_touchscreen.As<bool>()); + touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state); + } + } +} + +void TouchResource::SanitizeInput(TouchScreenState& state) const { + for (std::size_t i = 0; i < static_cast<std::size_t>(state.entry_count); i++) { + auto& entry = state.states[i]; + entry.position.x = + std::clamp(entry.position.x, TouchBorders, TouchSensorWidth - TouchBorders - 1); + entry.position.y = + std::clamp(entry.position.y, TouchBorders, TouchSensorHeight - TouchBorders - 1); + entry.diameter_x = std::clamp(entry.diameter_x, 0u, TouchSensorWidth - MaxTouchDiameter); + entry.diameter_y = std::clamp(entry.diameter_y, 0u, TouchSensorHeight - MaxTouchDiameter); + entry.rotation_angle = + std::clamp(entry.rotation_angle, -MaxRotationAngle, MaxRotationAngle); + } +} + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_screen_resource.h b/src/hid_core/resources/touch_screen/touch_screen_resource.h new file mode 100644 index 000000000..095cddd76 --- /dev/null +++ b/src/hid_core/resources/touch_screen/touch_screen_resource.h @@ -0,0 +1,126 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include <array> +#include <mutex> + +#include "common/common_types.h" +#include "common/point.h" +#include "core/hle/result.h" +#include "hid_core/hid_types.h" +#include "hid_core/resources/touch_screen/gesture_handler.h" +#include "hid_core/resources/touch_screen/touch_types.h" + +namespace Core { +class System; +} + +namespace Core::Timing { +struct EventType; +} + +namespace Kernel { +class KEvent; +} // namespace Kernel + +namespace Service::Set { +class ISystemSettingsServer; +} + +namespace Service::HID { +class AppletResource; +class TouchSharedMemoryManager; +class TouchDriver; +struct HandheldConfig; + +class TouchResource { +public: + TouchResource(Core::System& system_); + ~TouchResource(); + + Result ActivateTouch(); + Result ActivateTouch(u64 aruid); + + Result ActivateGesture(); + Result ActivateGesture(u64 aruid, u32 basic_gesture_id); + + Result DeactivateTouch(); + Result DeactivateGesture(); + + bool IsTouchActive() const; + bool IsGestureActive() const; + + void SetTouchDriver(std::shared_ptr<TouchDriver> driver); + void SetAppletResource(std::shared_ptr<AppletResource> shared, std::recursive_mutex* mutex); + void SetInputEvent(Kernel::KEvent* event, std::mutex* mutex); + void SetHandheldConfig(std::shared_ptr<HandheldConfig> config); + void SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event); + + Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state); + Result UnsetTouchScreenAutoPilotState(); + + Result RequestNextTouchInput(); + Result RequestNextDummyInput(); + + Result ProcessTouchScreenAutoTune(); + void SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y); + Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid); + + Result SetTouchScreenConfiguration( + const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid); + Result GetTouchScreenConfiguration( + Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const; + + Result SetTouchScreenDefaultConfiguration( + const Core::HID::TouchScreenConfigurationForNx& touch_configuration); + Result GetTouchScreenDefaultConfiguration( + Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const; + + void OnTouchUpdate(s64 timestamp); + +private: + Result Finalize(); + + void StorePreviousTouchState(TouchScreenState& out_previous_touch, + TouchFingerMap& out_finger_map, + const TouchScreenState& current_touch, + bool is_touch_enabled) const; + void ReadTouchInput(); + + void SanitizeInput(TouchScreenState& state) const; + + s32 global_ref_counter{}; + s32 gesture_ref_counter{}; + s32 touch_ref_counter{}; + bool is_initalized{}; + u64 sample_number{}; + + // External resources + std::shared_ptr<Core::Timing::EventType> timer_event{nullptr}; + std::shared_ptr<TouchDriver> touch_driver{nullptr}; + std::shared_ptr<AppletResource> applet_resource{nullptr}; + std::recursive_mutex* shared_mutex{nullptr}; + std::shared_ptr<HandheldConfig> handheld_config{nullptr}; + Kernel::KEvent* input_event{nullptr}; + std::mutex* input_mutex{nullptr}; + + // Internal state + TouchScreenState current_touch_state{}; + TouchScreenState previous_touch_state{}; + GestureState gesture_state{}; + bool is_auto_pilot_initialized{}; + AutoPilotState auto_pilot{}; + GestureHandler gesture_handler{}; + std::array<TouchAruidData, 0x20> aruid_data{}; + Common::Point<f32> magnification{1.0f, 1.0f}; + Common::Point<f32> offset{0.0f, 0.0f}; + Core::HID::TouchScreenModeForNx default_touch_screen_mode{ + Core::HID::TouchScreenModeForNx::Finger}; + + Core::System& system; + std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; +}; + +} // namespace Service::HID diff --git a/src/hid_core/resources/touch_screen/touch_types.h b/src/hid_core/resources/touch_screen/touch_types.h index 97ee847da..362088939 100644 --- a/src/hid_core/resources/touch_screen/touch_types.h +++ b/src/hid_core/resources/touch_screen/touch_types.h @@ -13,8 +13,20 @@ #include "hid_core/hid_types.h" namespace Service::HID { -static constexpr std::size_t MAX_FINGERS = 16; -static constexpr size_t MAX_POINTS = 4; +constexpr std::size_t MaxFingers = 16; +constexpr std::size_t MaxPoints = 4; +constexpr u32 TouchSensorWidth = 1280; +constexpr u32 TouchSensorHeight = 720; +constexpr s32 MaxRotationAngle = 270; +constexpr u32 MaxTouchDiameter = 30; +constexpr u32 TouchBorders = 15; + +// HW is around 700, value is set to 400 to make it easier to trigger with mouse +constexpr f32 SwipeThreshold = 400.0f; // Threshold in pixels/s +constexpr f32 AngleThreshold = 0.015f; // Threshold in radians +constexpr f32 PinchThreshold = 0.5f; // Threshold in pixels +constexpr f32 PressDelay = 0.5f; // Time in seconds +constexpr f32 DoubleTapDelay = 0.35f; // Time in seconds // This is nn::hid::GestureType enum class GestureType : u32 { @@ -28,6 +40,7 @@ enum class GestureType : u32 { Swipe, // Fast press movement and release of a single point Pinch, // All points moving away/closer to the midpoint Rotate, // All points rotating from the midpoint + GestureTypeMax = Rotate, }; // This is nn::hid::GestureDirection @@ -69,7 +82,7 @@ struct GestureState { static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); struct GestureProperties { - std::array<Common::Point<s32>, MAX_POINTS> points{}; + std::array<Common::Point<s32>, MaxPoints> points{}; std::size_t active_points{}; Common::Point<s32> mid_point{}; s64 detection_count{}; @@ -78,13 +91,53 @@ struct GestureProperties { f32 angle{}; }; +// This is nn::hid::TouchState +struct TouchState { + u64 delta_time{}; + Core::HID::TouchAttribute attribute{}; + u32 finger{}; + Common::Point<u32> position{}; + u32 diameter_x{}; + u32 diameter_y{}; + s32 rotation_angle{}; +}; +static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); + // This is nn::hid::TouchScreenState struct TouchScreenState { s64 sampling_number{}; s32 entry_count{}; INSERT_PADDING_BYTES(4); // Reserved - std::array<Core::HID::TouchState, MAX_FINGERS> states{}; + std::array<TouchState, MaxFingers> states{}; }; static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); +struct TouchFingerMap { + s32 finger_count{}; + Core::HID::TouchScreenModeForNx touch_mode; + INSERT_PADDING_BYTES(3); + std::array<u32, MaxFingers> finger_ids{}; +}; +static_assert(sizeof(TouchFingerMap) == 0x48, "TouchFingerMap is an invalid size"); + +struct TouchAruidData { + u64 aruid; + u32 basic_gesture_id; + u64 used_1; + u64 used_2; + u64 used_3; + u64 used_4; + GestureType gesture_type; + u16 resolution_width; + u16 resolution_height; + TouchFingerMap finger_map; +}; +static_assert(sizeof(TouchAruidData) == 0x80, "TouchAruidData is an invalid size"); + +struct AutoPilotState { + u64 count; + std::array<TouchState, 16> state; +}; +static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size"); + } // namespace Service::HID diff --git a/src/hid_core/resources/unique_pad/unique_pad.cpp b/src/hid_core/resources/unique_pad/unique_pad.cpp index 89fc57269..b2db55c5a 100644 --- a/src/hid_core/resources/unique_pad/unique_pad.cpp +++ b/src/hid_core/resources/unique_pad/unique_pad.cpp @@ -17,10 +17,6 @@ void UniquePad::OnInit() {} void UniquePad::OnRelease() {} void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { - if (!smart_update) { - return; - } - const u64 aruid = applet_resource->GetActiveAruid(); auto* data = applet_resource->GetAruidData(aruid); diff --git a/src/hid_core/resources/unique_pad/unique_pad.h b/src/hid_core/resources/unique_pad/unique_pad.h index 674ad1691..4873b7f7e 100644 --- a/src/hid_core/resources/unique_pad/unique_pad.h +++ b/src/hid_core/resources/unique_pad/unique_pad.h @@ -20,8 +20,5 @@ public: // When the controller is requesting an update for the shared memory void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; - -private: - bool smart_update{}; }; } // namespace Service::HID diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 0755ba772..16c905db9 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -55,6 +55,7 @@ add_library(video_core STATIC engines/maxwell_dma.h engines/puller.cpp engines/puller.h + framebuffer_config.cpp framebuffer_config.h fsr.cpp fsr.h @@ -115,8 +116,24 @@ add_library(video_core STATIC renderer_null/null_rasterizer.h renderer_null/renderer_null.cpp renderer_null/renderer_null.h + renderer_opengl/present/filters.cpp + renderer_opengl/present/filters.h + renderer_opengl/present/fsr.cpp + renderer_opengl/present/fsr.h + renderer_opengl/present/fxaa.cpp + renderer_opengl/present/fxaa.h + renderer_opengl/present/layer.cpp + renderer_opengl/present/layer.h + renderer_opengl/present/present_uniforms.h + renderer_opengl/present/smaa.cpp + renderer_opengl/present/smaa.h + renderer_opengl/present/util.h + renderer_opengl/present/window_adapt_pass.cpp + renderer_opengl/present/window_adapt_pass.h renderer_opengl/blit_image.cpp renderer_opengl/blit_image.h + renderer_opengl/gl_blit_screen.cpp + renderer_opengl/gl_blit_screen.h renderer_opengl/gl_buffer_cache_base.cpp renderer_opengl/gl_buffer_cache.cpp renderer_opengl/gl_buffer_cache.h @@ -126,8 +143,6 @@ add_library(video_core STATIC renderer_opengl/gl_device.h renderer_opengl/gl_fence_manager.cpp renderer_opengl/gl_fence_manager.h - renderer_opengl/gl_fsr.cpp - renderer_opengl/gl_fsr.h renderer_opengl/gl_graphics_pipeline.cpp renderer_opengl/gl_graphics_pipeline.h renderer_opengl/gl_rasterizer.cpp @@ -155,6 +170,22 @@ add_library(video_core STATIC renderer_opengl/renderer_opengl.h renderer_opengl/util_shaders.cpp renderer_opengl/util_shaders.h + renderer_vulkan/present/anti_alias_pass.h + renderer_vulkan/present/filters.cpp + renderer_vulkan/present/filters.h + renderer_vulkan/present/fsr.cpp + renderer_vulkan/present/fsr.h + renderer_vulkan/present/fxaa.cpp + renderer_vulkan/present/fxaa.h + renderer_vulkan/present/layer.cpp + renderer_vulkan/present/layer.h + renderer_vulkan/present/present_push_constants.h + renderer_vulkan/present/smaa.cpp + renderer_vulkan/present/smaa.h + renderer_vulkan/present/util.cpp + renderer_vulkan/present/util.h + renderer_vulkan/present/window_adapt_pass.cpp + renderer_vulkan/present/window_adapt_pass.h renderer_vulkan/blit_image.cpp renderer_vulkan/blit_image.h renderer_vulkan/fixed_pipeline_state.cpp @@ -181,8 +212,6 @@ add_library(video_core STATIC renderer_vulkan/vk_descriptor_pool.h renderer_vulkan/vk_fence_manager.cpp renderer_vulkan/vk_fence_manager.h - renderer_vulkan/vk_fsr.cpp - renderer_vulkan/vk_fsr.h renderer_vulkan/vk_graphics_pipeline.cpp renderer_vulkan/vk_graphics_pipeline.h renderer_vulkan/vk_master_semaphore.cpp @@ -203,8 +232,6 @@ add_library(video_core STATIC renderer_vulkan/vk_scheduler.h renderer_vulkan/vk_shader_util.cpp renderer_vulkan/vk_shader_util.h - renderer_vulkan/vk_smaa.cpp - renderer_vulkan/vk_smaa.h renderer_vulkan/vk_staging_buffer_pool.cpp renderer_vulkan/vk_staging_buffer_pool.h renderer_vulkan/vk_state_tracker.cpp diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h index 1a43e24b6..99341e431 100644 --- a/src/video_core/engines/maxwell_dma.h +++ b/src/video_core/engines/maxwell_dma.h @@ -8,6 +8,7 @@ #include <vector> #include "common/bit_field.h" +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/scratch_buffer.h" #include "video_core/engines/engine_interface.h" diff --git a/src/video_core/engines/sw_blitter/blitter.cpp b/src/video_core/engines/sw_blitter/blitter.cpp index 4bc079024..8bcc2f7a7 100644 --- a/src/video_core/engines/sw_blitter/blitter.cpp +++ b/src/video_core/engines/sw_blitter/blitter.cpp @@ -111,6 +111,20 @@ void Bilinear(std::span<const f32> input, std::span<f32> output, size_t src_widt } } +template <bool unpack> +void ProcessPitchLinear(std::span<const u8> input, std::span<u8> output, size_t extent_x, + size_t extent_y, u32 pitch, u32 x0, u32 y0, size_t bpp) { + const size_t base_offset = x0 * bpp; + const size_t copy_size = extent_x * bpp; + for (size_t y = 0; y < extent_y; y++) { + const size_t first_offset = (y + y0) * pitch + base_offset; + const size_t second_offset = y * extent_x * bpp; + u8* write_to = unpack ? &output[first_offset] : &output[second_offset]; + const u8* read_from = unpack ? &input[second_offset] : &input[first_offset]; + std::memcpy(write_to, read_from, copy_size); + } +} + } // namespace struct SoftwareBlitEngine::BlitEngineImpl { @@ -138,19 +152,6 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, } return static_cast<size_t>(surface.pitch * surface.height); }; - const auto process_pitch_linear = [](bool unpack, std::span<const u8> input, - std::span<u8> output, u32 extent_x, u32 extent_y, - u32 pitch, u32 x0, u32 y0, size_t bpp) { - const size_t base_offset = x0 * bpp; - const size_t copy_size = extent_x * bpp; - for (u32 y = y0; y < extent_y; y++) { - const size_t first_offset = y * pitch + base_offset; - const size_t second_offset = y * extent_x * bpp; - u8* write_to = unpack ? &output[first_offset] : &output[second_offset]; - const u8* read_from = unpack ? &input[second_offset] : &input[first_offset]; - std::memcpy(write_to, read_from, copy_size); - } - }; const u32 src_extent_x = config.src_x1 - config.src_x0; const u32 src_extent_y = config.src_y1 - config.src_y0; @@ -205,8 +206,8 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y, src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel); } else { - process_pitch_linear(false, tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, - src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); + ProcessPitchLinear<false>(tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, + src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); } // Conversion Phase @@ -229,9 +230,9 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y, dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel); } else { - process_pitch_linear(true, impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, - dst.pitch, config.dst_x0, config.dst_y0, - static_cast<size_t>(dst_bytes_per_pixel)); + ProcessPitchLinear<true>(impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, + dst.pitch, config.dst_x0, config.dst_y0, + static_cast<size_t>(dst_bytes_per_pixel)); } return true; } diff --git a/src/video_core/framebuffer_config.cpp b/src/video_core/framebuffer_config.cpp new file mode 100644 index 000000000..e28d41f84 --- /dev/null +++ b/src/video_core/framebuffer_config.cpp @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/assert.h" +#include "video_core/framebuffer_config.h" + +namespace Tegra { + +Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, + u32 texture_height) { + f32 left, top, right, bottom; + + if (!framebuffer.crop_rect.IsEmpty()) { + // If crop rectangle is not empty, apply properties from rectangle. + left = static_cast<f32>(framebuffer.crop_rect.left); + top = static_cast<f32>(framebuffer.crop_rect.top); + right = static_cast<f32>(framebuffer.crop_rect.right); + bottom = static_cast<f32>(framebuffer.crop_rect.bottom); + } else { + // Otherwise, fall back to framebuffer dimensions. + left = 0; + top = 0; + right = static_cast<f32>(framebuffer.width); + bottom = static_cast<f32>(framebuffer.height); + } + + // Apply transformation flags. + auto framebuffer_transform_flags = framebuffer.transform_flags; + + if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) { + // Switch left and right. + std::swap(left, right); + } + if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) { + // Switch top and bottom. + std::swap(top, bottom); + } + + framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH; + framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV; + if (True(framebuffer_transform_flags)) { + UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}", + static_cast<u32>(framebuffer_transform_flags)); + } + + // Normalize coordinate space. + left /= static_cast<f32>(texture_width); + top /= static_cast<f32>(texture_height); + right /= static_cast<f32>(texture_width); + bottom /= static_cast<f32>(texture_height); + + return Common::Rectangle<f32>(left, top, right, bottom); +} + +} // namespace Tegra diff --git a/src/video_core/framebuffer_config.h b/src/video_core/framebuffer_config.h index 856f4bd52..6a18b76fb 100644 --- a/src/video_core/framebuffer_config.h +++ b/src/video_core/framebuffer_config.h @@ -7,6 +7,7 @@ #include "common/math_util.h" #include "core/hle/service/nvnflinger/buffer_transform_flags.h" #include "core/hle/service/nvnflinger/pixel_format.h" +#include "core/hle/service/nvnflinger/ui/fence.h" namespace Tegra { @@ -21,7 +22,10 @@ struct FramebufferConfig { u32 stride{}; Service::android::PixelFormat pixel_format{}; Service::android::BufferTransformFlags transform_flags{}; - Common::Rectangle<int> crop_rect; + Common::Rectangle<int> crop_rect{}; }; +Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, + u32 texture_height); + } // namespace Tegra diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp index 609704b33..f4a5d831c 100644 --- a/src/video_core/gpu.cpp +++ b/src/video_core/gpu.cpp @@ -274,11 +274,6 @@ struct GPU::Impl { } } - /// Swap buffers (render frame) - void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { - gpu_thread.SwapBuffers(framebuffer); - } - /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory void FlushRegion(DAddr addr, u64 size) { gpu_thread.FlushRegion(addr, size); @@ -313,8 +308,9 @@ struct GPU::Impl { gpu_thread.FlushAndInvalidateRegion(addr, size); } - void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, - std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { + void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, + std::vector<Service::Nvidia::NvFence>&& fences) { + size_t num_fences{fences.size()}; size_t current_request_counter{}; { std::unique_lock<std::mutex> lk(request_swap_mutex); @@ -328,13 +324,12 @@ struct GPU::Impl { } } const auto wait_fence = - RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] { + RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { auto& syncpoint_manager = host1x.GetSyncpointManager(); if (num_fences == 0) { - renderer->SwapBuffers(framebuffer); + renderer->Composite(layers); } - const auto executer = [this, current_request_counter, - framebuffer_copy = *framebuffer]() { + const auto executer = [this, current_request_counter, layers_copy = layers]() { { std::unique_lock<std::mutex> lk(request_swap_mutex); if (--request_swap_counters[current_request_counter] != 0) { @@ -342,7 +337,7 @@ struct GPU::Impl { } free_swap_counters.push_back(current_request_counter); } - renderer->SwapBuffers(&framebuffer_copy); + renderer->Composite(layers_copy); }; for (size_t i = 0; i < num_fences; i++) { syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); @@ -505,9 +500,9 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const { return impl->ShaderNotify(); } -void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, - std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { - impl->RequestSwapBuffers(framebuffer, fences, num_fences); +void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, + std::vector<Service::Nvidia::NvFence>&& fences) { + impl->RequestComposite(std::move(layers), std::move(fences)); } u64 GPU::GetTicks() const { @@ -554,10 +549,6 @@ void GPU::ClearCdmaInstance(u32 id) { impl->ClearCdmaInstance(id); } -void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { - impl->SwapBuffers(framebuffer); -} - VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { return impl->OnCPURead(addr, size); } diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h index b3c1d15bd..c4602ca37 100644 --- a/src/video_core/gpu.h +++ b/src/video_core/gpu.h @@ -212,8 +212,8 @@ public: void RendererFrameEndNotify(); - void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, - std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences); + void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, + std::vector<Service::Nvidia::NvFence>&& fences); /// Performs any additional setup necessary in order to begin GPU emulation. /// This can be used to launch any necessary threads and register any necessary diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp index 788d4f61e..58d8110b8 100644 --- a/src/video_core/gpu_thread.cpp +++ b/src/video_core/gpu_thread.cpp @@ -40,8 +40,6 @@ static void RunThread(std::stop_token stop_token, Core::System& system, } if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { scheduler.Push(submit_list->channel, std::move(submit_list->entries)); - } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) { - renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); } else if (std::holds_alternative<GPUTickCommand>(next.data)) { system.GPU().TickWork(); } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { @@ -78,10 +76,6 @@ void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { PushCommand(SubmitListCommand(channel, std::move(entries))); } -void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { - PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt)); -} - void ThreadManager::FlushRegion(DAddr addr, u64 size) { if (!is_async) { // Always flush with synchronous GPU mode diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h index 2de25e9ef..dc0fce9f8 100644 --- a/src/video_core/gpu_thread.h +++ b/src/video_core/gpu_thread.h @@ -44,14 +44,6 @@ struct SubmitListCommand final { Tegra::CommandList entries; }; -/// Command to signal to the GPU thread that a swap buffers is pending -struct SwapBuffersCommand final { - explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer_) - : framebuffer{std::move(framebuffer_)} {} - - std::optional<Tegra::FramebufferConfig> framebuffer; -}; - /// Command to signal to the GPU thread to flush a region struct FlushRegionCommand final { explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} @@ -81,8 +73,8 @@ struct FlushAndInvalidateRegionCommand final { struct GPUTickCommand final {}; using CommandData = - std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, - InvalidateRegionCommand, FlushAndInvalidateRegionCommand, GPUTickCommand>; + std::variant<std::monostate, SubmitListCommand, FlushRegionCommand, InvalidateRegionCommand, + FlushAndInvalidateRegionCommand, GPUTickCommand>; struct CommandDataContainer { CommandDataContainer() = default; @@ -118,9 +110,6 @@ public: /// Push GPU command entries to be processed void SubmitList(s32 channel, Tegra::CommandList&& entries); - /// Swap buffers (render frame) - void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); - /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory void FlushRegion(DAddr addr, u64 size); diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt index cd2549232..969f21d50 100644 --- a/src/video_core/host_shaders/CMakeLists.txt +++ b/src/video_core/host_shaders/CMakeLists.txt @@ -9,7 +9,7 @@ set(FIDELITYFX_FILES ) set(GLSL_INCLUDES - fidelityfx_fsr.comp + fidelityfx_fsr.frag ${FIDELITYFX_FILES} ) @@ -56,10 +56,11 @@ set(SHADER_FILES vulkan_color_clear.frag vulkan_color_clear.vert vulkan_depthstencil_clear.frag - vulkan_fidelityfx_fsr_easu_fp16.comp - vulkan_fidelityfx_fsr_easu_fp32.comp - vulkan_fidelityfx_fsr_rcas_fp16.comp - vulkan_fidelityfx_fsr_rcas_fp32.comp + vulkan_fidelityfx_fsr.vert + vulkan_fidelityfx_fsr_easu_fp16.frag + vulkan_fidelityfx_fsr_easu_fp32.frag + vulkan_fidelityfx_fsr_rcas_fp16.frag + vulkan_fidelityfx_fsr_rcas_fp32.frag vulkan_present.frag vulkan_present.vert vulkan_present_scaleforce_fp16.frag diff --git a/src/video_core/host_shaders/fidelityfx_fsr.comp b/src/video_core/host_shaders/fidelityfx_fsr.frag index f91b1aa9f..a266e1c4e 100644 --- a/src/video_core/host_shaders/fidelityfx_fsr.comp +++ b/src/video_core/host_shaders/fidelityfx_fsr.frag @@ -34,7 +34,6 @@ layout( push_constant ) uniform constants { }; layout(set=0,binding=0) uniform sampler2D InputTexture; -layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture; #define A_GPU 1 #define A_GLSL 1 @@ -72,44 +71,40 @@ layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture; #include "ffx_fsr1.h" -void CurrFilter(AU2 pos) { -#if USE_BILINEAR - AF2 pp = (AF2(pos) * AF2_AU2(Const0.xy) + AF2_AU2(Const0.zw)) * AF2_AU2(Const1.xy) + AF2(0.5, -0.5) * AF2_AU2(Const1.zw); - imageStore(OutputTexture, ASU2(pos), textureLod(InputTexture, pp, 0.0)); +#if USE_RCAS + layout(location = 0) in vec2 frag_texcoord; #endif +layout (location = 0) out vec4 frag_color; + +void CurrFilter(AU2 pos) { #if USE_EASU #ifndef YUZU_USE_FP16 AF3 c; FsrEasuF(c, pos, Const0, Const1, Const2, Const3); - imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); + frag_color = AF4(c, 1.0); #else AH3 c; FsrEasuH(c, pos, Const0, Const1, Const2, Const3); - imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); + frag_color = AH4(c, 1.0); #endif #endif #if USE_RCAS #ifndef YUZU_USE_FP16 AF3 c; FsrRcasF(c.r, c.g, c.b, pos, Const0); - imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); + frag_color = AF4(c, 1.0); #else AH3 c; FsrRcasH(c.r, c.g, c.b, pos, Const0); - imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); + frag_color = AH4(c, 1.0); #endif #endif } -layout(local_size_x=64) in; void main() { - // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. - AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); - CurrFilter(gxy); - gxy.x += 8u; - CurrFilter(gxy); - gxy.y += 8u; - CurrFilter(gxy); - gxy.x -= 8u; - CurrFilter(gxy); +#if USE_RCAS + CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0)))); +#else + CurrFilter(AU2(gl_FragCoord.xy)); +#endif } diff --git a/src/video_core/host_shaders/fxaa.vert b/src/video_core/host_shaders/fxaa.vert index c2717d90d..223ab785e 100644 --- a/src/video_core/host_shaders/fxaa.vert +++ b/src/video_core/host_shaders/fxaa.vert @@ -7,8 +7,8 @@ out gl_PerVertex { vec4 gl_Position; }; -const vec2 vertices[4] = - vec2[4](vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0)); +const vec2 vertices[3] = + vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3)); layout (location = 0) out vec4 posPos; diff --git a/src/video_core/host_shaders/opengl_present_scaleforce.frag b/src/video_core/host_shaders/opengl_present_scaleforce.frag index a780373e3..1598575a1 100644 --- a/src/video_core/host_shaders/opengl_present_scaleforce.frag +++ b/src/video_core/host_shaders/opengl_present_scaleforce.frag @@ -26,21 +26,11 @@ #endif -#ifdef VULKAN - -#define BINDING_COLOR_TEXTURE 1 - -#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv - -#define BINDING_COLOR_TEXTURE 0 - -#endif - layout (location = 0) in vec2 tex_coord; layout (location = 0) out vec4 frag_color; -layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; +layout (binding = 0) uniform sampler2D input_texture; const bool ignore_alpha = true; diff --git a/src/video_core/host_shaders/present_bicubic.frag b/src/video_core/host_shaders/present_bicubic.frag index c57dd2851..c814629cf 100644 --- a/src/video_core/host_shaders/present_bicubic.frag +++ b/src/video_core/host_shaders/present_bicubic.frag @@ -3,22 +3,12 @@ #version 460 core -#ifdef VULKAN - -#define BINDING_COLOR_TEXTURE 1 - -#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv - -#define BINDING_COLOR_TEXTURE 0 - -#endif - layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; -layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; +layout (binding = 0) uniform sampler2D color_texture; vec4 cubic(float v) { vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; diff --git a/src/video_core/host_shaders/present_gaussian.frag b/src/video_core/host_shaders/present_gaussian.frag index 5f54b71b6..ad9bb76a4 100644 --- a/src/video_core/host_shaders/present_gaussian.frag +++ b/src/video_core/host_shaders/present_gaussian.frag @@ -7,21 +7,11 @@ #version 460 core -#ifdef VULKAN - -#define BINDING_COLOR_TEXTURE 1 - -#else // ^^^ Vulkan ^^^ // vvv OpenGL vvv - -#define BINDING_COLOR_TEXTURE 0 - -#endif - layout(location = 0) in vec2 frag_tex_coord; layout(location = 0) out vec4 color; -layout(binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; +layout(binding = 0) uniform sampler2D color_texture; const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308); const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert b/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert new file mode 100644 index 000000000..6a87a7cac --- /dev/null +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr.vert @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#version 450 + +layout(location = 0) out vec2 texcoord; + +void main() { + float x = float((gl_VertexIndex & 1) << 2); + float y = float((gl_VertexIndex & 2) << 1); + gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0); + texcoord = vec2(x, y) / 2.0; +} diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag index 00af13726..d369bef06 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.comp +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16.frag @@ -7,4 +7,4 @@ #define YUZU_USE_FP16 #define USE_EASU 1 -#include "fidelityfx_fsr.comp" +#include "fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag index 13d783fa8..6f25ef00f 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.comp +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32.frag @@ -6,4 +6,4 @@ #define USE_EASU 1 -#include "fidelityfx_fsr.comp" +#include "fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag index 331549d96..0c953a900 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.comp +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16.frag @@ -7,4 +7,4 @@ #define YUZU_USE_FP16 #define USE_RCAS 1 -#include "fidelityfx_fsr.comp" +#include "fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag index 013ca0014..02e9a27c6 100644 --- a/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.comp +++ b/src/video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32.frag @@ -6,4 +6,4 @@ #define USE_RCAS 1 -#include "fidelityfx_fsr.comp" +#include "fidelityfx_fsr.frag" diff --git a/src/video_core/host_shaders/vulkan_present.frag b/src/video_core/host_shaders/vulkan_present.frag index 97e098ced..adada9411 100644 --- a/src/video_core/host_shaders/vulkan_present.frag +++ b/src/video_core/host_shaders/vulkan_present.frag @@ -7,7 +7,7 @@ layout (location = 0) in vec2 frag_tex_coord; layout (location = 0) out vec4 color; -layout (binding = 1) uniform sampler2D color_texture; +layout (binding = 0) uniform sampler2D color_texture; void main() { color = texture(color_texture, frag_tex_coord); diff --git a/src/video_core/host_shaders/vulkan_present.vert b/src/video_core/host_shaders/vulkan_present.vert index 89dc80468..249c9675a 100644 --- a/src/video_core/host_shaders/vulkan_present.vert +++ b/src/video_core/host_shaders/vulkan_present.vert @@ -3,16 +3,37 @@ #version 460 core -layout (location = 0) in vec2 vert_position; -layout (location = 1) in vec2 vert_tex_coord; - layout (location = 0) out vec2 frag_tex_coord; -layout (set = 0, binding = 0) uniform MatrixBlock { +struct ScreenRectVertex { + vec2 position; + vec2 tex_coord; +}; + +layout (push_constant) uniform PushConstants { mat4 modelview_matrix; + ScreenRectVertex vertices[4]; }; +// Vulkan spec 15.8.1: +// Any member of a push constant block that is declared as an +// array must only be accessed with dynamically uniform indices. +ScreenRectVertex GetVertex(int index) { + switch (index) { + case 0: + default: + return vertices[0]; + case 1: + return vertices[1]; + case 2: + return vertices[2]; + case 3: + return vertices[3]; + } +} + void main() { - gl_Position = modelview_matrix * vec4(vert_position, 0.0, 1.0); - frag_tex_coord = vert_tex_coord; + ScreenRectVertex vertex = GetVertex(gl_VertexIndex); + gl_Position = modelview_matrix * vec4(vertex.position, 0.0, 1.0); + frag_tex_coord = vertex.tex_coord; } diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag index 3dc9c0df5..79ea817c2 100644 --- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag +++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp16.frag @@ -5,6 +5,7 @@ #extension GL_GOOGLE_include_directive : enable +#define VERSION 1 #define YUZU_USE_FP16 #include "opengl_present_scaleforce.frag" diff --git a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag index 77ed07552..9605bb58b 100644 --- a/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag +++ b/src/video_core/host_shaders/vulkan_present_scaleforce_fp32.frag @@ -5,4 +5,6 @@ #extension GL_GOOGLE_include_directive : enable +#define VERSION 1 + #include "opengl_present_scaleforce.frag" diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h index 8fa4e4d9a..6e2eccfbf 100644 --- a/src/video_core/rasterizer_interface.h +++ b/src/video_core/rasterizer_interface.h @@ -155,12 +155,6 @@ public: virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, std::span<const u8> memory) = 0; - /// Attempt to use a faster method to display the framebuffer to screen - [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, - DAddr framebuffer_addr, u32 pixel_stride) { - return false; - } - /// Initialize disk cached resources for the game being emulated virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const DiskResourceLoadCallback& callback) {} diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h index 78ea5208b..3ad180f67 100644 --- a/src/video_core/renderer_base.h +++ b/src/video_core/renderer_base.h @@ -38,7 +38,7 @@ public: virtual ~RendererBase(); /// Finalize rendering the guest frame and draw into the presentation texture - virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; + virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; diff --git a/src/video_core/renderer_null/null_rasterizer.cpp b/src/video_core/renderer_null/null_rasterizer.cpp index abfabb65b..a5cda0f38 100644 --- a/src/video_core/renderer_null/null_rasterizer.cpp +++ b/src/video_core/renderer_null/null_rasterizer.cpp @@ -92,10 +92,6 @@ bool RasterizerNull::AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Surfac } void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, std::span<const u8> memory) {} -bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config, - DAddr framebuffer_addr, u32 pixel_stride) { - return true; -} void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback) {} void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) { diff --git a/src/video_core/renderer_null/null_rasterizer.h b/src/video_core/renderer_null/null_rasterizer.h index a5789604f..c7f5849c7 100644 --- a/src/video_core/renderer_null/null_rasterizer.h +++ b/src/video_core/renderer_null/null_rasterizer.h @@ -77,8 +77,6 @@ public: Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, std::span<const u8> memory) override; - bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, - u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback) override; void InitializeChannel(Tegra::Control::ChannelState& channel) override; diff --git a/src/video_core/renderer_null/renderer_null.cpp b/src/video_core/renderer_null/renderer_null.cpp index 078feb925..c89daff53 100644 --- a/src/video_core/renderer_null/renderer_null.cpp +++ b/src/video_core/renderer_null/renderer_null.cpp @@ -13,8 +13,8 @@ RendererNull::RendererNull(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gp RendererNull::~RendererNull() = default; -void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { - if (!framebuffer) { +void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { + if (framebuffers.empty()) { return; } diff --git a/src/video_core/renderer_null/renderer_null.h b/src/video_core/renderer_null/renderer_null.h index 9531b43f6..063b476bb 100644 --- a/src/video_core/renderer_null/renderer_null.h +++ b/src/video_core/renderer_null/renderer_null.h @@ -17,7 +17,7 @@ public: std::unique_ptr<Core::Frontend::GraphicsContext> context); ~RendererNull() override; - void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; + void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; VideoCore::RasterizerInterface* ReadRasterizer() override { return &m_rasterizer; diff --git a/src/video_core/renderer_opengl/gl_blit_screen.cpp b/src/video_core/renderer_opengl/gl_blit_screen.cpp new file mode 100644 index 000000000..6ba8b214b --- /dev/null +++ b/src/video_core/renderer_opengl/gl_blit_screen.cpp @@ -0,0 +1,96 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "video_core/renderer_opengl/gl_blit_screen.h" +#include "video_core/renderer_opengl/gl_state_tracker.h" +#include "video_core/renderer_opengl/present/filters.h" +#include "video_core/renderer_opengl/present/layer.h" +#include "video_core/renderer_opengl/present/window_adapt_pass.h" + +namespace OpenGL { + +BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, + Tegra::MaxwellDeviceMemoryManager& device_memory_, + StateTracker& state_tracker_, ProgramManager& program_manager_, + Device& device_) + : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), + program_manager(program_manager_), device(device_) {} + +BlitScreen::~BlitScreen() = default; + +void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout) { + // TODO: Signal state tracker about these changes + state_tracker.NotifyScreenDrawVertexArray(); + state_tracker.NotifyPolygonModes(); + state_tracker.NotifyViewport0(); + state_tracker.NotifyScissor0(); + state_tracker.NotifyColorMask(0); + state_tracker.NotifyBlend0(); + state_tracker.NotifyFramebuffer(); + state_tracker.NotifyFrontFace(); + state_tracker.NotifyCullTest(); + state_tracker.NotifyDepthTest(); + state_tracker.NotifyStencilTest(); + state_tracker.NotifyPolygonOffset(); + state_tracker.NotifyRasterizeEnable(); + state_tracker.NotifyFramebufferSRGB(); + state_tracker.NotifyLogicOp(); + state_tracker.NotifyClipControl(); + state_tracker.NotifyAlphaTest(); + state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); + + glEnable(GL_CULL_FACE); + glDisable(GL_COLOR_LOGIC_OP); + glDisable(GL_DEPTH_TEST); + glDisable(GL_STENCIL_TEST); + glDisable(GL_POLYGON_OFFSET_FILL); + glDisable(GL_RASTERIZER_DISCARD); + glDisable(GL_ALPHA_TEST); + glDisablei(GL_BLEND, 0); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + glCullFace(GL_BACK); + glFrontFace(GL_CW); + glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glDepthRangeIndexed(0, 0.0, 0.0); + + while (layers.size() < framebuffers.size()) { + layers.emplace_back(rasterizer, device_memory); + } + + CreateWindowAdapt(); + window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout); + + // TODO + // program_manager.RestoreGuestPipeline(); +} + +void BlitScreen::CreateWindowAdapt() { + if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) { + return; + } + + current_window_adapt = Settings::values.scaling_filter.GetValue(); + switch (current_window_adapt) { + case Settings::ScalingFilter::NearestNeighbor: + window_adapt = MakeNearestNeighbor(device); + break; + case Settings::ScalingFilter::Bicubic: + window_adapt = MakeBicubic(device); + break; + case Settings::ScalingFilter::Gaussian: + window_adapt = MakeGaussian(device); + break; + case Settings::ScalingFilter::ScaleForce: + window_adapt = MakeScaleForce(device); + break; + case Settings::ScalingFilter::Fsr: + case Settings::ScalingFilter::Bilinear: + default: + window_adapt = MakeBilinear(device); + break; + } +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_blit_screen.h b/src/video_core/renderer_opengl/gl_blit_screen.h new file mode 100644 index 000000000..0c3d838f1 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_blit_screen.h @@ -0,0 +1,71 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> +#include <memory> +#include <span> + +#include "core/hle/service/nvnflinger/pixel_format.h" +#include "video_core/host1x/gpu_device_memory_manager.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Tegra { +struct FramebufferConfig; +} + +namespace Settings { +enum class ScalingFilter : u32; +} + +namespace OpenGL { + +class Device; +class Layer; +class ProgramManager; +class RasterizerOpenGL; +class StateTracker; +class WindowAdaptPass; + +/// Structure used for storing information about the display target for the Switch screen +struct FramebufferTextureInfo { + GLuint display_texture{}; + u32 width; + u32 height; + u32 scaled_width; + u32 scaled_height; +}; + +class BlitScreen { +public: + explicit BlitScreen(RasterizerOpenGL& rasterizer, + Tegra::MaxwellDeviceMemoryManager& device_memory, + StateTracker& state_tracker, ProgramManager& program_manager, + Device& device); + ~BlitScreen(); + + /// Draws the emulated screens to the emulator window. + void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout); + +private: + void CreateWindowAdapt(); + + RasterizerOpenGL& rasterizer; + Tegra::MaxwellDeviceMemoryManager& device_memory; + StateTracker& state_tracker; + ProgramManager& program_manager; + Device& device; + + Settings::ScalingFilter current_window_adapt{}; + std::unique_ptr<WindowAdaptPass> window_adapt; + + std::list<Layer> layers; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_fsr.cpp b/src/video_core/renderer_opengl/gl_fsr.cpp deleted file mode 100644 index 77262dcf1..000000000 --- a/src/video_core/renderer_opengl/gl_fsr.cpp +++ /dev/null @@ -1,101 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/settings.h" -#include "video_core/fsr.h" -#include "video_core/renderer_opengl/gl_fsr.h" -#include "video_core/renderer_opengl/gl_shader_manager.h" -#include "video_core/renderer_opengl/gl_shader_util.h" - -namespace OpenGL { -using namespace FSR; - -using FsrConstants = std::array<u32, 4 * 4>; - -FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source, - std::string_view fsr_rcas_source) - : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)}, - fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)}, - fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} { - glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f); - glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f); -} - -FSR::~FSR() = default; - -void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen, - u32 input_image_width, u32 input_image_height, - const Common::Rectangle<int>& crop_rect) { - - const auto output_image_width = screen.GetWidth(); - const auto output_image_height = screen.GetHeight(); - - if (fsr_intermediate_tex.handle) { - GLint fsr_tex_width, fsr_tex_height; - glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH, - &fsr_tex_width); - glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT, - &fsr_tex_height); - if (static_cast<u32>(fsr_tex_width) != output_image_width || - static_cast<u32>(fsr_tex_height) != output_image_height) { - fsr_intermediate_tex.Release(); - } - } - if (!fsr_intermediate_tex.handle) { - fsr_intermediate_tex.Create(GL_TEXTURE_2D); - glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width, - output_image_height); - glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0, - fsr_intermediate_tex.handle, 0); - } - - GLint old_draw_fb; - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); - - glFrontFace(GL_CW); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle); - glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width), - static_cast<GLfloat>(output_image_height)); - - FsrConstants constants; - FsrEasuConOffset( - constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12, - - static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()), - static_cast<f32>(input_image_width), static_cast<f32>(input_image_height), - static_cast<f32>(output_image_width), static_cast<f32>(output_image_height), - static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top)); - - glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants)); - - program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle); - glDrawArrays(GL_TRIANGLES, 0, 3); - - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); - glBindTextureUnit(0, fsr_intermediate_tex.handle); - - const float sharpening = - static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; - - FsrRcasCon(constants.data(), sharpening); - glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants)); -} - -void FSR::InitBuffers() { - fsr_framebuffer.Create(); -} - -void FSR::ReleaseBuffers() { - fsr_framebuffer.Release(); - fsr_intermediate_tex.Release(); -} - -const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept { - return fsr_rcas_frag; -} - -bool FSR::AreBuffersInitialized() const noexcept { - return fsr_framebuffer.handle; -} - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_fsr.h b/src/video_core/renderer_opengl/gl_fsr.h deleted file mode 100644 index 1f6ae3115..000000000 --- a/src/video_core/renderer_opengl/gl_fsr.h +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include <string_view> - -#include "common/common_types.h" -#include "common/math_util.h" -#include "video_core/fsr.h" -#include "video_core/renderer_opengl/gl_resource_manager.h" - -namespace OpenGL { - -class ProgramManager; - -class FSR { -public: - explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source, - std::string_view fsr_rcas_source); - ~FSR(); - - void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen, - u32 input_image_width, u32 input_image_height, - const Common::Rectangle<int>& crop_rect); - - void InitBuffers(); - - void ReleaseBuffers(); - - [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept; - - [[nodiscard]] bool AreBuffersInitialized() const noexcept; - -private: - OGLFramebuffer fsr_framebuffer; - OGLProgram fsr_vertex; - OGLProgram fsr_easu_frag; - OGLProgram fsr_rcas_frag; - OGLTexture fsr_intermediate_tex; -}; - -} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index d5354ef2d..b42fb110c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -71,10 +71,10 @@ std::optional<VideoCore::QueryType> MaxwellToVideoCoreQuery(VideoCommon::QueryTy RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, Tegra::MaxwellDeviceMemoryManager& device_memory_, - const Device& device_, ScreenInfo& screen_info_, - ProgramManager& program_manager_, StateTracker& state_tracker_) - : gpu(gpu_), device_memory(device_memory_), device(device_), screen_info(screen_info_), - program_manager(program_manager_), state_tracker(state_tracker_), + const Device& device_, ProgramManager& program_manager_, + StateTracker& state_tracker_) + : gpu(gpu_), device_memory(device_memory_), device(device_), program_manager(program_manager_), + state_tracker(state_tracker_), texture_cache_runtime(device, program_manager, state_tracker, staging_buffer_pool), texture_cache(texture_cache_runtime, device_memory_), buffer_cache_runtime(device, staging_buffer_pool), @@ -739,27 +739,29 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si query_cache.InvalidateRegion(*cpu_addr, copy_size); } -bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, - DAddr framebuffer_addr, u32 pixel_stride) { +std::optional<FramebufferTextureInfo> RasterizerOpenGL::AccelerateDisplay( + const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, u32 pixel_stride) { if (framebuffer_addr == 0) { - return false; + return {}; } MICROPROFILE_SCOPE(OpenGL_CacheManagement); std::scoped_lock lock{texture_cache.mutex}; - ImageView* const image_view{ - texture_cache.TryFindFramebufferImageView(config, framebuffer_addr)}; + const auto [image_view, scaled] = + texture_cache.TryFindFramebufferImageView(config, framebuffer_addr); if (!image_view) { - return false; + return {}; } - // Verify that the cached surface is the same size and format as the requested framebuffer - // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different"); - // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different"); - screen_info.texture.width = image_view->size.width; - screen_info.texture.height = image_view->size.height; - screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); - return true; + const auto& resolution = Settings::values.resolution_info; + + FramebufferTextureInfo info{}; + info.display_texture = image_view->Handle(Shader::TextureType::Color2D); + info.width = image_view->size.width; + info.height = image_view->size.height; + info.scaled_width = scaled ? resolution.ScaleUp(info.width) : info.width; + info.scaled_height = scaled ? resolution.ScaleUp(info.height) : info.height; + return info; } void RasterizerOpenGL::SyncState() { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 34aa73526..6eae51ff7 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -16,6 +16,7 @@ #include "video_core/engines/maxwell_dma.h" #include "video_core/rasterizer_interface.h" #include "video_core/renderer_opengl/blit_image.h" +#include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_buffer_cache.h" #include "video_core/renderer_opengl/gl_device.h" #include "video_core/renderer_opengl/gl_fence_manager.h" @@ -37,7 +38,7 @@ class MemoryManager; namespace OpenGL { -struct ScreenInfo; +struct FramebufferTextureInfo; struct ShaderEntries; struct BindlessSSBO { @@ -76,8 +77,8 @@ class RasterizerOpenGL : public VideoCore::RasterizerInterface, public: explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, Tegra::MaxwellDeviceMemoryManager& device_memory_, - const Device& device_, ScreenInfo& screen_info_, - ProgramManager& program_manager_, StateTracker& state_tracker_); + const Device& device_, ProgramManager& program_manager_, + StateTracker& state_tracker_); ~RasterizerOpenGL() override; void Draw(bool is_indexed, u32 instance_count) override; @@ -122,8 +123,6 @@ public: Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, std::span<const u8> memory) override; - bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, - u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback) override; @@ -144,6 +143,10 @@ public: return true; } + std::optional<FramebufferTextureInfo> AccelerateDisplay(const Tegra::FramebufferConfig& config, + VAddr framebuffer_addr, + u32 pixel_stride); + private: static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_IMAGES = 48; @@ -237,7 +240,6 @@ private: Tegra::MaxwellDeviceMemoryManager& device_memory; const Device& device; - ScreenInfo& screen_info; ProgramManager& program_manager; StateTracker& state_tracker; diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 66a5ca03e..be14494ca 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp @@ -1051,6 +1051,10 @@ void Image::Scale(bool up_scale) { state_tracker.NotifyScissor0(); } +bool Image::IsRescaled() const { + return True(flags & ImageFlagBits::Rescaled); +} + bool Image::ScaleUp(bool ignore) { const auto& resolution = runtime->resolution; if (!resolution.active) { diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h index 34870c81f..3e54edcc2 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.h +++ b/src/video_core/renderer_opengl/gl_texture_cache.h @@ -217,6 +217,8 @@ public: return gl_type; } + bool IsRescaled() const; + bool ScaleUp(bool ignore = false); bool ScaleDown(bool ignore = false); diff --git a/src/video_core/renderer_opengl/present/filters.cpp b/src/video_core/renderer_opengl/present/filters.cpp new file mode 100644 index 000000000..819e5d77f --- /dev/null +++ b/src/video_core/renderer_opengl/present/filters.cpp @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/host_shaders/opengl_present_frag.h" +#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" +#include "video_core/host_shaders/present_bicubic_frag.h" +#include "video_core/host_shaders/present_gaussian_frag.h" +#include "video_core/renderer_opengl/present/filters.h" +#include "video_core/renderer_opengl/present/util.h" + +namespace OpenGL { + +std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device) { + return std::make_unique<WindowAdaptPass>(device, CreateNearestNeighborSampler(), + HostShaders::OPENGL_PRESENT_FRAG); +} + +std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device) { + return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), + HostShaders::OPENGL_PRESENT_FRAG); +} + +std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device) { + return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), + HostShaders::PRESENT_BICUBIC_FRAG); +} + +std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device) { + return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), + HostShaders::PRESENT_GAUSSIAN_FRAG); +} + +std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device) { + return std::make_unique<WindowAdaptPass>( + device, CreateBilinearSampler(), + fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG)); +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/filters.h b/src/video_core/renderer_opengl/present/filters.h new file mode 100644 index 000000000..122ab7436 --- /dev/null +++ b/src/video_core/renderer_opengl/present/filters.h @@ -0,0 +1,17 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include "video_core/renderer_opengl/present/window_adapt_pass.h" + +namespace OpenGL { + +std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device); +std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device); +std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device); +std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device); +std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device); + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/fsr.cpp b/src/video_core/renderer_opengl/present/fsr.cpp new file mode 100644 index 000000000..b764aadae --- /dev/null +++ b/src/video_core/renderer_opengl/present/fsr.cpp @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "video_core/fsr.h" +#include "video_core/host_shaders/ffx_a_h.h" +#include "video_core/host_shaders/ffx_fsr1_h.h" +#include "video_core/host_shaders/full_screen_triangle_vert.h" +#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h" +#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h" +#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" +#include "video_core/renderer_opengl/present/fsr.h" +#include "video_core/renderer_opengl/present/util.h" + +namespace OpenGL { +using namespace FSR; + +using FsrConstants = std::array<u32, 4 * 4>; + +FSR::FSR(u32 output_width_, u32 output_height_) : width(output_width_), height(output_height_) { + std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; + ReplaceInclude(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); + ReplaceInclude(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); + + std::string fsr_easu_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; + std::string fsr_rcas_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; + ReplaceInclude(fsr_easu_source, "opengl_fidelityfx_fsr.frag", fsr_source); + ReplaceInclude(fsr_rcas_source, "opengl_fidelityfx_fsr.frag", fsr_source); + + vert = CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER); + easu_frag = CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER); + rcas_frag = CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER); + + glProgramUniform2f(vert.handle, 0, 1.0f, -1.0f); + glProgramUniform2f(vert.handle, 1, 0.0f, 1.0f); + + sampler = CreateBilinearSampler(); + framebuffer.Create(); + + easu_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(easu_tex.handle, 1, GL_RGBA16F, width, height); + + rcas_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(rcas_tex.handle, 1, GL_RGBA16F, width, height); +} + +FSR::~FSR() = default; + +GLuint FSR::Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width, + u32 input_image_height, const Common::Rectangle<f32>& crop_rect) { + const f32 input_width = static_cast<f32>(input_image_width); + const f32 input_height = static_cast<f32>(input_image_height); + const f32 output_width = static_cast<f32>(width); + const f32 output_height = static_cast<f32>(height); + const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_width; + const f32 viewport_x = crop_rect.left * input_width; + const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_height; + const f32 viewport_y = crop_rect.top * input_height; + + FsrConstants easu_con{}; + FsrConstants rcas_con{}; + + FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8, + easu_con.data() + 12, viewport_width, viewport_height, input_width, + input_height, output_width, output_height, viewport_x, viewport_y); + + const float sharpening = + static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; + + FsrRcasCon(rcas_con.data(), sharpening); + + glProgramUniform4uiv(easu_frag.handle, 0, sizeof(easu_con), easu_con.data()); + glProgramUniform4uiv(rcas_frag.handle, 0, sizeof(rcas_con), rcas_con.data()); + + glFrontFace(GL_CW); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle); + glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, easu_tex.handle, 0); + glViewportIndexedf(0, 0.0f, 0.0f, output_width, output_height); + program_manager.BindPresentPrograms(vert.handle, easu_frag.handle); + glBindTextureUnit(0, texture); + glBindSampler(0, sampler.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, rcas_tex.handle, 0); + program_manager.BindPresentPrograms(vert.handle, rcas_frag.handle); + glBindTextureUnit(0, easu_tex.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + return rcas_tex.handle; +} + +bool FSR::NeedsRecreation(const Common::Rectangle<u32>& screen) { + return screen.GetWidth() != width || screen.GetHeight() != height; +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/fsr.h b/src/video_core/renderer_opengl/present/fsr.h new file mode 100644 index 000000000..606935a01 --- /dev/null +++ b/src/video_core/renderer_opengl/present/fsr.h @@ -0,0 +1,39 @@ +// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <string_view> + +#include "common/common_types.h" +#include "common/math_util.h" +#include "video_core/fsr.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace OpenGL { + +class ProgramManager; + +class FSR { +public: + explicit FSR(u32 output_width, u32 output_height); + ~FSR(); + + GLuint Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width, + u32 input_image_height, const Common::Rectangle<f32>& crop_rect); + + bool NeedsRecreation(const Common::Rectangle<u32>& screen); + +private: + const u32 width; + const u32 height; + OGLFramebuffer framebuffer; + OGLSampler sampler; + OGLProgram vert; + OGLProgram easu_frag; + OGLProgram rcas_frag; + OGLTexture easu_tex; + OGLTexture rcas_tex; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/fxaa.cpp b/src/video_core/renderer_opengl/present/fxaa.cpp new file mode 100644 index 000000000..d9b58512d --- /dev/null +++ b/src/video_core/renderer_opengl/present/fxaa.cpp @@ -0,0 +1,41 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/host_shaders/fxaa_frag.h" +#include "video_core/host_shaders/fxaa_vert.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" +#include "video_core/renderer_opengl/present/fxaa.h" +#include "video_core/renderer_opengl/present/util.h" + +namespace OpenGL { + +FXAA::FXAA(u32 width, u32 height) { + vert_shader = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); + frag_shader = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); + + sampler = CreateBilinearSampler(); + + framebuffer.Create(); + + texture.Create(GL_TEXTURE_2D); + glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height); + glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0); +} + +FXAA::~FXAA() = default; + +GLuint FXAA::Draw(ProgramManager& program_manager, GLuint input_texture) { + glFrontFace(GL_CCW); + + program_manager.BindPresentPrograms(vert_shader.handle, frag_shader.handle); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle); + glBindTextureUnit(0, input_texture); + glBindSampler(0, sampler.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + glFrontFace(GL_CW); + + return texture.handle; +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/fxaa.h b/src/video_core/renderer_opengl/present/fxaa.h new file mode 100644 index 000000000..b898198f1 --- /dev/null +++ b/src/video_core/renderer_opengl/present/fxaa.h @@ -0,0 +1,27 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace OpenGL { + +class ProgramManager; + +class FXAA { +public: + explicit FXAA(u32 width, u32 height); + ~FXAA(); + + GLuint Draw(ProgramManager& program_manager, GLuint input_texture); + +private: + OGLProgram vert_shader; + OGLProgram frag_shader; + OGLSampler sampler; + OGLFramebuffer framebuffer; + OGLTexture texture; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/layer.cpp b/src/video_core/renderer_opengl/present/layer.cpp new file mode 100644 index 000000000..8643e07c6 --- /dev/null +++ b/src/video_core/renderer_opengl/present/layer.cpp @@ -0,0 +1,215 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/framebuffer_config.h" +#include "video_core/renderer_opengl/gl_blit_screen.h" +#include "video_core/renderer_opengl/gl_rasterizer.h" +#include "video_core/renderer_opengl/present/fsr.h" +#include "video_core/renderer_opengl/present/fxaa.h" +#include "video_core/renderer_opengl/present/layer.h" +#include "video_core/renderer_opengl/present/present_uniforms.h" +#include "video_core/renderer_opengl/present/smaa.h" +#include "video_core/surface.h" +#include "video_core/textures/decoders.h" + +namespace OpenGL { + +Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_) + : rasterizer(rasterizer_), device_memory(device_memory_) { + // Allocate textures for the screen + framebuffer_texture.resource.Create(GL_TEXTURE_2D); + + const GLuint texture = framebuffer_texture.resource.handle; + glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); + + // Clear screen to black + const u8 framebuffer_data[4] = {0, 0, 0, 0}; + glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, + framebuffer_data); +} + +Layer::~Layer() = default; + +GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, + std::array<ScreenRectVertex, 4>& out_vertices, + ProgramManager& program_manager, + const Tegra::FramebufferConfig& framebuffer, + const Layout::FramebufferLayout& layout) { + FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); + auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); + GLuint texture = info.display_texture; + + auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); + if (anti_aliasing != Settings::AntiAliasing::None) { + glEnablei(GL_SCISSOR_TEST, 0); + auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); + auto viewport_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); + + glScissorIndexed(0, 0, 0, viewport_width, viewport_height); + glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width), + static_cast<GLfloat>(viewport_height)); + + switch (anti_aliasing) { + case Settings::AntiAliasing::Fxaa: + CreateFXAA(); + texture = fxaa->Draw(program_manager, info.display_texture); + break; + case Settings::AntiAliasing::Smaa: + default: + CreateSMAA(); + texture = smaa->Draw(program_manager, info.display_texture); + break; + } + } + + glDisablei(GL_SCISSOR_TEST, 0); + + if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { + if (!fsr || fsr->NeedsRecreation(layout.screen)) { + fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); + } + + texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop); + crop = {0, 0, 1, 1}; + } + + out_matrix = + MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); + + // Map the coordinates to the screen. + const auto& screen = layout.screen; + const auto x = screen.left; + const auto y = screen.top; + const auto w = screen.GetWidth(); + const auto h = screen.GetHeight(); + + out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); + out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); + out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); + out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); + + return texture; +} + +FramebufferTextureInfo Layer::PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer) { + // If framebuffer is provided, reload it from memory to a texture + if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) || + framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) || + framebuffer_texture.pixel_format != framebuffer.pixel_format || + gl_framebuffer_data.empty()) { + // Reallocate texture if the framebuffer size has changed. + // This is expected to not happen very often and hence should not be a + // performance problem. + ConfigureFramebufferTexture(framebuffer); + } + + // Load the framebuffer from memory if needed + return LoadFBToScreenInfo(framebuffer); +} + +FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { + const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; + const auto accelerated_info = + rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride); + if (accelerated_info) { + return *accelerated_info; + } + + // Reset the screen info's display texture to its own permanent texture + FramebufferTextureInfo info{}; + info.display_texture = framebuffer_texture.resource.handle; + info.width = framebuffer.width; + info.height = framebuffer.height; + info.scaled_width = framebuffer.width; + info.scaled_height = framebuffer.height; + + // TODO(Rodrigo): Read this from HLE + constexpr u32 block_height_log2 = 4; + const auto pixel_format{ + VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; + const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; + const u64 size_in_bytes{Tegra::Texture::CalculateSize( + true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; + const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; + const std::span<const u8> input_data(host_ptr, size_in_bytes); + Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, + framebuffer.width, framebuffer.height, 1, block_height_log2, + 0); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); + + // Update existing texture + // TODO: Test what happens on hardware when you change the framebuffer dimensions so that + // they differ from the LCD resolution. + // TODO: Applications could theoretically crash yuzu here by specifying too large + // framebuffer sizes. We should make sure that this cannot happen. + glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width, + framebuffer.height, framebuffer_texture.gl_format, + framebuffer_texture.gl_type, gl_framebuffer_data.data()); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + return info; +} + +void Layer::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) { + framebuffer_texture.width = framebuffer.width; + framebuffer_texture.height = framebuffer.height; + framebuffer_texture.pixel_format = framebuffer.pixel_format; + + const auto pixel_format{ + VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; + const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; + gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height * + bytes_per_pixel); + + GLint internal_format; + switch (framebuffer.pixel_format) { + case Service::android::PixelFormat::Rgba8888: + internal_format = GL_RGBA8; + framebuffer_texture.gl_format = GL_RGBA; + framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + break; + case Service::android::PixelFormat::Rgb565: + internal_format = GL_RGB565; + framebuffer_texture.gl_format = GL_RGB; + framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; + break; + default: + internal_format = GL_RGBA8; + framebuffer_texture.gl_format = GL_RGBA; + framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; + // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", + // static_cast<u32>(framebuffer.pixel_format)); + break; + } + + framebuffer_texture.resource.Release(); + framebuffer_texture.resource.Create(GL_TEXTURE_2D); + glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format, + framebuffer_texture.width, framebuffer_texture.height); + + fxaa.reset(); + smaa.reset(); +} + +void Layer::CreateFXAA() { + smaa.reset(); + if (!fxaa) { + fxaa = std::make_unique<FXAA>( + Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), + Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); + } +} + +void Layer::CreateSMAA() { + fxaa.reset(); + if (!smaa) { + smaa = std::make_unique<SMAA>( + Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), + Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); + } +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/layer.h b/src/video_core/renderer_opengl/present/layer.h new file mode 100644 index 000000000..ef1055abf --- /dev/null +++ b/src/video_core/renderer_opengl/present/layer.h @@ -0,0 +1,80 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <memory> +#include <vector> + +#include "video_core/host1x/gpu_device_memory_manager.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Service::android { +enum class PixelFormat : u32; +}; + +namespace Tegra { +struct FramebufferConfig; +} + +namespace OpenGL { + +struct FramebufferTextureInfo; +class FSR; +class FXAA; +class ProgramManager; +class RasterizerOpenGL; +class SMAA; + +/// Structure used for storing information about the textures for the Switch screen +struct TextureInfo { + OGLTexture resource; + GLsizei width; + GLsizei height; + GLenum gl_format; + GLenum gl_type; + Service::android::PixelFormat pixel_format; +}; + +struct ScreenRectVertex; + +class Layer { +public: + explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory); + ~Layer(); + + GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, + std::array<ScreenRectVertex, 4>& out_vertices, + ProgramManager& program_manager, + const Tegra::FramebufferConfig& framebuffer, + const Layout::FramebufferLayout& layout); + +private: + /// Loads framebuffer from emulated memory into the active OpenGL texture. + FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); + FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer); + void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer); + + void CreateFXAA(); + void CreateSMAA(); + +private: + RasterizerOpenGL& rasterizer; + Tegra::MaxwellDeviceMemoryManager& device_memory; + + /// OpenGL framebuffer data + std::vector<u8> gl_framebuffer_data; + + /// Display information for Switch screen + TextureInfo framebuffer_texture; + + std::unique_ptr<FSR> fsr; + std::unique_ptr<FXAA> fxaa; + std::unique_ptr<SMAA> smaa; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/present_uniforms.h b/src/video_core/renderer_opengl/present/present_uniforms.h new file mode 100644 index 000000000..3a19f05c7 --- /dev/null +++ b/src/video_core/renderer_opengl/present/present_uniforms.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace OpenGL { + +constexpr GLint PositionLocation = 0; +constexpr GLint TexCoordLocation = 1; +constexpr GLint ModelViewMatrixLocation = 0; + +struct ScreenRectVertex { + constexpr ScreenRectVertex() = default; + + constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) + : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} + + std::array<GLfloat, 2> position{}; + std::array<GLfloat, 2> tex_coord{}; +}; + +/** + * Defines a 1:1 pixel orthographic projection matrix with (0,0) on the top-left + * corner and (width, height) on the lower-bottom. + * + * The projection part of the matrix is trivial, hence these operations are represented + * by a 3x2 matrix. + */ +static inline std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { + std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order + + // clang-format off + matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; + matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; + // Last matrix row is implicitly assumed to be [0, 0, 1]. + // clang-format on + + return matrix; +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/smaa.cpp b/src/video_core/renderer_opengl/present/smaa.cpp new file mode 100644 index 000000000..de7f6e502 --- /dev/null +++ b/src/video_core/renderer_opengl/present/smaa.cpp @@ -0,0 +1,102 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/host_shaders/opengl_smaa_glsl.h" +#include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" +#include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" +#include "video_core/host_shaders/smaa_edge_detection_frag.h" +#include "video_core/host_shaders/smaa_edge_detection_vert.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" +#include "video_core/renderer_opengl/present/smaa.h" +#include "video_core/renderer_opengl/present/util.h" +#include "video_core/smaa_area_tex.h" +#include "video_core/smaa_search_tex.h" + +namespace OpenGL { + +SMAA::SMAA(u32 width, u32 height) { + const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { + std::string shader_source{specialized_source}; + ReplaceInclude(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); + return CreateProgram(shader_source, stage); + }; + + edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); + edge_detection_frag = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); + blending_weight_calculation_vert = + SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); + blending_weight_calculation_frag = + SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); + neighborhood_blending_vert = + SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); + neighborhood_blending_frag = + SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + + area_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); + glTextureSubImage2D(area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, + GL_UNSIGNED_BYTE, areaTexBytes); + search_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); + glTextureSubImage2D(search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, + GL_UNSIGNED_BYTE, searchTexBytes); + + edges_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(edges_tex.handle, 1, GL_RG16F, width, height); + + blend_tex.Create(GL_TEXTURE_2D); + glTextureStorage2D(blend_tex.handle, 1, GL_RGBA16F, width, height); + + sampler = CreateBilinearSampler(); + + framebuffer.Create(); + + texture.Create(GL_TEXTURE_2D); + glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height); + glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0); +} + +SMAA::~SMAA() = default; + +GLuint SMAA::Draw(ProgramManager& program_manager, GLuint input_texture) { + glClearColor(0, 0, 0, 0); + glFrontFace(GL_CCW); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle); + glBindSampler(0, sampler.handle); + glBindSampler(1, sampler.handle); + glBindSampler(2, sampler.handle); + + glBindTextureUnit(0, input_texture); + glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, edges_tex.handle, 0); + glClear(GL_COLOR_BUFFER_BIT); + program_manager.BindPresentPrograms(edge_detection_vert.handle, edge_detection_frag.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glBindTextureUnit(0, edges_tex.handle); + glBindTextureUnit(1, area_tex.handle); + glBindTextureUnit(2, search_tex.handle); + glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, blend_tex.handle, 0); + glClear(GL_COLOR_BUFFER_BIT); + program_manager.BindPresentPrograms(blending_weight_calculation_vert.handle, + blending_weight_calculation_frag.handle); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glBindTextureUnit(0, input_texture); + glBindTextureUnit(1, blend_tex.handle); + glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0); + program_manager.BindPresentPrograms(neighborhood_blending_vert.handle, + neighborhood_blending_frag.handle); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArrays(GL_TRIANGLES, 0, 3); + glFrontFace(GL_CW); + + return texture.handle; +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/smaa.h b/src/video_core/renderer_opengl/present/smaa.h new file mode 100644 index 000000000..a48cb4fa9 --- /dev/null +++ b/src/video_core/renderer_opengl/present/smaa.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace OpenGL { + +class ProgramManager; + +class SMAA { +public: + explicit SMAA(u32 width, u32 height); + ~SMAA(); + + GLuint Draw(ProgramManager& program_manager, GLuint input_texture); + +private: + OGLProgram edge_detection_vert; + OGLProgram blending_weight_calculation_vert; + OGLProgram neighborhood_blending_vert; + OGLProgram edge_detection_frag; + OGLProgram blending_weight_calculation_frag; + OGLProgram neighborhood_blending_frag; + OGLTexture area_tex; + OGLTexture search_tex; + OGLTexture edges_tex; + OGLTexture blend_tex; + OGLSampler sampler; + OGLFramebuffer framebuffer; + OGLTexture texture; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/util.h b/src/video_core/renderer_opengl/present/util.h new file mode 100644 index 000000000..67f03aa27 --- /dev/null +++ b/src/video_core/renderer_opengl/present/util.h @@ -0,0 +1,43 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <string> + +#include "common/assert.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace OpenGL { + +static inline void ReplaceInclude(std::string& shader_source, std::string_view include_name, + std::string_view include_content) { + const std::string include_string = fmt::format("#include \"{}\"", include_name); + const std::size_t pos = shader_source.find(include_string); + ASSERT(pos != std::string::npos); + shader_source.replace(pos, include_string.size(), include_content); +}; + +static inline OGLSampler CreateBilinearSampler() { + OGLSampler sampler; + sampler.Create(); + glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + return sampler; +} + +static inline OGLSampler CreateNearestNeighborSampler() { + OGLSampler sampler; + sampler.Create(); + glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + return sampler; +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.cpp b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp new file mode 100644 index 000000000..4d681606b --- /dev/null +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.cpp @@ -0,0 +1,103 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/settings.h" +#include "video_core/framebuffer_config.h" +#include "video_core/host_shaders/opengl_present_vert.h" +#include "video_core/renderer_opengl/gl_device.h" +#include "video_core/renderer_opengl/gl_shader_manager.h" +#include "video_core/renderer_opengl/gl_shader_util.h" +#include "video_core/renderer_opengl/present/layer.h" +#include "video_core/renderer_opengl/present/present_uniforms.h" +#include "video_core/renderer_opengl/present/window_adapt_pass.h" + +namespace OpenGL { + +WindowAdaptPass::WindowAdaptPass(const Device& device_, OGLSampler&& sampler_, + std::string_view frag_source) + : device(device_), sampler(std::move(sampler_)) { + vert = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); + frag = CreateProgram(frag_source, GL_FRAGMENT_SHADER); + + // Generate VBO handle for drawing + vertex_buffer.Create(); + + // Attach vertex data to VAO + glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); + + // Query vertex buffer address when the driver supports unified vertex attributes + if (device.HasVertexBufferUnifiedMemory()) { + glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); + glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, + &vertex_buffer_address); + } +} + +WindowAdaptPass::~WindowAdaptPass() = default; + +void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, + std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout) { + GLint old_read_fb; + GLint old_draw_fb; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); + + const size_t layer_count = framebuffers.size(); + std::vector<GLuint> textures(layer_count); + std::vector<std::array<GLfloat, 3 * 2>> matrices(layer_count); + std::vector<std::array<ScreenRectVertex, 4>> vertices(layer_count); + + auto layer_it = layers.begin(); + for (size_t i = 0; i < layer_count; i++) { + textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager, + framebuffers[i], layout); + layer_it++; + } + + glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); + + program_manager.BindPresentPrograms(vert.handle, frag.handle); + + glDisable(GL_FRAMEBUFFER_SRGB); + glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), + static_cast<GLfloat>(layout.height)); + + glEnableVertexAttribArray(PositionLocation); + glEnableVertexAttribArray(TexCoordLocation); + glVertexAttribDivisor(PositionLocation, 0); + glVertexAttribDivisor(TexCoordLocation, 0); + glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, + offsetof(ScreenRectVertex, position)); + glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, + offsetof(ScreenRectVertex, tex_coord)); + glVertexAttribBinding(PositionLocation, 0); + glVertexAttribBinding(TexCoordLocation, 0); + if (device.HasVertexBufferUnifiedMemory()) { + glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); + glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, + sizeof(decltype(vertices)::value_type)); + } else { + glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); + } + + glBindSampler(0, sampler.handle); + + // Update background color before drawing + glClearColor(Settings::values.bg_red.GetValue() / 255.0f, + Settings::values.bg_green.GetValue() / 255.0f, + Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); + + glClear(GL_COLOR_BUFFER_BIT); + + for (size_t i = 0; i < layer_count; i++) { + glBindTextureUnit(0, textures[i]); + glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, + matrices[i].data()); + glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices[i]), std::data(vertices[i])); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); + } +} + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/present/window_adapt_pass.h b/src/video_core/renderer_opengl/present/window_adapt_pass.h new file mode 100644 index 000000000..00975a9c6 --- /dev/null +++ b/src/video_core/renderer_opengl/present/window_adapt_pass.h @@ -0,0 +1,47 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> +#include <span> + +#include "common/math_util.h" +#include "video_core/renderer_opengl/gl_resource_manager.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Tegra { +struct FramebufferConfig; +} + +namespace OpenGL { + +class Device; +class Layer; +class ProgramManager; + +class WindowAdaptPass final { +public: + explicit WindowAdaptPass(const Device& device, OGLSampler&& sampler, + std::string_view frag_source); + ~WindowAdaptPass(); + + void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, + std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout); + +private: + const Device& device; + OGLSampler sampler; + OGLProgram vert; + OGLProgram frag; + OGLBuffer vertex_buffer; + + // GPU address of the vertex buffer + GLuint64EXT vertex_buffer_address = 0; +}; + +} // namespace OpenGL diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp index b75376fdb..e33a32592 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.cpp +++ b/src/video_core/renderer_opengl/renderer_opengl.cpp @@ -16,68 +16,15 @@ #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/telemetry_session.h" -#include "video_core/host_shaders/ffx_a_h.h" -#include "video_core/host_shaders/ffx_fsr1_h.h" -#include "video_core/host_shaders/full_screen_triangle_vert.h" -#include "video_core/host_shaders/fxaa_frag.h" -#include "video_core/host_shaders/fxaa_vert.h" -#include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h" -#include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h" -#include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h" -#include "video_core/host_shaders/opengl_present_frag.h" -#include "video_core/host_shaders/opengl_present_scaleforce_frag.h" -#include "video_core/host_shaders/opengl_present_vert.h" -#include "video_core/host_shaders/opengl_smaa_glsl.h" -#include "video_core/host_shaders/present_bicubic_frag.h" -#include "video_core/host_shaders/present_gaussian_frag.h" -#include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" -#include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" -#include "video_core/host_shaders/smaa_edge_detection_frag.h" -#include "video_core/host_shaders/smaa_edge_detection_vert.h" -#include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" -#include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" -#include "video_core/renderer_opengl/gl_fsr.h" +#include "video_core/renderer_opengl/gl_blit_screen.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_shader_manager.h" #include "video_core/renderer_opengl/gl_shader_util.h" #include "video_core/renderer_opengl/renderer_opengl.h" -#include "video_core/smaa_area_tex.h" -#include "video_core/smaa_search_tex.h" #include "video_core/textures/decoders.h" namespace OpenGL { namespace { -constexpr GLint PositionLocation = 0; -constexpr GLint TexCoordLocation = 1; -constexpr GLint ModelViewMatrixLocation = 0; - -struct ScreenRectVertex { - constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) - : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} - - std::array<GLfloat, 2> position; - std::array<GLfloat, 2> tex_coord; -}; - -/** - * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left - * corner and (width, height) on the lower-bottom. - * - * The projection part of the matrix is trivial, hence these operations are represented - * by a 3x2 matrix. - */ -std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { - std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order - - // clang-format off - matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; - matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; - // Last matrix row is implicitly assumed to be [0, 0, 1]. - // clang-format on - - return matrix; -} - const char* GetSource(GLenum source) { switch (source) { case GL_DEBUG_SOURCE_API: @@ -148,15 +95,13 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_}, state_tracker{}, program_manager{device}, - rasterizer(emu_window, gpu, device_memory, device, screen_info, program_manager, - state_tracker) { + rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) { if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { glEnable(GL_DEBUG_OUTPUT); glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); glDebugMessageCallback(DebugHandler, nullptr); } AddTelemetryFields(); - InitOpenGLObjects(); // Initialize default attributes to match hardware's disabled attributes GLint max_attribs{}; @@ -168,27 +113,27 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); } - // Enable unified vertex attributes and query vertex buffer address when the driver supports it + + // Enable unified vertex attributes when the driver supports it if (device.HasVertexBufferUnifiedMemory()) { glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); - glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); - glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, - &vertex_buffer_address); } + blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, + program_manager, device); } RendererOpenGL::~RendererOpenGL() = default; -void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { - if (!framebuffer) { +void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { + if (framebuffers.empty()) { return; } - PrepareRendertarget(framebuffer); - RenderScreenshot(); + + RenderScreenshot(framebuffers); state_tracker.BindFramebuffer(0); - DrawScreen(emu_window.GetFramebufferLayout()); + blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout()); ++m_current_frame; @@ -199,172 +144,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { render_window.OnFrameDisplayed(); } -void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { - if (!framebuffer) { - return; - } - // If framebuffer is provided, reload it from memory to a texture - if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || - screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || - screen_info.texture.pixel_format != framebuffer->pixel_format || - gl_framebuffer_data.empty()) { - // Reallocate texture if the framebuffer size has changed. - // This is expected to not happen very often and hence should not be a - // performance problem. - ConfigureFramebufferTexture(screen_info.texture, *framebuffer); - } - - // Load the framebuffer from memory, draw it to the screen, and swap buffers - LoadFBToScreenInfo(*framebuffer); -} - -void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { - // Framebuffer orientation handling - framebuffer_transform_flags = framebuffer.transform_flags; - framebuffer_crop_rect = framebuffer.crop_rect; - framebuffer_width = framebuffer.width; - framebuffer_height = framebuffer.height; - - const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; - screen_info.was_accelerated = - rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride); - if (screen_info.was_accelerated) { - return; - } - - // Reset the screen info's display texture to its own permanent texture - screen_info.display_texture = screen_info.texture.resource.handle; - - // TODO(Rodrigo): Read this from HLE - constexpr u32 block_height_log2 = 4; - const auto pixel_format{ - VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; - const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; - const u64 size_in_bytes{Tegra::Texture::CalculateSize( - true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; - const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; - const std::span<const u8> input_data(host_ptr, size_in_bytes); - Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, - framebuffer.width, framebuffer.height, 1, block_height_log2, - 0); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); - - // Update existing texture - // TODO: Test what happens on hardware when you change the framebuffer dimensions so that - // they differ from the LCD resolution. - // TODO: Applications could theoretically crash yuzu here by specifying too large - // framebuffer sizes. We should make sure that this cannot happen. - glTextureSubImage2D(screen_info.texture.resource.handle, 0, 0, 0, framebuffer.width, - framebuffer.height, screen_info.texture.gl_format, - screen_info.texture.gl_type, gl_framebuffer_data.data()); - - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); -} - -void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, - const TextureInfo& texture) { - const u8 framebuffer_data[4] = {color_a, color_b, color_g, color_r}; - glClearTexImage(texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); -} - -void RendererOpenGL::InitOpenGLObjects() { - // Create shader programs - fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); - fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); - - const auto replace_include = [](std::string& shader_source, std::string_view include_name, - std::string_view include_content) { - const std::string include_string = fmt::format("#include \"{}\"", include_name); - const std::size_t pos = shader_source.find(include_string); - ASSERT(pos != std::string::npos); - shader_source.replace(pos, include_string.size(), include_content); - }; - - const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { - std::string shader_source{specialized_source}; - replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); - return CreateProgram(shader_source, stage); - }; - - smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); - smaa_edge_detection_frag = - SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); - smaa_blending_weight_calculation_vert = - SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); - smaa_blending_weight_calculation_frag = - SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); - smaa_neighborhood_blending_vert = - SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); - smaa_neighborhood_blending_frag = - SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); - - present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); - present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); - present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); - present_gaussian_fragment = - CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER); - present_scaleforce_fragment = - CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), - GL_FRAGMENT_SHADER); - - std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; - replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); - replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); - - std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; - std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; - replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); - replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); - - fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source, - fsr_rcas_frag_source); - - // Generate presentation sampler - present_sampler.Create(); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - present_sampler_nn.Create(); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - - // Generate VBO handle for drawing - vertex_buffer.Create(); - - // Attach vertex data to VAO - glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); - - // Allocate textures for the screen - screen_info.texture.resource.Create(GL_TEXTURE_2D); - - const GLuint texture = screen_info.texture.resource.handle; - glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); - - screen_info.display_texture = screen_info.texture.resource.handle; - - // Clear screen to black - LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); - - aa_framebuffer.Create(); - - smaa_area_tex.Create(GL_TEXTURE_2D); - glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); - glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, - GL_UNSIGNED_BYTE, areaTexBytes); - smaa_search_tex.Create(GL_TEXTURE_2D); - glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); - glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, - GL_UNSIGNED_BYTE, searchTexBytes); -} - void RendererOpenGL::AddTelemetryFields() { const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; @@ -380,328 +159,7 @@ void RendererOpenGL::AddTelemetryFields() { telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); } -void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, - const Tegra::FramebufferConfig& framebuffer) { - texture.width = framebuffer.width; - texture.height = framebuffer.height; - texture.pixel_format = framebuffer.pixel_format; - - const auto pixel_format{ - VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; - const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; - gl_framebuffer_data.resize(texture.width * texture.height * bytes_per_pixel); - - GLint internal_format; - switch (framebuffer.pixel_format) { - case Service::android::PixelFormat::Rgba8888: - internal_format = GL_RGBA8; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - break; - case Service::android::PixelFormat::Rgb565: - internal_format = GL_RGB565; - texture.gl_format = GL_RGB; - texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; - break; - default: - internal_format = GL_RGBA8; - texture.gl_format = GL_RGBA; - texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; - // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", - // static_cast<u32>(framebuffer.pixel_format)); - break; - } - - texture.resource.Release(); - texture.resource.Create(GL_TEXTURE_2D); - glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height); - aa_texture.Release(); - aa_texture.Create(GL_TEXTURE_2D); - glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F, - Settings::values.resolution_info.ScaleUp(screen_info.texture.width), - Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); - glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0); - smaa_edges_tex.Release(); - smaa_edges_tex.Create(GL_TEXTURE_2D); - glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F, - Settings::values.resolution_info.ScaleUp(screen_info.texture.width), - Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); - smaa_blend_tex.Release(); - smaa_blend_tex.Create(GL_TEXTURE_2D); - glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F, - Settings::values.resolution_info.ScaleUp(screen_info.texture.width), - Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); -} - -void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { - // TODO: Signal state tracker about these changes - state_tracker.NotifyScreenDrawVertexArray(); - state_tracker.NotifyPolygonModes(); - state_tracker.NotifyViewport0(); - state_tracker.NotifyScissor0(); - state_tracker.NotifyColorMask(0); - state_tracker.NotifyBlend0(); - state_tracker.NotifyFramebuffer(); - state_tracker.NotifyFrontFace(); - state_tracker.NotifyCullTest(); - state_tracker.NotifyDepthTest(); - state_tracker.NotifyStencilTest(); - state_tracker.NotifyPolygonOffset(); - state_tracker.NotifyRasterizeEnable(); - state_tracker.NotifyFramebufferSRGB(); - state_tracker.NotifyLogicOp(); - state_tracker.NotifyClipControl(); - state_tracker.NotifyAlphaTest(); - - state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); - - glEnable(GL_CULL_FACE); - glDisable(GL_COLOR_LOGIC_OP); - glDisable(GL_DEPTH_TEST); - glDisable(GL_STENCIL_TEST); - glDisable(GL_POLYGON_OFFSET_FILL); - glDisable(GL_RASTERIZER_DISCARD); - glDisable(GL_ALPHA_TEST); - glDisablei(GL_BLEND, 0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glCullFace(GL_BACK); - glFrontFace(GL_CW); - glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - glDepthRangeIndexed(0, 0.0, 0.0); - - glBindTextureUnit(0, screen_info.display_texture); - - auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); - if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) { - LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing); - anti_aliasing = Settings::AntiAliasing::None; - Settings::values.anti_aliasing.SetValue(anti_aliasing); - } - - if (anti_aliasing != Settings::AntiAliasing::None) { - glEnablei(GL_SCISSOR_TEST, 0); - auto viewport_width = screen_info.texture.width; - auto scissor_width = framebuffer_crop_rect.GetWidth(); - if (scissor_width <= 0) { - scissor_width = viewport_width; - } - auto viewport_height = screen_info.texture.height; - auto scissor_height = framebuffer_crop_rect.GetHeight(); - if (scissor_height <= 0) { - scissor_height = viewport_height; - } - if (screen_info.was_accelerated) { - viewport_width = Settings::values.resolution_info.ScaleUp(viewport_width); - scissor_width = Settings::values.resolution_info.ScaleUp(scissor_width); - viewport_height = Settings::values.resolution_info.ScaleUp(viewport_height); - scissor_height = Settings::values.resolution_info.ScaleUp(scissor_height); - } - glScissorIndexed(0, 0, 0, scissor_width, scissor_height); - glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width), - static_cast<GLfloat>(viewport_height)); - - glBindSampler(0, present_sampler.handle); - GLint old_read_fb; - GLint old_draw_fb; - glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); - glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); - - switch (anti_aliasing) { - case Settings::AntiAliasing::Fxaa: { - program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - } break; - case Settings::AntiAliasing::Smaa: { - glClearColor(0, 0, 0, 0); - glFrontFace(GL_CCW); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); - glBindSampler(1, present_sampler.handle); - glBindSampler(2, present_sampler.handle); - - glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, - smaa_edges_tex.handle, 0); - glClear(GL_COLOR_BUFFER_BIT); - program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle, - smaa_edge_detection_frag.handle); - glDrawArrays(GL_TRIANGLES, 0, 3); - - glBindTextureUnit(0, smaa_edges_tex.handle); - glBindTextureUnit(1, smaa_area_tex.handle); - glBindTextureUnit(2, smaa_search_tex.handle); - glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, - smaa_blend_tex.handle, 0); - glClear(GL_COLOR_BUFFER_BIT); - program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle, - smaa_blending_weight_calculation_frag.handle); - glDrawArrays(GL_TRIANGLES, 0, 3); - - glBindTextureUnit(0, screen_info.display_texture); - glBindTextureUnit(1, smaa_blend_tex.handle); - glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, - aa_texture.handle, 0); - program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle, - smaa_neighborhood_blending_frag.handle); - glDrawArrays(GL_TRIANGLES, 0, 3); - glFrontFace(GL_CW); - } break; - default: - UNREACHABLE(); - } - - glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); - - glBindTextureUnit(0, aa_texture.handle); - } - glDisablei(GL_SCISSOR_TEST, 0); - - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { - if (!fsr->AreBuffersInitialized()) { - fsr->InitBuffers(); - } - - auto crop_rect = framebuffer_crop_rect; - if (crop_rect.GetWidth() == 0) { - crop_rect.right = framebuffer_width; - } - if (crop_rect.GetHeight() == 0) { - crop_rect.bottom = framebuffer_height; - } - crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor); - const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width); - const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height); - glBindSampler(0, present_sampler.handle); - fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect); - } else { - if (fsr->AreBuffersInitialized()) { - fsr->ReleaseBuffers(); - } - } - - const std::array ortho_matrix = - MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); - - const auto fragment_handle = [this]() { - switch (Settings::values.scaling_filter.GetValue()) { - case Settings::ScalingFilter::NearestNeighbor: - case Settings::ScalingFilter::Bilinear: - return present_bilinear_fragment.handle; - case Settings::ScalingFilter::Bicubic: - return present_bicubic_fragment.handle; - case Settings::ScalingFilter::Gaussian: - return present_gaussian_fragment.handle; - case Settings::ScalingFilter::ScaleForce: - return present_scaleforce_fragment.handle; - case Settings::ScalingFilter::Fsr: - return fsr->GetPresentFragmentProgram().handle; - default: - return present_bilinear_fragment.handle; - } - }(); - program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle); - glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE, - ortho_matrix.data()); - - const auto& texcoords = screen_info.display_texcoords; - auto left = texcoords.left; - auto right = texcoords.right; - if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) { - if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) { - // Flip the framebuffer vertically - left = texcoords.right; - right = texcoords.left; - } else { - // Other transformations are unsupported - LOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags={}", - framebuffer_transform_flags); - UNIMPLEMENTED(); - } - } - - ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); - - f32 left_start{}; - if (framebuffer_crop_rect.Top() > 0) { - left_start = static_cast<f32>(framebuffer_crop_rect.Top()) / - static_cast<f32>(framebuffer_crop_rect.Bottom()); - } - f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); - f32 scale_v = - static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); - - if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) { - // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering - // (e.g. handheld mode) on a 1920x1080 framebuffer. - if (framebuffer_crop_rect.GetWidth() > 0) { - scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / - static_cast<f32>(screen_info.texture.width); - } - if (framebuffer_crop_rect.GetHeight() > 0) { - scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / - static_cast<f32>(screen_info.texture.height); - } - } - if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa && - !screen_info.was_accelerated) { - scale_u /= Settings::values.resolution_info.up_factor; - scale_v /= Settings::values.resolution_info.up_factor; - } - - const auto& screen = layout.screen; - const std::array vertices = { - ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u, - left_start + left * scale_v), - ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u, - left_start + left * scale_v), - ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u, - left_start + right * scale_v), - ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u, - left_start + right * scale_v), - }; - glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); - - glDisable(GL_FRAMEBUFFER_SRGB); - glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), - static_cast<GLfloat>(layout.height)); - - glEnableVertexAttribArray(PositionLocation); - glEnableVertexAttribArray(TexCoordLocation); - glVertexAttribDivisor(PositionLocation, 0); - glVertexAttribDivisor(TexCoordLocation, 0); - glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, - offsetof(ScreenRectVertex, position)); - glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, - offsetof(ScreenRectVertex, tex_coord)); - glVertexAttribBinding(PositionLocation, 0); - glVertexAttribBinding(TexCoordLocation, 0); - if (device.HasVertexBufferUnifiedMemory()) { - glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); - glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, - sizeof(vertices)); - } else { - glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); - } - - if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { - glBindSampler(0, present_sampler.handle); - } else { - glBindSampler(0, present_sampler_nn.handle); - } - - // Update background color before drawing - glClearColor(Settings::values.bg_red.GetValue() / 255.0f, - Settings::values.bg_green.GetValue() / 255.0f, - Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); - - glClear(GL_COLOR_BUFFER_BIT); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - // TODO - // program_manager.RestoreGuestPipeline(); -} - -void RendererOpenGL::RenderScreenshot() { +void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { if (!renderer_settings.screenshot_requested) { return; } @@ -723,7 +181,7 @@ void RendererOpenGL::RenderScreenshot() { glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); - DrawScreen(layout); + blit_screen->DrawScreen(framebuffers, layout); glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); glPixelStorei(GL_PACK_ROW_LENGTH, 0); diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h index 18699610a..c4625c96e 100644 --- a/src/video_core/renderer_opengl/renderer_opengl.h +++ b/src/video_core/renderer_opengl/renderer_opengl.h @@ -10,7 +10,6 @@ #include "video_core/renderer_base.h" #include "video_core/renderer_opengl/gl_device.h" -#include "video_core/renderer_opengl/gl_fsr.h" #include "video_core/renderer_opengl/gl_rasterizer.h" #include "video_core/renderer_opengl/gl_resource_manager.h" #include "video_core/renderer_opengl/gl_shader_manager.h" @@ -25,37 +24,13 @@ namespace Core::Frontend { class EmuWindow; } -namespace Core::Memory { -class Memory; -} - -namespace Layout { -struct FramebufferLayout; -} - namespace Tegra { class GPU; } namespace OpenGL { -/// Structure used for storing information about the textures for the Switch screen -struct TextureInfo { - OGLTexture resource; - GLsizei width; - GLsizei height; - GLenum gl_format; - GLenum gl_type; - Service::android::PixelFormat pixel_format; -}; - -/// Structure used for storing information about the display target for the Switch screen -struct ScreenInfo { - GLuint display_texture{}; - bool was_accelerated = false; - const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f}; - TextureInfo texture; -}; +class BlitScreen; class RendererOpenGL final : public VideoCore::RendererBase { public: @@ -65,7 +40,7 @@ public: std::unique_ptr<Core::Frontend::GraphicsContext> context_); ~RendererOpenGL() override; - void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; + void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; VideoCore::RasterizerInterface* ReadRasterizer() override { return &rasterizer; @@ -76,28 +51,8 @@ public: } private: - /// Initializes the OpenGL state and creates persistent objects. - void InitOpenGLObjects(); - void AddTelemetryFields(); - - void ConfigureFramebufferTexture(TextureInfo& texture, - const Tegra::FramebufferConfig& framebuffer); - - /// Draws the emulated screens to the emulator window. - void DrawScreen(const Layout::FramebufferLayout& layout); - - void RenderScreenshot(); - - /// Loads framebuffer from emulated memory into the active OpenGL texture. - void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); - - /// Fills active OpenGL texture with the given RGB color.Since the color is solid, the texture - /// can be 1x1 but will stretch across whatever it's rendered on. - void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, - const TextureInfo& texture); - - void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); + void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); Core::TelemetrySession& telemetry_session; Core::Frontend::EmuWindow& emu_window; @@ -108,49 +63,9 @@ private: StateTracker state_tracker; ProgramManager program_manager; RasterizerOpenGL rasterizer; - - // OpenGL object IDs - OGLSampler present_sampler; - OGLSampler present_sampler_nn; - OGLBuffer vertex_buffer; - OGLProgram fxaa_vertex; - OGLProgram fxaa_fragment; - OGLProgram present_vertex; - OGLProgram present_bilinear_fragment; - OGLProgram present_bicubic_fragment; - OGLProgram present_gaussian_fragment; - OGLProgram present_scaleforce_fragment; OGLFramebuffer screenshot_framebuffer; - // GPU address of the vertex buffer - GLuint64EXT vertex_buffer_address = 0; - - /// Display information for Switch screen - ScreenInfo screen_info; - OGLTexture aa_texture; - OGLFramebuffer aa_framebuffer; - - OGLProgram smaa_edge_detection_vert; - OGLProgram smaa_blending_weight_calculation_vert; - OGLProgram smaa_neighborhood_blending_vert; - OGLProgram smaa_edge_detection_frag; - OGLProgram smaa_blending_weight_calculation_frag; - OGLProgram smaa_neighborhood_blending_frag; - OGLTexture smaa_area_tex; - OGLTexture smaa_search_tex; - OGLTexture smaa_edges_tex; - OGLTexture smaa_blend_tex; - - std::unique_ptr<FSR> fsr; - - /// OpenGL framebuffer data - std::vector<u8> gl_framebuffer_data; - - /// Used for transforming the framebuffer orientation - Service::android::BufferTransformFlags framebuffer_transform_flags{}; - Common::Rectangle<int> framebuffer_crop_rect; - u32 framebuffer_width; - u32 framebuffer_height; + std::unique_ptr<BlitScreen> blit_screen; }; } // namespace OpenGL diff --git a/src/video_core/renderer_vulkan/present/anti_alias_pass.h b/src/video_core/renderer_vulkan/present/anti_alias_pass.h new file mode 100644 index 000000000..1f20fbd7f --- /dev/null +++ b/src/video_core/renderer_vulkan/present/anti_alias_pass.h @@ -0,0 +1,25 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Scheduler; + +class AntiAliasPass { +public: + virtual ~AntiAliasPass() = default; + virtual void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) = 0; +}; + +class NoAA final : public AntiAliasPass { +public: + void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) override {} +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/filters.cpp b/src/video_core/renderer_vulkan/present/filters.cpp new file mode 100644 index 000000000..b5e08938e --- /dev/null +++ b/src/video_core/renderer_vulkan/present/filters.cpp @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/common_types.h" + +#include "video_core/host_shaders/present_bicubic_frag_spv.h" +#include "video_core/host_shaders/present_gaussian_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" +#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" +#include "video_core/renderer_vulkan/present/filters.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { + +namespace { + +vk::ShaderModule SelectScaleForceShader(const Device& device) { + if (device.IsFloat16Supported()) { + return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV); + } else { + return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV); + } +} + +} // Anonymous namespace + +std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, + CreateNearestNeighborSampler(device), + BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); +} + +std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); +} + +std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); +} + +std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), + BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); +} + +std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format) { + return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), + SelectScaleForceShader(device)); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/filters.h b/src/video_core/renderer_vulkan/present/filters.h new file mode 100644 index 000000000..6c83726dd --- /dev/null +++ b/src/video_core/renderer_vulkan/present/filters.h @@ -0,0 +1,18 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_vulkan/present/window_adapt_pass.h" + +namespace Vulkan { + +class MemoryAllocator; + +std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format); +std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format); +std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format); +std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format); +std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format); + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fsr.cpp b/src/video_core/renderer_vulkan/present/fsr.cpp new file mode 100644 index 000000000..3f708be70 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/fsr.cpp @@ -0,0 +1,226 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/common_types.h" +#include "common/div_ceil.h" +#include "common/settings.h" + +#include "video_core/fsr.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_frag_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_frag_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_frag_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_frag_spv.h" +#include "video_core/host_shaders/vulkan_fidelityfx_fsr_vert_spv.h" +#include "video_core/renderer_vulkan/present/fsr.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { +using namespace FSR; + +using PushConstants = std::array<u32, 4 * 4>; + +FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, + VkExtent2D extent) + : m_device{device}, m_memory_allocator{memory_allocator}, + m_image_count{image_count}, m_extent{extent} { + + CreateImages(); + CreateRenderPasses(); + CreateSampler(); + CreateShaders(); + CreateDescriptorPool(); + CreateDescriptorSetLayout(); + CreateDescriptorSets(); + CreatePipelineLayouts(); + CreatePipelines(); +} + +void FSR::CreateImages() { + m_dynamic_images.resize(m_image_count); + for (auto& images : m_dynamic_images) { + images.images[Easu] = + CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.images[Rcas] = + CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Easu] = + CreateWrappedImageView(m_device, images.images[Easu], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Rcas] = + CreateWrappedImageView(m_device, images.images[Rcas], VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void FSR::CreateRenderPasses() { + m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + + for (auto& images : m_dynamic_images) { + images.framebuffers[Easu] = + CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Easu], m_extent); + images.framebuffers[Rcas] = + CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Rcas], m_extent); + } +} + +void FSR::CreateSampler() { + m_sampler = CreateBilinearSampler(m_device); +} + +void FSR::CreateShaders() { + m_vert_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_VERT_SPV); + + if (m_device.IsFloat16Supported()) { + m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP16_FRAG_SPV); + m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_FRAG_SPV); + } else { + m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP32_FRAG_SPV); + m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_FRAG_SPV); + } +} + +void FSR::CreateDescriptorPool() { + // EASU: 1 descriptor + // RCAS: 1 descriptor + // 2 descriptors, 2 descriptor sets per invocation + m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, 2 * m_image_count); +} + +void FSR::CreateDescriptorSetLayout() { + m_descriptor_set_layout = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void FSR::CreateDescriptorSets() { + std::vector<VkDescriptorSetLayout> layouts(MaxFsrStage, *m_descriptor_set_layout); + + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); + } +} + +void FSR::CreatePipelineLayouts() { + const VkPushConstantRange range{ + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0, + .size = sizeof(PushConstants), + }; + VkPipelineLayoutCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = m_descriptor_set_layout.address(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &range, + }; + + m_pipeline_layout = m_device.GetLogical().CreatePipelineLayout(ci); +} + +void FSR::CreatePipelines() { + m_easu_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, + std::tie(m_vert_shader, m_easu_shader)); + m_rcas_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, + std::tie(m_vert_shader, m_rcas_shader)); +} + +void FSR::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { + Images& images = m_dynamic_images[image_index]; + std::vector<VkDescriptorImageInfo> image_infos; + std::vector<VkWriteDescriptorSet> updates; + image_infos.reserve(2); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, + images.descriptor_sets[Easu], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Easu], + images.descriptor_sets[Rcas], 0)); + + m_device.GetLogical().UpdateDescriptorSets(updates, {}); +} + +void FSR::UploadImages(Scheduler& scheduler) { + if (m_images_ready) { + return; + } + + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) { + ClearColorImage(cmdbuf, *image.images[Easu]); + ClearColorImage(cmdbuf, *image.images[Rcas]); + } + }); + scheduler.Finish(); + + m_images_ready = true; +} + +VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, + VkImageView source_image_view, VkExtent2D input_image_extent, + const Common::Rectangle<f32>& crop_rect) { + Images& images = m_dynamic_images[image_index]; + + VkImage easu_image = *images.images[Easu]; + VkImage rcas_image = *images.images[Rcas]; + VkDescriptorSet easu_descriptor_set = images.descriptor_sets[Easu]; + VkDescriptorSet rcas_descriptor_set = images.descriptor_sets[Rcas]; + VkFramebuffer easu_framebuffer = *images.framebuffers[Easu]; + VkFramebuffer rcas_framebuffer = *images.framebuffers[Rcas]; + VkPipeline easu_pipeline = *m_easu_pipeline; + VkPipeline rcas_pipeline = *m_rcas_pipeline; + VkPipelineLayout pipeline_layout = *m_pipeline_layout; + VkRenderPass renderpass = *m_renderpass; + VkExtent2D extent = m_extent; + + const f32 input_image_width = static_cast<f32>(input_image_extent.width); + const f32 input_image_height = static_cast<f32>(input_image_extent.height); + const f32 output_image_width = static_cast<f32>(extent.width); + const f32 output_image_height = static_cast<f32>(extent.height); + const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; + const f32 viewport_x = crop_rect.left * input_image_width; + const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; + const f32 viewport_y = crop_rect.top * input_image_height; + + PushConstants easu_con{}; + PushConstants rcas_con{}; + FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8, + easu_con.data() + 12, viewport_width, viewport_height, input_image_width, + input_image_height, output_image_width, output_image_height, viewport_x, + viewport_y); + + const float sharpening = + static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; + FsrRcasCon(rcas_con.data(), sharpening); + + UploadImages(scheduler); + UpdateDescriptorSets(source_image_view, image_index); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([=](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, easu_framebuffer, extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, easu_pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, + easu_descriptor_set, {}); + cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, easu_con); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, rcas_framebuffer, extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, rcas_pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, + rcas_descriptor_set, {}); + cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, rcas_con); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL); + }); + + return *images.image_views[Rcas]; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fsr.h b/src/video_core/renderer_vulkan/present/fsr.h new file mode 100644 index 000000000..8602e8146 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/fsr.h @@ -0,0 +1,69 @@ +// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/math_util.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Device; +class Scheduler; + +class FSR { +public: + explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, + VkExtent2D extent); + VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, + VkImageView source_image_view, VkExtent2D input_image_extent, + const Common::Rectangle<f32>& crop_rect); + +private: + void CreateImages(); + void CreateRenderPasses(); + void CreateSampler(); + void CreateShaders(); + void CreateDescriptorPool(); + void CreateDescriptorSetLayout(); + void CreateDescriptorSets(); + void CreatePipelineLayouts(); + void CreatePipelines(); + + void UploadImages(Scheduler& scheduler); + void UpdateDescriptorSets(VkImageView image_view, size_t image_index); + + const Device& m_device; + MemoryAllocator& m_memory_allocator; + const size_t m_image_count; + const VkExtent2D m_extent; + + enum FsrStage { + Easu, + Rcas, + MaxFsrStage, + }; + + vk::DescriptorPool m_descriptor_pool; + vk::DescriptorSetLayout m_descriptor_set_layout; + vk::PipelineLayout m_pipeline_layout; + vk::ShaderModule m_vert_shader; + vk::ShaderModule m_easu_shader; + vk::ShaderModule m_rcas_shader; + vk::Pipeline m_easu_pipeline; + vk::Pipeline m_rcas_pipeline; + vk::RenderPass m_renderpass; + vk::Sampler m_sampler; + + struct Images { + vk::DescriptorSets descriptor_sets; + std::array<vk::Image, MaxFsrStage> images; + std::array<vk::ImageView, MaxFsrStage> image_views; + std::array<vk::Framebuffer, MaxFsrStage> framebuffers; + }; + std::vector<Images> m_dynamic_images; + bool m_images_ready{}; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fxaa.cpp b/src/video_core/renderer_vulkan/present/fxaa.cpp new file mode 100644 index 000000000..bdafd1f4d --- /dev/null +++ b/src/video_core/renderer_vulkan/present/fxaa.cpp @@ -0,0 +1,148 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/common_types.h" + +#include "video_core/host_shaders/fxaa_frag_spv.h" +#include "video_core/host_shaders/fxaa_vert_spv.h" +#include "video_core/renderer_vulkan/present/fxaa.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/vulkan_common/vulkan_device.h" + +namespace Vulkan { + +FXAA::FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) + : m_device(device), m_allocator(allocator), m_extent(extent), + m_image_count(static_cast<u32>(image_count)) { + CreateImages(); + CreateRenderPasses(); + CreateSampler(); + CreateShaders(); + CreateDescriptorPool(); + CreateDescriptorSetLayouts(); + CreateDescriptorSets(); + CreatePipelineLayouts(); + CreatePipelines(); +} + +FXAA::~FXAA() = default; + +void FXAA::CreateImages() { + for (u32 i = 0; i < m_image_count; i++) { + Image& image = m_dynamic_images.emplace_back(); + + image.image = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + image.image_view = + CreateWrappedImageView(m_device, image.image, VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void FXAA::CreateRenderPasses() { + m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + + for (auto& image : m_dynamic_images) { + image.framebuffer = + CreateWrappedFramebuffer(m_device, m_renderpass, image.image_view, m_extent); + } +} + +void FXAA::CreateSampler() { + m_sampler = CreateWrappedSampler(m_device); +} + +void FXAA::CreateShaders() { + m_vertex_shader = CreateWrappedShaderModule(m_device, FXAA_VERT_SPV); + m_fragment_shader = CreateWrappedShaderModule(m_device, FXAA_FRAG_SPV); +} + +void FXAA::CreateDescriptorPool() { + // 2 descriptors, 1 descriptor set per image + m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, m_image_count); +} + +void FXAA::CreateDescriptorSetLayouts() { + m_descriptor_set_layout = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void FXAA::CreateDescriptorSets() { + VkDescriptorSetLayout layout = *m_descriptor_set_layout; + + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, {layout}); + } +} + +void FXAA::CreatePipelineLayouts() { + m_pipeline_layout = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layout); +} + +void FXAA::CreatePipelines() { + m_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, + std::tie(m_vertex_shader, m_fragment_shader)); +} + +void FXAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { + Image& image = m_dynamic_images[image_index]; + std::vector<VkDescriptorImageInfo> image_infos; + std::vector<VkWriteDescriptorSet> updates; + image_infos.reserve(2); + + updates.push_back( + CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 0)); + updates.push_back( + CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 1)); + + m_device.GetLogical().UpdateDescriptorSets(updates, {}); +} + +void FXAA::UploadImages(Scheduler& scheduler) { + if (m_images_ready) { + return; + } + + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& image : m_dynamic_images) { + ClearColorImage(cmdbuf, *image.image); + } + }); + scheduler.Finish(); + + m_images_ready = true; +} + +void FXAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) { + const Image& image{m_dynamic_images[image_index]}; + const VkImage input_image{*inout_image}; + const VkImage output_image{*image.image}; + const VkDescriptorSet descriptor_set{image.descriptor_sets[0]}; + const VkFramebuffer framebuffer{*image.framebuffer}; + const VkRenderPass renderpass{*m_renderpass}; + const VkPipeline pipeline{*m_pipeline}; + const VkPipelineLayout layout{*m_pipeline_layout}; + const VkExtent2D extent{m_extent}; + + UploadImages(scheduler); + UpdateDescriptorSets(*inout_image_view, image_index); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([=](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, renderpass, framebuffer, extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + }); + + *inout_image = *image.image; + *inout_image_view = *image.image_view; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/fxaa.h b/src/video_core/renderer_vulkan/present/fxaa.h new file mode 100644 index 000000000..97a2e5c1c --- /dev/null +++ b/src/video_core/renderer_vulkan/present/fxaa.h @@ -0,0 +1,63 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_vulkan/present/anti_alias_pass.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +class Device; +class Scheduler; +class StagingBufferPool; + +class FXAA final : public AntiAliasPass { +public: + explicit FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, + VkExtent2D extent); + ~FXAA() override; + + void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) override; + +private: + void CreateImages(); + void CreateRenderPasses(); + void CreateSampler(); + void CreateShaders(); + void CreateDescriptorPool(); + void CreateDescriptorSetLayouts(); + void CreateDescriptorSets(); + void CreatePipelineLayouts(); + void CreatePipelines(); + void UpdateDescriptorSets(VkImageView image_view, size_t image_index); + void UploadImages(Scheduler& scheduler); + + const Device& m_device; + MemoryAllocator& m_allocator; + const VkExtent2D m_extent; + const u32 m_image_count; + + vk::ShaderModule m_vertex_shader{}; + vk::ShaderModule m_fragment_shader{}; + vk::DescriptorPool m_descriptor_pool{}; + vk::DescriptorSetLayout m_descriptor_set_layout{}; + vk::PipelineLayout m_pipeline_layout{}; + vk::Pipeline m_pipeline{}; + vk::RenderPass m_renderpass{}; + + struct Image { + vk::DescriptorSets descriptor_sets{}; + vk::Framebuffer framebuffer{}; + vk::Image image{}; + vk::ImageView image_view{}; + }; + std::vector<Image> m_dynamic_images{}; + bool m_images_ready{}; + + vk::Sampler m_sampler{}; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/layer.cpp b/src/video_core/renderer_vulkan/present/layer.cpp new file mode 100644 index 000000000..cfc04be44 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/layer.cpp @@ -0,0 +1,336 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "video_core/renderer_vulkan/vk_rasterizer.h" + +#include "common/settings.h" +#include "video_core/framebuffer_config.h" +#include "video_core/renderer_vulkan/present/fsr.h" +#include "video_core/renderer_vulkan/present/fxaa.h" +#include "video_core/renderer_vulkan/present/layer.h" +#include "video_core/renderer_vulkan/present/present_push_constants.h" +#include "video_core/renderer_vulkan/present/smaa.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_blit_screen.h" +#include "video_core/textures/decoders.h" + +namespace Vulkan { + +namespace { + +u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { + using namespace VideoCore::Surface; + return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); +} + +std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { + return static_cast<std::size_t>(framebuffer.stride) * + static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); +} + +VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { + switch (framebuffer.pixel_format) { + case Service::android::PixelFormat::Rgba8888: + case Service::android::PixelFormat::Rgbx8888: + return VK_FORMAT_A8B8G8R8_UNORM_PACK32; + case Service::android::PixelFormat::Rgb565: + return VK_FORMAT_R5G6B5_UNORM_PACK16; + case Service::android::PixelFormat::Bgra8888: + return VK_FORMAT_B8G8R8A8_UNORM; + default: + UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", + static_cast<u32>(framebuffer.pixel_format)); + return VK_FORMAT_A8B8G8R8_UNORM_PACK32; + } +} + +} // Anonymous namespace + +Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, + Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, + VkExtent2D output_size, VkDescriptorSetLayout layout) + : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), + device_memory(device_memory_), image_count(image_count_) { + CreateDescriptorPool(); + CreateDescriptorSets(layout); + if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { + CreateFSR(output_size); + } +} + +Layer::~Layer() { + ReleaseRawImages(); +} + +void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, + VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, + VkSampler sampler, size_t image_index, + const Tegra::FramebufferConfig& framebuffer, + const Layout::FramebufferLayout& layout) { + const auto texture_info = rasterizer.AccelerateDisplay( + framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride); + const u32 texture_width = texture_info ? texture_info->width : framebuffer.width; + const u32 texture_height = texture_info ? texture_info->height : framebuffer.height; + const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width; + const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; + const bool use_accelerated = texture_info.has_value(); + + RefreshResources(framebuffer); + SetAntiAliasPass(); + + // Finish any pending renderpass + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Wait(resource_ticks[image_index]); + SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); }); + + if (!use_accelerated) { + UpdateRawImage(framebuffer, image_index); + } + + VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index]; + VkImageView source_image_view = + texture_info ? texture_info->image_view : *raw_image_views[image_index]; + + anti_alias->Draw(scheduler, image_index, &source_image, &source_image_view); + + auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); + const VkExtent2D render_extent{ + .width = scaled_width, + .height = scaled_height, + }; + + if (fsr) { + source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view, + render_extent, crop_rect); + crop_rect = {0, 0, 1, 1}; + } + + SetMatrixData(*out_push_constants, layout); + SetVertexData(*out_push_constants, layout, crop_rect); + + UpdateDescriptorSet(source_image_view, sampler, image_index); + *out_descriptor_set = descriptor_sets[image_index]; +} + +void Layer::CreateDescriptorPool() { + descriptor_pool = CreateWrappedDescriptorPool(device, image_count, image_count); +} + +void Layer::CreateDescriptorSets(VkDescriptorSetLayout layout) { + const std::vector layouts(image_count, layout); + descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); +} + +void Layer::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { + const VkBufferCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = CalculateBufferSize(framebuffer), + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | + VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + + buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); +} + +void Layer::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { + const auto format = GetFormat(framebuffer); + resource_ticks.resize(image_count); + raw_images.resize(image_count); + raw_image_views.resize(image_count); + + for (size_t i = 0; i < image_count; ++i) { + raw_images[i] = + CreateWrappedImage(memory_allocator, {framebuffer.width, framebuffer.height}, format); + raw_image_views[i] = CreateWrappedImageView(device, raw_images[i], format); + } +} + +void Layer::CreateFSR(VkExtent2D output_size) { + fsr = std::make_unique<FSR>(device, memory_allocator, image_count, output_size); +} + +void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { + if (framebuffer.width == raw_width && framebuffer.height == raw_height && + framebuffer.pixel_format == pixel_format && !raw_images.empty()) { + return; + } + + raw_width = framebuffer.width; + raw_height = framebuffer.height; + pixel_format = framebuffer.pixel_format; + anti_alias.reset(); + + ReleaseRawImages(); + CreateStagingBuffer(framebuffer); + CreateRawImages(framebuffer); +} + +void Layer::SetAntiAliasPass() { + if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) { + return; + } + + anti_alias_setting = Settings::values.anti_aliasing.GetValue(); + + const VkExtent2D render_area{ + .width = Settings::values.resolution_info.ScaleUp(raw_width), + .height = Settings::values.resolution_info.ScaleUp(raw_height), + }; + + switch (anti_alias_setting) { + case Settings::AntiAliasing::Fxaa: + anti_alias = std::make_unique<FXAA>(device, memory_allocator, image_count, render_area); + break; + case Settings::AntiAliasing::Smaa: + anti_alias = std::make_unique<SMAA>(device, memory_allocator, image_count, render_area); + break; + default: + anti_alias = std::make_unique<NoAA>(); + break; + } +} + +void Layer::ReleaseRawImages() { + for (const u64 tick : resource_ticks) { + scheduler.Wait(tick); + } + raw_images.clear(); + buffer.reset(); +} + +u64 Layer::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const { + return GetSizeInBytes(framebuffer) * image_count; +} + +u64 Layer::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, + size_t image_index) const { + return GetSizeInBytes(framebuffer) * image_index; +} + +void Layer::SetMatrixData(PresentPushConstants& data, + const Layout::FramebufferLayout& layout) const { + data.modelview_matrix = + MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); +} + +void Layer::SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, + const Common::Rectangle<f32>& crop) const { + // Map the coordinates to the screen. + const auto& screen = layout.screen; + const auto x = static_cast<f32>(screen.left); + const auto y = static_cast<f32>(screen.top); + const auto w = static_cast<f32>(screen.GetWidth()); + const auto h = static_cast<f32>(screen.GetHeight()); + + data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); + data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); + data.vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); + data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); +} + +void Layer::UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index) { + const VkDescriptorImageInfo image_info{ + .sampler = sampler, + .imageView = image_view, + .imageLayout = VK_IMAGE_LAYOUT_GENERAL, + }; + + const VkWriteDescriptorSet sampler_write{ + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .pNext = nullptr, + .dstSet = descriptor_sets[image_index], + .dstBinding = 0, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .pImageInfo = &image_info, + .pBufferInfo = nullptr, + .pTexelBufferView = nullptr, + }; + + device.GetLogical().UpdateDescriptorSets(std::array{sampler_write}, {}); +} + +void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index) { + const std::span<u8> mapped_span = buffer.Mapped(); + + const u64 image_offset = GetRawImageOffset(framebuffer, image_index); + + const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset; + const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr); + + // TODO(Rodrigo): Read this from HLE + constexpr u32 block_height_log2 = 4; + const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); + const u64 linear_size{GetSizeInBytes(framebuffer)}; + const u64 tiled_size{Tegra::Texture::CalculateSize( + true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; + Tegra::Texture::UnswizzleTexture( + mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), + bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); + + const VkBufferImageCopy copy{ + .bufferOffset = image_offset, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource = + { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset = {.x = 0, .y = 0, .z = 0}, + .imageExtent = + { + .width = framebuffer.width, + .height = framebuffer.height, + .depth = 1, + }, + }; + scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { + const VkImage image = *raw_images[index]; + const VkImageMemoryBarrier base_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = 0, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }, + }; + VkImageMemoryBarrier read_barrier = base_barrier; + read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + read_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + VkImageMemoryBarrier write_barrier = base_barrier; + write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + read_barrier); + cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, write_barrier); + }); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/layer.h b/src/video_core/renderer_vulkan/present/layer.h new file mode 100644 index 000000000..88d43fc5f --- /dev/null +++ b/src/video_core/renderer_vulkan/present/layer.h @@ -0,0 +1,92 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/math_util.h" +#include "video_core/host1x/gpu_device_memory_manager.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Tegra { +struct FramebufferConfig; +} + +namespace Service::android { +enum class PixelFormat : u32; +} + +namespace Settings { +enum class AntiAliasing : u32; +} + +namespace Vulkan { + +class AntiAliasPass; +class Device; +class FSR; +class MemoryAllocator; +struct PresentPushConstants; +class RasterizerVulkan; +class Scheduler; + +class Layer final { +public: + explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, + Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, + VkExtent2D output_size, VkDescriptorSetLayout layout); + ~Layer(); + + void ConfigureDraw(PresentPushConstants* out_push_constants, + VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, + VkSampler sampler, size_t image_index, + const Tegra::FramebufferConfig& framebuffer, + const Layout::FramebufferLayout& layout); + +private: + void CreateDescriptorPool(); + void CreateDescriptorSets(VkDescriptorSetLayout layout); + void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); + void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); + void CreateFSR(VkExtent2D output_size); + + void RefreshResources(const Tegra::FramebufferConfig& framebuffer); + void SetAntiAliasPass(); + void ReleaseRawImages(); + + u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; + u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, size_t image_index) const; + + void SetMatrixData(PresentPushConstants& data, const Layout::FramebufferLayout& layout) const; + void SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, + const Common::Rectangle<f32>& crop) const; + void UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index); + void UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index); + +private: + const Device& device; + MemoryAllocator& memory_allocator; + Scheduler& scheduler; + Tegra::MaxwellDeviceMemoryManager& device_memory; + const size_t image_count{}; + vk::DescriptorPool descriptor_pool{}; + vk::DescriptorSets descriptor_sets{}; + + vk::Buffer buffer{}; + std::vector<vk::Image> raw_images{}; + std::vector<vk::ImageView> raw_image_views{}; + u32 raw_width{}; + u32 raw_height{}; + Service::android::PixelFormat pixel_format{}; + + Settings::AntiAliasing anti_alias_setting{}; + std::unique_ptr<AntiAliasPass> anti_alias{}; + + std::unique_ptr<FSR> fsr{}; + std::vector<u64> resource_ticks{}; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/present_push_constants.h b/src/video_core/renderer_vulkan/present/present_push_constants.h new file mode 100644 index 000000000..f1949e7aa --- /dev/null +++ b/src/video_core/renderer_vulkan/present/present_push_constants.h @@ -0,0 +1,34 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/common_types.h" + +namespace Vulkan { + +struct ScreenRectVertex { + ScreenRectVertex() = default; + explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} + + std::array<f32, 2> position; + std::array<f32, 2> tex_coord; +}; + +static inline std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { + // clang-format off + return { 2.f / width, 0.f, 0.f, 0.f, + 0.f, 2.f / height, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + -1.f, -1.f, 0.f, 1.f}; + // clang-format on +} + +struct PresentPushConstants { + std::array<f32, 4 * 4> modelview_matrix; + std::array<ScreenRectVertex, 4> vertices; +}; + +static_assert(sizeof(PresentPushConstants) <= 128, "Push constants are too large"); + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/smaa.cpp b/src/video_core/renderer_vulkan/present/smaa.cpp new file mode 100644 index 000000000..39645fd1d --- /dev/null +++ b/src/video_core/renderer_vulkan/present/smaa.cpp @@ -0,0 +1,277 @@ +// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include <list> + +#include "common/assert.h" +#include "common/polyfill_ranges.h" + +#include "video_core/renderer_vulkan/present/smaa.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/smaa_area_tex.h" +#include "video_core/smaa_search_tex.h" +#include "video_core/vulkan_common/vulkan_device.h" + +#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h" +#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h" +#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h" +#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h" +#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h" + +namespace Vulkan { + +SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) + : m_device(device), m_allocator(allocator), m_extent(extent), + m_image_count(static_cast<u32>(image_count)) { + CreateImages(); + CreateRenderPasses(); + CreateSampler(); + CreateShaders(); + CreateDescriptorPool(); + CreateDescriptorSetLayouts(); + CreateDescriptorSets(); + CreatePipelineLayouts(); + CreatePipelines(); +} + +SMAA::~SMAA() = default; + +void SMAA::CreateImages() { + static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; + static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; + + m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM); + m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM); + + m_static_image_views[Area] = + CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); + m_static_image_views[Search] = + CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM); + + for (u32 i = 0; i < m_image_count; i++) { + Images& images = m_dynamic_images.emplace_back(); + + images.images[Blend] = + CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); + images.images[Output] = + CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); + + images.image_views[Blend] = + CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); + images.image_views[Edges] = + CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); + images.image_views[Output] = + CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); + } +} + +void SMAA::CreateRenderPasses() { + m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT); + m_renderpasses[BlendingWeightCalculation] = + CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + m_renderpasses[NeighborhoodBlending] = + CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); + + for (auto& images : m_dynamic_images) { + images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( + m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); + + images.framebuffers[BlendingWeightCalculation] = + CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation], + images.image_views[Blend], m_extent); + + images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( + m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); + } +} + +void SMAA::CreateSampler() { + m_sampler = CreateWrappedSampler(m_device); +} + +void SMAA::CreateShaders() { + // These match the order of the SMAAStage enum + static constexpr std::array vert_shader_sources{ + ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV), + ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV), + ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV), + }; + static constexpr std::array frag_shader_sources{ + ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV), + ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV), + ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV), + }; + + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]); + m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]); + } +} + +void SMAA::CreateDescriptorPool() { + // Edge detection: 1 descriptor + // Blending weight calculation: 3 descriptors + // Neighborhood blending: 2 descriptors + + // 6 descriptors, 3 descriptor sets per image + m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count); +} + +void SMAA::CreateDescriptorSetLayouts() { + m_descriptor_set_layouts[EdgeDetection] = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); + m_descriptor_set_layouts[BlendingWeightCalculation] = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); + m_descriptor_set_layouts[NeighborhoodBlending] = + CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void SMAA::CreateDescriptorSets() { + std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size()); + std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), + [](auto& layout) { return *layout; }); + + for (auto& images : m_dynamic_images) { + images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); + } +} + +void SMAA::CreatePipelineLayouts() { + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]); + } +} + +void SMAA::CreatePipelines() { + for (size_t i = 0; i < MaxSMAAStage; i++) { + m_pipelines[i] = + CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i], + std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); + } +} + +void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { + Images& images = m_dynamic_images[image_index]; + std::vector<VkDescriptorImageInfo> image_infos; + std::vector<VkWriteDescriptorSet> updates; + image_infos.reserve(6); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, + images.descriptor_sets[EdgeDetection], 0)); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges], + images.descriptor_sets[BlendingWeightCalculation], + 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area], + images.descriptor_sets[BlendingWeightCalculation], + 1)); + updates.push_back( + CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search], + images.descriptor_sets[BlendingWeightCalculation], 2)); + + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, + images.descriptor_sets[NeighborhoodBlending], 0)); + updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], + images.descriptor_sets[NeighborhoodBlending], 1)); + + m_device.GetLogical().UpdateDescriptorSets(updates, {}); +} + +void SMAA::UploadImages(Scheduler& scheduler) { + if (m_images_ready) { + return; + } + + static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; + static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; + + UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent, + VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes)); + UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, + VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); + + scheduler.Record([&](vk::CommandBuffer cmdbuf) { + for (auto& images : m_dynamic_images) { + for (size_t i = 0; i < MaxDynamicImage; i++) { + ClearColorImage(cmdbuf, *images.images[i]); + } + } + }); + scheduler.Finish(); + + m_images_ready = true; +} + +void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) { + Images& images = m_dynamic_images[image_index]; + + VkImage input_image = *inout_image; + VkImage output_image = *images.images[Output]; + VkImage edges_image = *images.images[Edges]; + VkImage blend_image = *images.images[Blend]; + + VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection]; + VkDescriptorSet blending_weight_calculation_descriptor_set = + images.descriptor_sets[BlendingWeightCalculation]; + VkDescriptorSet neighborhood_blending_descriptor_set = + images.descriptor_sets[NeighborhoodBlending]; + + VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection]; + VkFramebuffer blending_weight_calculation_framebuffer = + *images.framebuffers[BlendingWeightCalculation]; + VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; + + UploadImages(scheduler); + UpdateDescriptorSets(*inout_image_view, image_index); + + scheduler.RequestOutsideRenderPassOperationContext(); + scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { + TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[EdgeDetection], edge_detection_framebuffer, + m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[EdgeDetection], 0, + edge_detection_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[BlendingWeightCalculation], + blending_weight_calculation_framebuffer, m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipelines[BlendingWeightCalculation]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[BlendingWeightCalculation], 0, + blending_weight_calculation_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + + TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + BeginRenderPass(cmdbuf, *m_renderpasses[NeighborhoodBlending], + neighborhood_blending_framebuffer, m_extent); + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, + *m_pipeline_layouts[NeighborhoodBlending], 0, + neighborhood_blending_descriptor_set, {}); + cmdbuf.Draw(3, 1, 0, 0); + cmdbuf.EndRenderPass(); + TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); + }); + + *inout_image = *images.images[Output]; + *inout_image_view = *images.image_views[Output]; +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_smaa.h b/src/video_core/renderer_vulkan/present/smaa.h index 0e214258a..fdf6def07 100644 --- a/src/video_core/renderer_vulkan/vk_smaa.h +++ b/src/video_core/renderer_vulkan/present/smaa.h @@ -4,6 +4,7 @@ #pragma once #include <array> +#include "video_core/renderer_vulkan/present/anti_alias_pass.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -13,12 +14,14 @@ class Device; class Scheduler; class StagingBufferPool; -class SMAA { +class SMAA final : public AntiAliasPass { public: explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent); - VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, - VkImageView source_image_view); + ~SMAA() override; + + void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, + VkImageView* inout_image_view) override; private: enum SMAAStage { diff --git a/src/video_core/renderer_vulkan/vk_smaa.cpp b/src/video_core/renderer_vulkan/present/util.cpp index 70644ea82..6ee16595d 100644 --- a/src/video_core/renderer_vulkan/vk_smaa.cpp +++ b/src/video_core/renderer_vulkan/present/util.cpp @@ -1,29 +1,25 @@ -// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <list> - #include "common/assert.h" #include "common/polyfill_ranges.h" - -#include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" -#include "video_core/renderer_vulkan/vk_smaa.h" -#include "video_core/smaa_area_tex.h" -#include "video_core/smaa_search_tex.h" -#include "video_core/vulkan_common/vulkan_device.h" - -#include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h" -#include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h" -#include "video_core/host_shaders/smaa_edge_detection_frag_spv.h" -#include "video_core/host_shaders/smaa_edge_detection_vert_spv.h" -#include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h" -#include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h" +#include "video_core/renderer_vulkan/present/util.h" namespace Vulkan { -namespace { -#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) +vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage) { + const VkBufferCreateInfo dst_buffer_info{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .size = size, + .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = nullptr, + }; + return allocator.CreateBuffer(dst_buffer_info, usage); +} vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { const VkImageCreateInfo image_ci{ @@ -48,7 +44,7 @@ vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, } void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, - VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { + VkImageLayout source_layout) { constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; const VkImageMemoryBarrier barrier{ @@ -75,7 +71,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, vk::Image& image, VkExtent2D dimensions, VkFormat format, - std::span<const u8> initial_contents = {}) { + std::span<const u8> initial_contents) { const VkBufferCreateInfo upload_ci = { .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .pNext = nullptr, @@ -114,6 +110,70 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc scheduler.Finish(); } +void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, + VkExtent3D extent) { + const VkImageMemoryBarrier read_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_GENERAL, + .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + const VkImageMemoryBarrier image_write_barrier{ + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = 0, + .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_GENERAL, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .image = image, + .subresourceRange{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = VK_REMAINING_MIP_LEVELS, + .baseArrayLayer = 0, + .layerCount = VK_REMAINING_ARRAY_LAYERS, + }, + }; + static constexpr VkMemoryBarrier memory_write_barrier{ + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, + .pNext = nullptr, + .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, + }; + const VkBufferImageCopy copy{ + .bufferOffset = 0, + .bufferRowLength = 0, + .bufferImageHeight = 0, + .imageSubresource{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .baseArrayLayer = 0, + .layerCount = 1, + }, + .imageOffset{.x = 0, .y = 0, .z = 0}, + .imageExtent{extent}, + }; + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + read_barrier); + cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy); + cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, + memory_write_barrier, nullptr, image_write_barrier); +} + vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, @@ -131,16 +191,18 @@ vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkF }); } -vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format) { +vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, + VkImageLayout initial_layout) { const VkAttachmentDescription attachment{ .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, .format = format, .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, + .loadOp = initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE + : VK_ATTACHMENT_LOAD_OP_LOAD, .storeOp = VK_ATTACHMENT_STORE_OP_STORE, .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, - .initialLayout = VK_IMAGE_LAYOUT_GENERAL, + .initialLayout = initial_layout, .finalLayout = VK_IMAGE_LAYOUT_GENERAL, }; @@ -200,13 +262,13 @@ vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& r }); } -vk::Sampler CreateWrappedSampler(const Device& device) { +vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter) { return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .pNext = nullptr, .flags = 0, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, + .magFilter = filter, + .minFilter = filter, .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, @@ -233,30 +295,34 @@ vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const }); } -vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, u32 max_descriptors, - u32 max_sets) { - const VkDescriptorPoolSize pool_size{ - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = static_cast<u32>(max_descriptors), - }; +vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors, + size_t max_sets, + std::initializer_list<VkDescriptorType> types) { + std::vector<VkDescriptorPoolSize> pool_sizes(types.size()); + for (u32 i = 0; i < types.size(); i++) { + pool_sizes[i] = VkDescriptorPoolSize{ + .type = std::data(types)[i], + .descriptorCount = static_cast<u32>(max_descriptors), + }; + } return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .pNext = nullptr, .flags = 0, - .maxSets = max_sets, - .poolSizeCount = 1, - .pPoolSizes = &pool_size, + .maxSets = static_cast<u32>(max_sets), + .poolSizeCount = static_cast<u32>(pool_sizes.size()), + .pPoolSizes = pool_sizes.data(), }); } -vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(const Device& device, - u32 max_sampler_bindings) { - std::vector<VkDescriptorSetLayoutBinding> bindings(max_sampler_bindings); - for (u32 i = 0; i < max_sampler_bindings; i++) { +vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout( + const Device& device, std::initializer_list<VkDescriptorType> types) { + std::vector<VkDescriptorSetLayoutBinding> bindings(types.size()); + for (size_t i = 0; i < types.size(); i++) { bindings[i] = { - .binding = i, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .binding = static_cast<u32>(i), + .descriptorType = std::data(types)[i], .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = nullptr, @@ -298,7 +364,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, vk::PipelineLayout& layout, - std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, + bool enable_blending) { const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, @@ -376,7 +443,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp .alphaToOneEnable = VK_FALSE, }; - constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{ + constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ .blendEnable = VK_FALSE, .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, @@ -388,6 +455,18 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, }; + constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{ + .blendEnable = VK_TRUE, + .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, + .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, + .colorBlendOp = VK_BLEND_OP_ADD, + .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, + .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, + .alphaBlendOp = VK_BLEND_OP_ADD, + .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | + VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, + }; + const VkPipelineColorBlendStateCreateInfo color_blend_ci{ .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, @@ -395,7 +474,8 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_COPY, .attachmentCount = 1, - .pAttachments = &color_blend_attachment, + .pAttachments = + enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled, .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, }; @@ -459,6 +539,56 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo> }; } +vk::Sampler CreateBilinearSampler(const Device& device) { + const VkSamplerCreateInfo ci{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_LINEAR, + .minFilter = VK_FILTER_LINEAR, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 0.0f, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + }; + + return device.GetLogical().CreateSampler(ci); +} + +vk::Sampler CreateNearestNeighborSampler(const Device& device) { + const VkSamplerCreateInfo ci_nn{ + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .magFilter = VK_FILTER_NEAREST, + .minFilter = VK_FILTER_NEAREST, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 0.0f, + .compareEnable = VK_FALSE, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, + .unnormalizedCoordinates = VK_FALSE, + }; + + return device.GetLogical().CreateSampler(ci_nn); +} + void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{ .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, @@ -471,12 +601,12 @@ void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources); } -void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, - VkFramebuffer framebuffer, VkExtent2D extent) { +void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, + VkExtent2D extent) { const VkRenderPassBeginInfo renderpass_bi{ .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, .pNext = nullptr, - .renderPass = *render_pass, + .renderPass = render_pass, .framebuffer = framebuffer, .renderArea{ .offset{}, @@ -503,248 +633,4 @@ void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, cmdbuf.SetScissor(0, scissor); } -} // Anonymous namespace - -SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) - : m_device(device), m_allocator(allocator), m_extent(extent), - m_image_count(static_cast<u32>(image_count)) { - CreateImages(); - CreateRenderPasses(); - CreateSampler(); - CreateShaders(); - CreateDescriptorPool(); - CreateDescriptorSetLayouts(); - CreateDescriptorSets(); - CreatePipelineLayouts(); - CreatePipelines(); -} - -void SMAA::CreateImages() { - static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; - static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; - - m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM); - m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM); - - m_static_image_views[Area] = - CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); - m_static_image_views[Search] = - CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM); - - for (u32 i = 0; i < m_image_count; i++) { - Images& images = m_dynamic_images.emplace_back(); - - images.images[Blend] = - CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); - images.images[Output] = - CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); - - images.image_views[Blend] = - CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); - images.image_views[Edges] = - CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); - images.image_views[Output] = - CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); - } -} - -void SMAA::CreateRenderPasses() { - m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT); - m_renderpasses[BlendingWeightCalculation] = - CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); - m_renderpasses[NeighborhoodBlending] = - CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); - - for (auto& images : m_dynamic_images) { - images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( - m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); - - images.framebuffers[BlendingWeightCalculation] = - CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation], - images.image_views[Blend], m_extent); - - images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( - m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); - } -} - -void SMAA::CreateSampler() { - m_sampler = CreateWrappedSampler(m_device); -} - -void SMAA::CreateShaders() { - // These match the order of the SMAAStage enum - static constexpr std::array vert_shader_sources{ - ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV), - ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV), - ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV), - }; - static constexpr std::array frag_shader_sources{ - ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV), - ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV), - ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV), - }; - - for (size_t i = 0; i < MaxSMAAStage; i++) { - m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]); - m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]); - } -} - -void SMAA::CreateDescriptorPool() { - // Edge detection: 1 descriptor - // Blending weight calculation: 3 descriptors - // Neighborhood blending: 2 descriptors - - // 6 descriptors, 3 descriptor sets per image - m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count); -} - -void SMAA::CreateDescriptorSetLayouts() { - m_descriptor_set_layouts[EdgeDetection] = CreateWrappedDescriptorSetLayout(m_device, 1); - m_descriptor_set_layouts[BlendingWeightCalculation] = - CreateWrappedDescriptorSetLayout(m_device, 3); - m_descriptor_set_layouts[NeighborhoodBlending] = CreateWrappedDescriptorSetLayout(m_device, 2); -} - -void SMAA::CreateDescriptorSets() { - std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size()); - std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), - [](auto& layout) { return *layout; }); - - for (auto& images : m_dynamic_images) { - images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); - } -} - -void SMAA::CreatePipelineLayouts() { - for (size_t i = 0; i < MaxSMAAStage; i++) { - m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]); - } -} - -void SMAA::CreatePipelines() { - for (size_t i = 0; i < MaxSMAAStage; i++) { - m_pipelines[i] = - CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i], - std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); - } -} - -void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { - Images& images = m_dynamic_images[image_index]; - std::vector<VkDescriptorImageInfo> image_infos; - std::vector<VkWriteDescriptorSet> updates; - image_infos.reserve(6); - - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, - images.descriptor_sets[EdgeDetection], 0)); - - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges], - images.descriptor_sets[BlendingWeightCalculation], - 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area], - images.descriptor_sets[BlendingWeightCalculation], - 1)); - updates.push_back( - CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search], - images.descriptor_sets[BlendingWeightCalculation], 2)); - - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, - images.descriptor_sets[NeighborhoodBlending], 0)); - updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], - images.descriptor_sets[NeighborhoodBlending], 1)); - - m_device.GetLogical().UpdateDescriptorSets(updates, {}); -} - -void SMAA::UploadImages(Scheduler& scheduler) { - if (m_images_ready) { - return; - } - - static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; - static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; - - UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent, - VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes)); - UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, - VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); - - scheduler.Record([&](vk::CommandBuffer cmdbuf) { - for (auto& images : m_dynamic_images) { - for (size_t i = 0; i < MaxDynamicImage; i++) { - ClearColorImage(cmdbuf, *images.images[i]); - } - } - }); - scheduler.Finish(); - - m_images_ready = true; -} - -VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, - VkImageView source_image_view) { - Images& images = m_dynamic_images[image_index]; - - VkImage output_image = *images.images[Output]; - VkImage edges_image = *images.images[Edges]; - VkImage blend_image = *images.images[Blend]; - - VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection]; - VkDescriptorSet blending_weight_calculation_descriptor_set = - images.descriptor_sets[BlendingWeightCalculation]; - VkDescriptorSet neighborhood_blending_descriptor_set = - images.descriptor_sets[NeighborhoodBlending]; - - VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection]; - VkFramebuffer blending_weight_calculation_framebuffer = - *images.framebuffers[BlendingWeightCalculation]; - VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; - - UploadImages(scheduler); - UpdateDescriptorSets(source_image_view, image_index); - - scheduler.RequestOutsideRenderPassOperationContext(); - scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { - TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); - BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, - m_extent); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, - *m_pipeline_layouts[EdgeDetection], 0, - edge_detection_descriptor_set, {}); - cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRenderPass(); - - TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); - BeginRenderPass(cmdbuf, m_renderpasses[BlendingWeightCalculation], - blending_weight_calculation_framebuffer, m_extent); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, - *m_pipelines[BlendingWeightCalculation]); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, - *m_pipeline_layouts[BlendingWeightCalculation], 0, - blending_weight_calculation_descriptor_set, {}); - cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRenderPass(); - - TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); - TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); - BeginRenderPass(cmdbuf, m_renderpasses[NeighborhoodBlending], - neighborhood_blending_framebuffer, m_extent); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, - *m_pipeline_layouts[NeighborhoodBlending], 0, - neighborhood_blending_descriptor_set, {}); - cmdbuf.Draw(3, 1, 0, 0); - cmdbuf.EndRenderPass(); - TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); - }); - - return *images.image_views[Output]; -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/util.h b/src/video_core/renderer_vulkan/present/util.h new file mode 100644 index 000000000..1104aaa15 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/util.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "video_core/renderer_vulkan/vk_scheduler.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Vulkan { + +#define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) + +vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage); + +vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format); +void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, + VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL); +void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, + vk::Image& image, VkExtent2D dimensions, VkFormat format, + std::span<const u8> initial_contents = {}); +void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, + VkExtent3D extent); +void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); + +vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); +vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, + VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL); +vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass, + vk::ImageView& dest_image, VkExtent2D extent); +vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter = VK_FILTER_LINEAR); +vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const u32> code); +vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors, + size_t max_sets, + std::initializer_list<VkDescriptorType> types = { + VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout( + const Device& device, std::initializer_list<VkDescriptorType> types); +vk::DescriptorSets CreateWrappedDescriptorSets(vk::DescriptorPool& pool, + vk::Span<VkDescriptorSetLayout> layouts); +vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, + vk::DescriptorSetLayout& layout); +vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, + vk::PipelineLayout& layout, + std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, + bool enable_blending = false); +VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, + VkSampler sampler, VkImageView view, + VkDescriptorSet set, u32 binding); +vk::Sampler CreateBilinearSampler(const Device& device); +vk::Sampler CreateNearestNeighborSampler(const Device& device); + +void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, + VkExtent2D extent); + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp new file mode 100644 index 000000000..c5db0230d --- /dev/null +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.cpp @@ -0,0 +1,137 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "core/frontend/framebuffer_layout.h" +#include "video_core/framebuffer_config.h" +#include "video_core/host_shaders/vulkan_present_vert_spv.h" +#include "video_core/renderer_vulkan/present/layer.h" +#include "video_core/renderer_vulkan/present/present_push_constants.h" +#include "video_core/renderer_vulkan/present/util.h" +#include "video_core/renderer_vulkan/present/window_adapt_pass.h" +#include "video_core/renderer_vulkan/vk_present_manager.h" +#include "video_core/renderer_vulkan/vk_shader_util.h" +#include "video_core/vulkan_common/vulkan_device.h" +#include "video_core/vulkan_common/vulkan_memory_allocator.h" + +namespace Vulkan { + +WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format, + vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_) + : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) { + CreateDescriptorSetLayout(); + CreatePipelineLayout(); + CreateVertexShader(); + CreateRenderPass(frame_format); + CreatePipeline(); +} + +WindowAdaptPass::~WindowAdaptPass() = default; + +void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, + std::list<Layer>& layers, + std::span<const Tegra::FramebufferConfig> configs, + const Layout::FramebufferLayout& layout, Frame* dst) { + + const VkFramebuffer host_framebuffer{*dst->framebuffer}; + const VkRenderPass renderpass{*render_pass}; + const VkPipeline graphics_pipeline{*pipeline}; + const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; + const VkExtent2D render_area{ + .width = dst->width, + .height = dst->height, + }; + + const size_t layer_count = configs.size(); + std::vector<PresentPushConstants> push_constants(layer_count); + std::vector<VkDescriptorSet> descriptor_sets(layer_count); + + auto layer_it = layers.begin(); + for (size_t i = 0; i < layer_count; i++) { + layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, + image_index, configs[i], layout); + layer_it++; + } + + scheduler.Record([=](vk::CommandBuffer cmdbuf) { + const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; + const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; + const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; + const VkClearAttachment clear_attachment{ + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .colorAttachment = 0, + .clearValue = + { + .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, + }, + }; + const VkClearRect clear_rect{ + .rect = + { + .offset = {0, 0}, + .extent = render_area, + }, + .baseArrayLayer = 0, + .layerCount = 1, + }; + + BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); + cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); + + cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); + for (size_t i = 0; i < layer_count; i++) { + cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, + push_constants[i]); + cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, + descriptor_sets[i], {}); + cmdbuf.Draw(4, 1, 0, 0); + } + + cmdbuf.EndRenderPass(); + }); +} + +VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() { + return *descriptor_set_layout; +} + +VkRenderPass WindowAdaptPass::GetRenderPass() { + return *render_pass; +} + +void WindowAdaptPass::CreateDescriptorSetLayout() { + descriptor_set_layout = + CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); +} + +void WindowAdaptPass::CreatePipelineLayout() { + const VkPushConstantRange range{ + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, + .offset = 0, + .size = sizeof(PresentPushConstants), + }; + + pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .setLayoutCount = 1, + .pSetLayouts = descriptor_set_layout.address(), + .pushConstantRangeCount = 1, + .pPushConstantRanges = &range, + }); +} + +void WindowAdaptPass::CreateVertexShader() { + vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); +} + +void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { + render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); +} + +void WindowAdaptPass::CreatePipeline() { + pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, + std::tie(vertex_shader, fragment_shader), false); +} + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/present/window_adapt_pass.h b/src/video_core/renderer_vulkan/present/window_adapt_pass.h new file mode 100644 index 000000000..0e2edfc31 --- /dev/null +++ b/src/video_core/renderer_vulkan/present/window_adapt_pass.h @@ -0,0 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include <list> + +#include "common/math_util.h" +#include "video_core/vulkan_common/vulkan_wrapper.h" + +namespace Layout { +struct FramebufferLayout; +} + +namespace Tegra { +struct FramebufferConfig; +} + +namespace Vulkan { + +class Device; +struct Frame; +class Layer; +class Scheduler; +class RasterizerVulkan; + +class WindowAdaptPass final { +public: + explicit WindowAdaptPass(const Device& device, VkFormat frame_format, vk::Sampler&& sampler, + vk::ShaderModule&& fragment_shader); + ~WindowAdaptPass(); + + void Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, + std::list<Layer>& layers, std::span<const Tegra::FramebufferConfig> configs, + const Layout::FramebufferLayout& layout, Frame* dst); + + VkDescriptorSetLayout GetDescriptorSetLayout(); + VkRenderPass GetRenderPass(); + +private: + void CreateDescriptorSetLayout(); + void CreatePipelineLayout(); + void CreateVertexShader(); + void CreateRenderPass(VkFormat frame_format); + void CreatePipeline(); + +private: + const Device& device; + vk::DescriptorSetLayout descriptor_set_layout; + vk::PipelineLayout pipeline_layout; + vk::Sampler sampler; + vk::ShaderModule vertex_shader; + vk::ShaderModule fragment_shader; + vk::RenderPass render_pass; + vk::Pipeline pipeline; +}; + +} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp index 1631276c6..48a105327 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp +++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp @@ -20,12 +20,14 @@ #include "core/frontend/graphics_context.h" #include "core/telemetry_session.h" #include "video_core/gpu.h" +#include "video_core/renderer_vulkan/present/util.h" #include "video_core/renderer_vulkan/renderer_vulkan.h" #include "video_core/renderer_vulkan/vk_blit_screen.h" #include "video_core/renderer_vulkan/vk_rasterizer.h" #include "video_core/renderer_vulkan/vk_scheduler.h" #include "video_core/renderer_vulkan/vk_state_tracker.h" #include "video_core/renderer_vulkan/vk_swapchain.h" +#include "video_core/textures/decoders.h" #include "video_core/vulkan_common/vulkan_debug_callback.h" #include "video_core/vulkan_common/vulkan_device.h" #include "video_core/vulkan_common/vulkan_instance.h" @@ -97,10 +99,10 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, render_window.GetFramebufferLayout().height), present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, surface), - blit_screen(device_memory, render_window, device, memory_allocator, swapchain, - present_manager, scheduler, screen_info), - rasterizer(render_window, gpu, device_memory, screen_info, device, memory_allocator, - state_tracker, scheduler) { + blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler), + blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler), + rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, + scheduler) { if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { turbo_mode.emplace(instance, dld); scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); @@ -116,25 +118,22 @@ RendererVulkan::~RendererVulkan() { void(device.GetLogical().WaitIdle()); } -void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { - if (!framebuffer) { +void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { + if (framebuffers.empty()) { return; } + SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); + if (!render_window.IsShown()) { return; } - // Update screen info if the framebuffer size has changed. - screen_info.width = framebuffer->width; - screen_info.height = framebuffer->height; - - const DAddr framebuffer_addr = framebuffer->address + framebuffer->offset; - const bool use_accelerated = - rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); - RenderScreenshot(*framebuffer, use_accelerated); + RenderScreenshot(framebuffers); Frame* frame = present_manager.GetRenderFrame(); - blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated); + blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers, + render_window.GetFramebufferLayout(), swapchain.GetImageCount(), + swapchain.GetImageViewFormat()); scheduler.Flush(*frame->render_ready); present_manager.Present(frame); @@ -168,143 +167,37 @@ void RendererVulkan::Report() const { telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); } -void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, - bool use_accelerated) { +void Vulkan::RendererVulkan::RenderScreenshot( + std::span<const Tegra::FramebufferConfig> framebuffers) { if (!renderer_settings.screenshot_requested) { return; } + + constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM}; const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; - vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, - .imageType = VK_IMAGE_TYPE_2D, - .format = VK_FORMAT_B8G8R8A8_UNORM, - .extent = - { - .width = layout.width, - .height = layout.height, - .depth = 1, - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | - VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }); - const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .image = *staging_image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = VK_FORMAT_B8G8R8A8_UNORM, - .components{ - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }); - const VkExtent2D render_area{.width = layout.width, .height = layout.height}; - const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area); - blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated); + auto frame = [&]() { + Frame f{}; + f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, + ScreenshotFormat); + f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat); + f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat); + return f; + }(); - const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); - const VkBufferCreateInfo dst_buffer_info{ - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = buffer_size, - .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - }; - const vk::Buffer dst_buffer = - memory_allocator.CreateBuffer(dst_buffer_info, MemoryUsage::Download); + blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, + VK_FORMAT_B8G8R8A8_UNORM); + + const auto dst_buffer = CreateWrappedBuffer( + memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4), + MemoryUsage::Download); scheduler.RequestOutsideRenderPassOperationContext(); scheduler.Record([&](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier read_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, - .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = *staging_image, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - const VkImageMemoryBarrier image_write_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, - .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = *staging_image, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = VK_REMAINING_MIP_LEVELS, - .baseArrayLayer = 0, - .layerCount = VK_REMAINING_ARRAY_LAYERS, - }, - }; - static constexpr VkMemoryBarrier memory_write_barrier{ - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, - .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, - }; - const VkBufferImageCopy copy{ - .bufferOffset = 0, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .imageOffset{.x = 0, .y = 0, .z = 0}, - .imageExtent{ - .width = layout.width, - .height = layout.height, - .depth = 1, - }, - }; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, - 0, read_barrier); - cmdbuf.CopyImageToBuffer(*staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer, - copy); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - 0, memory_write_barrier, nullptr, image_write_barrier); + DownloadColorImage(cmdbuf, *frame.image, *dst_buffer, + VkExtent3D{layout.width, layout.height, 1}); }); + // Ensure the copy is fully completed before saving the screenshot scheduler.Finish(); diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h index 11c52287a..c6d8a0f21 100644 --- a/src/video_core/renderer_vulkan/renderer_vulkan.h +++ b/src/video_core/renderer_vulkan/renderer_vulkan.h @@ -46,7 +46,7 @@ public: std::unique_ptr<Core::Frontend::GraphicsContext> context_); ~RendererVulkan() override; - void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; + void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; VideoCore::RasterizerInterface* ReadRasterizer() override { return &rasterizer; @@ -59,7 +59,7 @@ public: private: void Report() const; - void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated); + void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); Core::TelemetrySession& telemetry_session; Tegra::MaxwellDeviceMemoryManager& device_memory; @@ -72,15 +72,14 @@ private: vk::DebugUtilsMessenger debug_messenger; vk::SurfaceKHR surface; - ScreenInfo screen_info; - Device device; MemoryAllocator memory_allocator; StateTracker state_tracker; Scheduler scheduler; Swapchain swapchain; PresentManager present_manager; - BlitScreen blit_screen; + BlitScreen blit_swapchain; + BlitScreen blit_screenshot; RasterizerVulkan rasterizer; std::optional<TurboMode> turbo_mode; }; diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index 610f27c84..2275fcc46 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -1,522 +1,143 @@ // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include <algorithm> -#include <array> -#include <cstring> -#include <memory> -#include <vector> - -#include "common/assert.h" -#include "common/common_types.h" -#include "common/math_util.h" -#include "common/polyfill_ranges.h" -#include "common/settings.h" -#include "core/core.h" -#include "core/frontend/emu_window.h" -#include "video_core/gpu.h" -#include "video_core/host1x/gpu_device_memory_manager.h" -#include "video_core/host_shaders/fxaa_frag_spv.h" -#include "video_core/host_shaders/fxaa_vert_spv.h" -#include "video_core/host_shaders/present_bicubic_frag_spv.h" -#include "video_core/host_shaders/present_gaussian_frag_spv.h" -#include "video_core/host_shaders/vulkan_present_frag_spv.h" -#include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" -#include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" -#include "video_core/host_shaders/vulkan_present_vert_spv.h" -#include "video_core/renderer_vulkan/renderer_vulkan.h" +#include "video_core/framebuffer_config.h" +#include "video_core/renderer_vulkan/present/filters.h" +#include "video_core/renderer_vulkan/present/layer.h" #include "video_core/renderer_vulkan/vk_blit_screen.h" -#include "video_core/renderer_vulkan/vk_fsr.h" +#include "video_core/renderer_vulkan/vk_present_manager.h" #include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" -#include "video_core/renderer_vulkan/vk_smaa.h" -#include "video_core/renderer_vulkan/vk_swapchain.h" -#include "video_core/surface.h" -#include "video_core/textures/decoders.h" -#include "video_core/vulkan_common/vulkan_device.h" -#include "video_core/vulkan_common/vulkan_memory_allocator.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" namespace Vulkan { -namespace { - -struct ScreenRectVertex { - ScreenRectVertex() = default; - explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} - - std::array<f32, 2> position; - std::array<f32, 2> tex_coord; - - static VkVertexInputBindingDescription GetDescription() { - return { - .binding = 0, - .stride = sizeof(ScreenRectVertex), - .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, - }; - } - - static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() { - return {{ - { - .location = 0, - .binding = 0, - .format = VK_FORMAT_R32G32_SFLOAT, - .offset = offsetof(ScreenRectVertex, position), - }, - { - .location = 1, - .binding = 0, - .format = VK_FORMAT_R32G32_SFLOAT, - .offset = offsetof(ScreenRectVertex, tex_coord), - }, - }}; - } -}; +BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, + MemoryAllocator& memory_allocator_, PresentManager& present_manager_, + Scheduler& scheduler_) + : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, + present_manager{present_manager_}, scheduler{scheduler_}, image_count{1}, + swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} -std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { - // clang-format off - return { 2.f / width, 0.f, 0.f, 0.f, - 0.f, 2.f / height, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - -1.f, -1.f, 0.f, 1.f}; - // clang-format on -} - -u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { - using namespace VideoCore::Surface; - return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); -} +BlitScreen::~BlitScreen() = default; -std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { - return static_cast<std::size_t>(framebuffer.stride) * - static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); +void BlitScreen::WaitIdle() { + present_manager.WaitPresent(); + scheduler.Finish(); + device.GetLogical().WaitIdle(); } -VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { - switch (framebuffer.pixel_format) { - case Service::android::PixelFormat::Rgba8888: - case Service::android::PixelFormat::Rgbx8888: - return VK_FORMAT_A8B8G8R8_UNORM_PACK32; - case Service::android::PixelFormat::Rgb565: - return VK_FORMAT_R5G6B5_UNORM_PACK16; - case Service::android::PixelFormat::Bgra8888: - return VK_FORMAT_B8G8R8A8_UNORM; +void BlitScreen::SetWindowAdaptPass() { + layers.clear(); + scaling_filter = Settings::values.scaling_filter.GetValue(); + + switch (scaling_filter) { + case Settings::ScalingFilter::NearestNeighbor: + window_adapt = MakeNearestNeighbor(device, swapchain_view_format); + break; + case Settings::ScalingFilter::Bicubic: + window_adapt = MakeBicubic(device, swapchain_view_format); + break; + case Settings::ScalingFilter::Gaussian: + window_adapt = MakeGaussian(device, swapchain_view_format); + break; + case Settings::ScalingFilter::ScaleForce: + window_adapt = MakeScaleForce(device, swapchain_view_format); + break; + case Settings::ScalingFilter::Fsr: + case Settings::ScalingFilter::Bilinear: default: - UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", - static_cast<u32>(framebuffer.pixel_format)); - return VK_FORMAT_A8B8G8R8_UNORM_PACK32; + window_adapt = MakeBilinear(device, swapchain_view_format); + break; } } -} // Anonymous namespace - -struct BlitScreen::BufferData { - struct { - std::array<f32, 4 * 4> modelview_matrix; - } uniform; - - std::array<ScreenRectVertex, 4> vertices; - - // Unaligned image data goes here -}; - -BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, - Core::Frontend::EmuWindow& render_window_, const Device& device_, - MemoryAllocator& memory_allocator_, Swapchain& swapchain_, - PresentManager& present_manager_, Scheduler& scheduler_, - const ScreenInfo& screen_info_) - : device_memory{device_memory_}, render_window{render_window_}, device{device_}, - memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_}, - scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { - resource_ticks.resize(image_count); - swapchain_view_format = swapchain.GetImageViewFormat(); - - CreateStaticResources(); - CreateDynamicResources(); -} - -BlitScreen::~BlitScreen() = default; - -static Common::Rectangle<f32> NormalizeCrop(const Tegra::FramebufferConfig& framebuffer, - const ScreenInfo& screen_info) { - f32 left, top, right, bottom; - - if (!framebuffer.crop_rect.IsEmpty()) { - // If crop rectangle is not empty, apply properties from rectangle. - left = static_cast<f32>(framebuffer.crop_rect.left); - top = static_cast<f32>(framebuffer.crop_rect.top); - right = static_cast<f32>(framebuffer.crop_rect.right); - bottom = static_cast<f32>(framebuffer.crop_rect.bottom); - } else { - // Otherwise, fall back to framebuffer dimensions. - left = 0; - top = 0; - right = static_cast<f32>(framebuffer.width); - bottom = static_cast<f32>(framebuffer.height); - } - - // Apply transformation flags. - auto framebuffer_transform_flags = framebuffer.transform_flags; +void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, + std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout, + size_t current_swapchain_image_count, + VkFormat current_swapchain_view_format) { + bool resource_update_required = false; + bool presentation_recreate_required = false; - if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) { - // Switch left and right. - std::swap(left, right); - } - if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) { - // Switch top and bottom. - std::swap(top, bottom); + // Recreate dynamic resources if the adapting filter changed + if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) { + resource_update_required = true; } - framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH; - framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV; - if (True(framebuffer_transform_flags)) { - UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}", - static_cast<u32>(framebuffer_transform_flags)); + // Recreate dynamic resources if the image count changed + const size_t old_swapchain_image_count = + std::exchange(image_count, current_swapchain_image_count); + if (old_swapchain_image_count != current_swapchain_image_count) { + resource_update_required = true; } - // Get the screen properties. - const f32 screen_width = static_cast<f32>(screen_info.width); - const f32 screen_height = static_cast<f32>(screen_info.height); - - // Normalize coordinate space. - left /= screen_width; - top /= screen_height; - right /= screen_width; - bottom /= screen_height; - - return Common::Rectangle<f32>(left, top, right, bottom); -} - -void BlitScreen::Recreate() { - present_manager.WaitPresent(); - scheduler.Finish(); - device.GetLogical().WaitIdle(); - CreateDynamicResources(); -} - -void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, - const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout, - VkExtent2D render_area, bool use_accelerated) { - RefreshResources(framebuffer); - - // Finish any pending renderpass - scheduler.RequestOutsideRenderPassOperationContext(); - - scheduler.Wait(resource_ticks[image_index]); - resource_ticks[image_index] = scheduler.CurrentTick(); - - VkImage source_image = use_accelerated ? screen_info.image : *raw_images[image_index]; - VkImageView source_image_view = - use_accelerated ? screen_info.image_view : *raw_image_views[image_index]; - - BufferData data; - SetUniformData(data, layout); - SetVertexData(data, framebuffer, layout); - - const std::span<u8> mapped_span = buffer.Mapped(); - std::memcpy(mapped_span.data(), &data, sizeof(data)); - - if (!use_accelerated) { - const u64 image_offset = GetRawImageOffset(framebuffer); - - const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset; - const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr); - - // TODO(Rodrigo): Read this from HLE - constexpr u32 block_height_log2 = 4; - const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); - const u64 linear_size{GetSizeInBytes(framebuffer)}; - const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel, - framebuffer.stride, framebuffer.height, - 1, block_height_log2, 0)}; - Tegra::Texture::UnswizzleTexture( - mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), - bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); - - const VkBufferImageCopy copy{ - .bufferOffset = image_offset, - .bufferRowLength = 0, - .bufferImageHeight = 0, - .imageSubresource = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .mipLevel = 0, - .baseArrayLayer = 0, - .layerCount = 1, - }, - .imageOffset = {.x = 0, .y = 0, .z = 0}, - .imageExtent = - { - .width = framebuffer.width, - .height = framebuffer.height, - .depth = 1, - }, - }; - scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { - const VkImage image = *raw_images[index]; - const VkImageMemoryBarrier base_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = 0, - .dstAccessMask = 0, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = image, - .subresourceRange{ - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - VkImageMemoryBarrier read_barrier = base_barrier; - read_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; - read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - VkImageMemoryBarrier write_barrier = base_barrier; - write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; - write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, - read_barrier); - cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_GENERAL, copy); - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - 0, write_barrier); - }); + // Recreate the presentation frame if the format or dimensions of the window changed + const VkFormat old_swapchain_view_format = + std::exchange(swapchain_view_format, current_swapchain_view_format); + if (old_swapchain_view_format != current_swapchain_view_format || + layout.width != frame->width || layout.height != frame->height) { + resource_update_required = true; + presentation_recreate_required = true; } - const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); - if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { - UpdateAADescriptorSet(source_image_view, false); - const u32 up_scale = Settings::values.resolution_info.up_scale; - const u32 down_shift = Settings::values.resolution_info.down_shift; - VkExtent2D size{ - .width = (up_scale * framebuffer.width) >> down_shift, - .height = (up_scale * framebuffer.height) >> down_shift, - }; - scheduler.Record([this, index = image_index, size, - anti_alias_pass](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier base_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = 0, - .dstAccessMask = 0, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = {}, - .subresourceRange = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - - { - VkImageMemoryBarrier fsr_write_barrier = base_barrier; - fsr_write_barrier.image = *aa_image; - fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, fsr_write_barrier); - } + // If we have a pending resource update, perform it + if (resource_update_required) { + // Wait for idle to ensure no resources are in use + WaitIdle(); - const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; - const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; - const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; - const VkClearValue clear_color{ - .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, - }; - const VkRenderPassBeginInfo renderpass_bi{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .pNext = nullptr, - .renderPass = *aa_renderpass, - .framebuffer = *aa_framebuffer, - .renderArea = - { - .offset = {0, 0}, - .extent = size, - }, - .clearValueCount = 1, - .pClearValues = &clear_color, - }; - const VkViewport viewport{ - .x = 0.0f, - .y = 0.0f, - .width = static_cast<float>(size.width), - .height = static_cast<float>(size.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - const VkRect2D scissor{ - .offset = {0, 0}, - .extent = size, - }; - cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); - switch (anti_alias_pass) { - case Settings::AntiAliasing::Fxaa: - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline); - break; - default: - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline); - break; - } - cmdbuf.SetViewport(0, viewport); - cmdbuf.SetScissor(0, scissor); + // Update window adapt pass + SetWindowAdaptPass(); - cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0, - aa_descriptor_sets[index], {}); - cmdbuf.Draw(4, 1, 0, 0); - cmdbuf.EndRenderPass(); - - { - VkImageMemoryBarrier blit_read_barrier = base_barrier; - blit_read_barrier.image = *aa_image; - blit_read_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; - blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, blit_read_barrier); - } - }); - source_image_view = *aa_image_view; - } - if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Smaa) { - if (!smaa) { - const u32 up_scale = Settings::values.resolution_info.up_scale; - const u32 down_shift = Settings::values.resolution_info.down_shift; - const VkExtent2D smaa_size{ - .width = (up_scale * framebuffer.width) >> down_shift, - .height = (up_scale * framebuffer.height) >> down_shift, - }; - CreateSMAA(smaa_size); + // Update frame format if needed + if (presentation_recreate_required) { + present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, + window_adapt->GetRenderPass()); } - source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view); } - if (fsr) { - const auto crop_rect = NormalizeCrop(framebuffer, screen_info); - const VkExtent2D fsr_input_size{ - .width = Settings::values.resolution_info.ScaleUp(screen_info.width), - .height = Settings::values.resolution_info.ScaleUp(screen_info.height), - }; - VkImageView fsr_image_view = - fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); - UpdateDescriptorSet(fsr_image_view, true); - } else { - const bool is_nn = - Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor; - UpdateDescriptorSet(source_image_view, is_nn); - } - - scheduler.Record([this, host_framebuffer, index = image_index, - size = render_area](vk::CommandBuffer cmdbuf) { - const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; - const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; - const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; - const VkClearValue clear_color{ - .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, - }; - const VkRenderPassBeginInfo renderpass_bi{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, - .pNext = nullptr, - .renderPass = *renderpass, - .framebuffer = host_framebuffer, - .renderArea = - { - .offset = {0, 0}, - .extent = size, - }, - .clearValueCount = 1, - .pClearValues = &clear_color, - }; - const VkViewport viewport{ - .x = 0.0f, - .y = 0.0f, - .width = static_cast<float>(size.width), - .height = static_cast<float>(size.height), - .minDepth = 0.0f, - .maxDepth = 1.0f, - }; - const VkRect2D scissor{ - .offset = {0, 0}, - .extent = size, - }; - cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); - auto graphics_pipeline = [this]() { - switch (Settings::values.scaling_filter.GetValue()) { - case Settings::ScalingFilter::NearestNeighbor: - case Settings::ScalingFilter::Bilinear: - return *bilinear_pipeline; - case Settings::ScalingFilter::Bicubic: - return *bicubic_pipeline; - case Settings::ScalingFilter::Gaussian: - return *gaussian_pipeline; - case Settings::ScalingFilter::ScaleForce: - return *scaleforce_pipeline; - default: - return *bilinear_pipeline; - } - }(); - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); - cmdbuf.SetViewport(0, viewport); - cmdbuf.SetScissor(0, scissor); - cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, - descriptor_sets[index], {}); - cmdbuf.Draw(4, 1, 0, 0); - cmdbuf.EndRenderPass(); - }); -} + // Add additional layers if needed + const VkExtent2D window_size{ + .width = layout.screen.GetWidth(), + .height = layout.screen.GetHeight(), + }; -void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, - bool use_accelerated) { - // Recreate dynamic resources if the the image count or input format changed - const VkFormat current_framebuffer_format = - std::exchange(framebuffer_view_format, GetFormat(framebuffer)); - if (const std::size_t swapchain_images = swapchain.GetImageCount(); - swapchain_images != image_count || current_framebuffer_format != framebuffer_view_format) { - image_count = swapchain_images; - Recreate(); + while (layers.size() < framebuffers.size()) { + layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, + window_size, window_adapt->GetDescriptorSetLayout()); } - // Recreate the presentation frame if the dimensions of the window changed - const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); - if (layout.width != frame->width || layout.height != frame->height) { - Recreate(); - present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, - *renderpass); - } + // Perform the draw + window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame); - const VkExtent2D render_area{frame->width, frame->height}; - Draw(framebuffer, *frame->framebuffer, layout, render_area, use_accelerated); + // Advance to next image if (++image_index >= image_count) { image_index = 0; } } -vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { - return CreateFramebuffer(image_view, extent, renderpass); +vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, + VkImageView image_view, + VkFormat current_view_format) { + const bool format_updated = + std::exchange(swapchain_view_format, current_view_format) != current_view_format; + if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() || + format_updated) { + WaitIdle(); + SetWindowAdaptPass(); + } + const VkExtent2D extent{ + .width = layout.width, + .height = layout.height, + }; + return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass()); } vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, - vk::RenderPass& rd) { + VkRenderPass render_pass) { return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, .pNext = nullptr, .flags = 0, - .renderPass = *rd, + .renderPass = render_pass, .attachmentCount = 1, .pAttachments = &image_view, .width = extent.width, @@ -525,969 +146,4 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE }); } -void BlitScreen::CreateStaticResources() { - CreateShaders(); - CreateSampler(); -} - -void BlitScreen::CreateDynamicResources() { - CreateDescriptorPool(); - CreateDescriptorSetLayout(); - CreateDescriptorSets(); - CreatePipelineLayout(); - CreateRenderPass(); - CreateGraphicsPipeline(); - fsr.reset(); - smaa.reset(); - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { - CreateFSR(); - } -} - -void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { - if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { - if (!fsr) { - CreateFSR(); - } - } else { - fsr.reset(); - } - - if (framebuffer.width == raw_width && framebuffer.height == raw_height && - framebuffer.pixel_format == pixel_format && !raw_images.empty()) { - return; - } - - raw_width = framebuffer.width; - raw_height = framebuffer.height; - pixel_format = framebuffer.pixel_format; - - smaa.reset(); - ReleaseRawImages(); - - CreateStagingBuffer(framebuffer); - CreateRawImages(framebuffer); -} - -void BlitScreen::CreateShaders() { - vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); - fxaa_vertex_shader = BuildShader(device, FXAA_VERT_SPV); - fxaa_fragment_shader = BuildShader(device, FXAA_FRAG_SPV); - bilinear_fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV); - bicubic_fragment_shader = BuildShader(device, PRESENT_BICUBIC_FRAG_SPV); - gaussian_fragment_shader = BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV); - if (device.IsFloat16Supported()) { - scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV); - } else { - scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV); - } -} - -void BlitScreen::CreateDescriptorPool() { - const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ - { - .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = static_cast<u32>(image_count), - }, - { - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = static_cast<u32>(image_count), - }, - }}; - - const std::array<VkDescriptorPoolSize, 1> pool_sizes_aa{{ - { - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = static_cast<u32>(image_count * 2), - }, - }}; - - const VkDescriptorPoolCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .maxSets = static_cast<u32>(image_count), - .poolSizeCount = static_cast<u32>(pool_sizes.size()), - .pPoolSizes = pool_sizes.data(), - }; - descriptor_pool = device.GetLogical().CreateDescriptorPool(ci); - - const VkDescriptorPoolCreateInfo ci_aa{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .maxSets = static_cast<u32>(image_count), - .poolSizeCount = static_cast<u32>(pool_sizes_aa.size()), - .pPoolSizes = pool_sizes_aa.data(), - }; - aa_descriptor_pool = device.GetLogical().CreateDescriptorPool(ci_aa); -} - -void BlitScreen::CreateRenderPass() { - renderpass = CreateRenderPassImpl(swapchain_view_format); -} - -vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) { - const VkAttachmentDescription color_attachment{ - .flags = 0, - .format = format, - .samples = VK_SAMPLE_COUNT_1_BIT, - .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, - .storeOp = VK_ATTACHMENT_STORE_OP_STORE, - .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, - .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - .finalLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - - const VkAttachmentReference color_attachment_ref{ - .attachment = 0, - .layout = VK_IMAGE_LAYOUT_GENERAL, - }; - - const VkSubpassDescription subpass_description{ - .flags = 0, - .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, - .inputAttachmentCount = 0, - .pInputAttachments = nullptr, - .colorAttachmentCount = 1, - .pColorAttachments = &color_attachment_ref, - .pResolveAttachments = nullptr, - .pDepthStencilAttachment = nullptr, - .preserveAttachmentCount = 0, - .pPreserveAttachments = nullptr, - }; - - const VkSubpassDependency dependency{ - .srcSubpass = VK_SUBPASS_EXTERNAL, - .dstSubpass = 0, - .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - .srcAccessMask = 0, - .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - .dependencyFlags = 0, - }; - - const VkRenderPassCreateInfo renderpass_ci{ - .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .attachmentCount = 1, - .pAttachments = &color_attachment, - .subpassCount = 1, - .pSubpasses = &subpass_description, - .dependencyCount = 1, - .pDependencies = &dependency, - }; - - return device.GetLogical().CreateRenderPass(renderpass_ci); -} - -void BlitScreen::CreateDescriptorSetLayout() { - const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{ - { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, - .pImmutableSamplers = nullptr, - }, - { - .binding = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = nullptr, - }, - }}; - - const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings_aa{{ - { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, - .pImmutableSamplers = nullptr, - }, - { - .binding = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, - .pImmutableSamplers = nullptr, - }, - }}; - - const VkDescriptorSetLayoutCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .bindingCount = static_cast<u32>(layout_bindings.size()), - .pBindings = layout_bindings.data(), - }; - - const VkDescriptorSetLayoutCreateInfo ci_aa{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .bindingCount = static_cast<u32>(layout_bindings_aa.size()), - .pBindings = layout_bindings_aa.data(), - }; - - descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); - aa_descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci_aa); -} - -void BlitScreen::CreateDescriptorSets() { - const std::vector layouts(image_count, *descriptor_set_layout); - const std::vector layouts_aa(image_count, *aa_descriptor_set_layout); - - const VkDescriptorSetAllocateInfo ai{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .pNext = nullptr, - .descriptorPool = *descriptor_pool, - .descriptorSetCount = static_cast<u32>(image_count), - .pSetLayouts = layouts.data(), - }; - - const VkDescriptorSetAllocateInfo ai_aa{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .pNext = nullptr, - .descriptorPool = *aa_descriptor_pool, - .descriptorSetCount = static_cast<u32>(image_count), - .pSetLayouts = layouts_aa.data(), - }; - - descriptor_sets = descriptor_pool.Allocate(ai); - aa_descriptor_sets = aa_descriptor_pool.Allocate(ai_aa); -} - -void BlitScreen::CreatePipelineLayout() { - const VkPipelineLayoutCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .setLayoutCount = 1, - .pSetLayouts = descriptor_set_layout.address(), - .pushConstantRangeCount = 0, - .pPushConstantRanges = nullptr, - }; - const VkPipelineLayoutCreateInfo ci_aa{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .setLayoutCount = 1, - .pSetLayouts = aa_descriptor_set_layout.address(), - .pushConstantRangeCount = 0, - .pPushConstantRanges = nullptr, - }; - pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); - aa_pipeline_layout = device.GetLogical().CreatePipelineLayout(ci_aa); -} - -void BlitScreen::CreateGraphicsPipeline() { - const std::array<VkPipelineShaderStageCreateInfo, 2> bilinear_shader_stages{{ - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = *vertex_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = *bilinear_fragment_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - }}; - - const std::array<VkPipelineShaderStageCreateInfo, 2> bicubic_shader_stages{{ - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = *vertex_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = *bicubic_fragment_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - }}; - - const std::array<VkPipelineShaderStageCreateInfo, 2> gaussian_shader_stages{{ - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = *vertex_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = *gaussian_fragment_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - }}; - - const std::array<VkPipelineShaderStageCreateInfo, 2> scaleforce_shader_stages{{ - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = *vertex_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = *scaleforce_fragment_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - }}; - - const auto vertex_binding_description = ScreenRectVertex::GetDescription(); - const auto vertex_attrs_description = ScreenRectVertex::GetAttributes(); - - const VkPipelineVertexInputStateCreateInfo vertex_input_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .vertexBindingDescriptionCount = 1, - .pVertexBindingDescriptions = &vertex_binding_description, - .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()}, - .pVertexAttributeDescriptions = vertex_attrs_description.data(), - }; - - const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, - .primitiveRestartEnable = VK_FALSE, - }; - - const VkPipelineViewportStateCreateInfo viewport_state_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .viewportCount = 1, - .pViewports = nullptr, - .scissorCount = 1, - .pScissors = nullptr, - }; - - const VkPipelineRasterizationStateCreateInfo rasterization_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .depthClampEnable = VK_FALSE, - .rasterizerDiscardEnable = VK_FALSE, - .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = VK_CULL_MODE_NONE, - .frontFace = VK_FRONT_FACE_CLOCKWISE, - .depthBiasEnable = VK_FALSE, - .depthBiasConstantFactor = 0.0f, - .depthBiasClamp = 0.0f, - .depthBiasSlopeFactor = 0.0f, - .lineWidth = 1.0f, - }; - - const VkPipelineMultisampleStateCreateInfo multisampling_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, - .sampleShadingEnable = VK_FALSE, - .minSampleShading = 0.0f, - .pSampleMask = nullptr, - .alphaToCoverageEnable = VK_FALSE, - .alphaToOneEnable = VK_FALSE, - }; - - const VkPipelineColorBlendAttachmentState color_blend_attachment{ - .blendEnable = VK_FALSE, - .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, - .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, - .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - }; - - const VkPipelineColorBlendStateCreateInfo color_blend_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .logicOpEnable = VK_FALSE, - .logicOp = VK_LOGIC_OP_COPY, - .attachmentCount = 1, - .pAttachments = &color_blend_attachment, - .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, - }; - - static constexpr std::array dynamic_states{ - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, - }; - const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .dynamicStateCount = static_cast<u32>(dynamic_states.size()), - .pDynamicStates = dynamic_states.data(), - }; - - const VkGraphicsPipelineCreateInfo bilinear_pipeline_ci{ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stageCount = static_cast<u32>(bilinear_shader_stages.size()), - .pStages = bilinear_shader_stages.data(), - .pVertexInputState = &vertex_input_ci, - .pInputAssemblyState = &input_assembly_ci, - .pTessellationState = nullptr, - .pViewportState = &viewport_state_ci, - .pRasterizationState = &rasterization_ci, - .pMultisampleState = &multisampling_ci, - .pDepthStencilState = nullptr, - .pColorBlendState = &color_blend_ci, - .pDynamicState = &dynamic_state_ci, - .layout = *pipeline_layout, - .renderPass = *renderpass, - .subpass = 0, - .basePipelineHandle = 0, - .basePipelineIndex = 0, - }; - - const VkGraphicsPipelineCreateInfo bicubic_pipeline_ci{ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stageCount = static_cast<u32>(bicubic_shader_stages.size()), - .pStages = bicubic_shader_stages.data(), - .pVertexInputState = &vertex_input_ci, - .pInputAssemblyState = &input_assembly_ci, - .pTessellationState = nullptr, - .pViewportState = &viewport_state_ci, - .pRasterizationState = &rasterization_ci, - .pMultisampleState = &multisampling_ci, - .pDepthStencilState = nullptr, - .pColorBlendState = &color_blend_ci, - .pDynamicState = &dynamic_state_ci, - .layout = *pipeline_layout, - .renderPass = *renderpass, - .subpass = 0, - .basePipelineHandle = 0, - .basePipelineIndex = 0, - }; - - const VkGraphicsPipelineCreateInfo gaussian_pipeline_ci{ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stageCount = static_cast<u32>(gaussian_shader_stages.size()), - .pStages = gaussian_shader_stages.data(), - .pVertexInputState = &vertex_input_ci, - .pInputAssemblyState = &input_assembly_ci, - .pTessellationState = nullptr, - .pViewportState = &viewport_state_ci, - .pRasterizationState = &rasterization_ci, - .pMultisampleState = &multisampling_ci, - .pDepthStencilState = nullptr, - .pColorBlendState = &color_blend_ci, - .pDynamicState = &dynamic_state_ci, - .layout = *pipeline_layout, - .renderPass = *renderpass, - .subpass = 0, - .basePipelineHandle = 0, - .basePipelineIndex = 0, - }; - - const VkGraphicsPipelineCreateInfo scaleforce_pipeline_ci{ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stageCount = static_cast<u32>(scaleforce_shader_stages.size()), - .pStages = scaleforce_shader_stages.data(), - .pVertexInputState = &vertex_input_ci, - .pInputAssemblyState = &input_assembly_ci, - .pTessellationState = nullptr, - .pViewportState = &viewport_state_ci, - .pRasterizationState = &rasterization_ci, - .pMultisampleState = &multisampling_ci, - .pDepthStencilState = nullptr, - .pColorBlendState = &color_blend_ci, - .pDynamicState = &dynamic_state_ci, - .layout = *pipeline_layout, - .renderPass = *renderpass, - .subpass = 0, - .basePipelineHandle = 0, - .basePipelineIndex = 0, - }; - - bilinear_pipeline = device.GetLogical().CreateGraphicsPipeline(bilinear_pipeline_ci); - bicubic_pipeline = device.GetLogical().CreateGraphicsPipeline(bicubic_pipeline_ci); - gaussian_pipeline = device.GetLogical().CreateGraphicsPipeline(gaussian_pipeline_ci); - scaleforce_pipeline = device.GetLogical().CreateGraphicsPipeline(scaleforce_pipeline_ci); -} - -void BlitScreen::CreateSampler() { - const VkSamplerCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .mipLodBias = 0.0f, - .anisotropyEnable = VK_FALSE, - .maxAnisotropy = 0.0f, - .compareEnable = VK_FALSE, - .compareOp = VK_COMPARE_OP_NEVER, - .minLod = 0.0f, - .maxLod = 0.0f, - .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - .unnormalizedCoordinates = VK_FALSE, - }; - - const VkSamplerCreateInfo ci_nn{ - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .magFilter = VK_FILTER_NEAREST, - .minFilter = VK_FILTER_NEAREST, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, - .mipLodBias = 0.0f, - .anisotropyEnable = VK_FALSE, - .maxAnisotropy = 0.0f, - .compareEnable = VK_FALSE, - .compareOp = VK_COMPARE_OP_NEVER, - .minLod = 0.0f, - .maxLod = 0.0f, - .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - .unnormalizedCoordinates = VK_FALSE, - }; - - sampler = device.GetLogical().CreateSampler(ci); - nn_sampler = device.GetLogical().CreateSampler(ci_nn); -} - -void BlitScreen::ReleaseRawImages() { - for (const u64 tick : resource_ticks) { - scheduler.Wait(tick); - } - raw_images.clear(); - aa_image_view.reset(); - aa_image.reset(); - buffer.reset(); -} - -void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { - const VkBufferCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .size = CalculateBufferSize(framebuffer), - .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | - VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - }; - - buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); -} - -void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { - raw_images.resize(image_count); - raw_image_views.resize(image_count); - - const auto create_image = [&](bool used_on_framebuffer = false, u32 up_scale = 1, - u32 down_shift = 0) { - u32 extra_usages = used_on_framebuffer ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT - : VK_IMAGE_USAGE_TRANSFER_DST_BIT; - return memory_allocator.CreateImage(VkImageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format, - .extent = - { - .width = (up_scale * framebuffer.width) >> down_shift, - .height = (up_scale * framebuffer.height) >> down_shift, - .depth = 1, - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = used_on_framebuffer ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR, - .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | extra_usages, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }); - }; - const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) { - return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .image = *image, - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format, - .components = - { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }); - }; - - for (size_t i = 0; i < image_count; ++i) { - raw_images[i] = create_image(); - raw_image_views[i] = create_image_view(raw_images[i]); - } - - // AA Resources - const u32 up_scale = Settings::values.resolution_info.up_scale; - const u32 down_shift = Settings::values.resolution_info.down_shift; - aa_image = create_image(true, up_scale, down_shift); - aa_image_view = create_image_view(aa_image, true); - VkExtent2D size{ - .width = (up_scale * framebuffer.width) >> down_shift, - .height = (up_scale * framebuffer.height) >> down_shift, - }; - if (aa_renderpass) { - aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); - return; - } - aa_renderpass = CreateRenderPassImpl(VK_FORMAT_R16G16B16A16_SFLOAT); - aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); - - const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{ - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_VERTEX_BIT, - .module = *fxaa_vertex_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - { - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_FRAGMENT_BIT, - .module = *fxaa_fragment_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }, - }}; - - const auto vertex_binding_description = ScreenRectVertex::GetDescription(); - const auto vertex_attrs_description = ScreenRectVertex::GetAttributes(); - - const VkPipelineVertexInputStateCreateInfo vertex_input_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .vertexBindingDescriptionCount = 1, - .pVertexBindingDescriptions = &vertex_binding_description, - .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()}, - .pVertexAttributeDescriptions = vertex_attrs_description.data(), - }; - - const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, - .primitiveRestartEnable = VK_FALSE, - }; - - const VkPipelineViewportStateCreateInfo viewport_state_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .viewportCount = 1, - .pViewports = nullptr, - .scissorCount = 1, - .pScissors = nullptr, - }; - - const VkPipelineRasterizationStateCreateInfo rasterization_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .depthClampEnable = VK_FALSE, - .rasterizerDiscardEnable = VK_FALSE, - .polygonMode = VK_POLYGON_MODE_FILL, - .cullMode = VK_CULL_MODE_NONE, - .frontFace = VK_FRONT_FACE_CLOCKWISE, - .depthBiasEnable = VK_FALSE, - .depthBiasConstantFactor = 0.0f, - .depthBiasClamp = 0.0f, - .depthBiasSlopeFactor = 0.0f, - .lineWidth = 1.0f, - }; - - const VkPipelineMultisampleStateCreateInfo multisampling_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, - .sampleShadingEnable = VK_FALSE, - .minSampleShading = 0.0f, - .pSampleMask = nullptr, - .alphaToCoverageEnable = VK_FALSE, - .alphaToOneEnable = VK_FALSE, - }; - - const VkPipelineColorBlendAttachmentState color_blend_attachment{ - .blendEnable = VK_FALSE, - .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, - .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, - .colorBlendOp = VK_BLEND_OP_ADD, - .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, - .alphaBlendOp = VK_BLEND_OP_ADD, - .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | - VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, - }; - - const VkPipelineColorBlendStateCreateInfo color_blend_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .logicOpEnable = VK_FALSE, - .logicOp = VK_LOGIC_OP_COPY, - .attachmentCount = 1, - .pAttachments = &color_blend_attachment, - .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, - }; - - static constexpr std::array dynamic_states{ - VK_DYNAMIC_STATE_VIEWPORT, - VK_DYNAMIC_STATE_SCISSOR, - }; - const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .dynamicStateCount = static_cast<u32>(dynamic_states.size()), - .pDynamicStates = dynamic_states.data(), - }; - - const VkGraphicsPipelineCreateInfo fxaa_pipeline_ci{ - .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stageCount = static_cast<u32>(fxaa_shader_stages.size()), - .pStages = fxaa_shader_stages.data(), - .pVertexInputState = &vertex_input_ci, - .pInputAssemblyState = &input_assembly_ci, - .pTessellationState = nullptr, - .pViewportState = &viewport_state_ci, - .pRasterizationState = &rasterization_ci, - .pMultisampleState = &multisampling_ci, - .pDepthStencilState = nullptr, - .pColorBlendState = &color_blend_ci, - .pDynamicState = &dynamic_state_ci, - .layout = *aa_pipeline_layout, - .renderPass = *aa_renderpass, - .subpass = 0, - .basePipelineHandle = 0, - .basePipelineIndex = 0, - }; - - // AA - aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci); -} - -void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const { - const VkDescriptorImageInfo image_info{ - .sampler = nn ? *nn_sampler : *sampler, - .imageView = image_view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - - const VkWriteDescriptorSet sampler_write{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = aa_descriptor_sets[image_index], - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &image_info, - .pBufferInfo = nullptr, - .pTexelBufferView = nullptr, - }; - - const VkWriteDescriptorSet sampler_write_2{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = aa_descriptor_sets[image_index], - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &image_info, - .pBufferInfo = nullptr, - .pTexelBufferView = nullptr, - }; - - device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {}); -} - -void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const { - const VkDescriptorBufferInfo buffer_info{ - .buffer = *buffer, - .offset = offsetof(BufferData, uniform), - .range = sizeof(BufferData::uniform), - }; - - const VkWriteDescriptorSet ubo_write{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = descriptor_sets[image_index], - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .pImageInfo = nullptr, - .pBufferInfo = &buffer_info, - .pTexelBufferView = nullptr, - }; - - const VkDescriptorImageInfo image_info{ - .sampler = nn ? *nn_sampler : *sampler, - .imageView = image_view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - - const VkWriteDescriptorSet sampler_write{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = descriptor_sets[image_index], - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &image_info, - .pBufferInfo = nullptr, - .pTexelBufferView = nullptr, - }; - - device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {}); -} - -void BlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const { - data.uniform.modelview_matrix = - MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); -} - -void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, - const Layout::FramebufferLayout layout) const { - f32 left, top, right, bottom; - - if (fsr) { - // FSR has already applied the crop, so we just want to render the image - // it has produced. - left = 0; - top = 0; - right = 1; - bottom = 1; - } else { - // Get the normalized crop rectangle. - const auto crop = NormalizeCrop(framebuffer, screen_info); - - // Apply the crop. - left = crop.left; - top = crop.top; - right = crop.right; - bottom = crop.bottom; - } - - // Map the coordinates to the screen. - const auto& screen = layout.screen; - const auto x = static_cast<f32>(screen.left); - const auto y = static_cast<f32>(screen.top); - const auto w = static_cast<f32>(screen.GetWidth()); - const auto h = static_cast<f32>(screen.GetHeight()); - - data.vertices[0] = ScreenRectVertex(x, y, left, top); - data.vertices[1] = ScreenRectVertex(x + w, y, right, top); - data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom); - data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom); -} - -void BlitScreen::CreateSMAA(VkExtent2D smaa_size) { - smaa = std::make_unique<SMAA>(device, memory_allocator, image_count, smaa_size); -} - -void BlitScreen::CreateFSR() { - const auto& layout = render_window.GetFramebufferLayout(); - const VkExtent2D fsr_size{ - .width = layout.screen.GetWidth(), - .height = layout.screen.GetHeight(), - }; - fsr = std::make_unique<FSR>(device, memory_allocator, image_count, fsr_size); -} - -u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const { - return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count; -} - -u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const { - constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData)); - return first_image_offset + GetSizeInBytes(framebuffer) * image_index; -} - } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h index 3eff76009..cbdf2d5d0 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.h +++ b/src/video_core/renderer_vulkan/vk_blit_screen.h @@ -3,10 +3,12 @@ #pragma once +#include <list> #include <memory> #include "core/frontend/framebuffer_layout.h" #include "video_core/host1x/gpu_device_memory_manager.h" +#include "video_core/renderer_vulkan/present/layer.h" #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" @@ -14,155 +16,67 @@ namespace Core { class System; } -namespace Core::Frontend { -class EmuWindow; -} - namespace Tegra { struct FramebufferConfig; } -namespace VideoCore { -class RasterizerInterface; -} - -namespace Service::android { -enum class PixelFormat : u32; -} +namespace Settings { +enum class ScalingFilter : u32; +} // namespace Settings namespace Vulkan { -struct ScreenInfo; - class Device; -class FSR; class RasterizerVulkan; class Scheduler; -class SMAA; -class Swapchain; class PresentManager; +class WindowAdaptPass; struct Frame; -struct ScreenInfo { +struct FramebufferTextureInfo { VkImage image{}; VkImageView image_view{}; u32 width{}; u32 height{}; + u32 scaled_width{}; + u32 scaled_height{}; }; class BlitScreen { public: - explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, - Core::Frontend::EmuWindow& render_window, const Device& device, - MemoryAllocator& memory_manager, Swapchain& swapchain, - PresentManager& present_manager, Scheduler& scheduler, - const ScreenInfo& screen_info); + explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device, + MemoryAllocator& memory_allocator, PresentManager& present_manager, + Scheduler& scheduler); ~BlitScreen(); - void Recreate(); - - void Draw(const Tegra::FramebufferConfig& framebuffer, const VkFramebuffer& host_framebuffer, - const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated); - - void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, - bool use_accelerated); + void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, + std::span<const Tegra::FramebufferConfig> framebuffers, + const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count, + VkFormat current_swapchain_view_format); - [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, - VkExtent2D extent); - - [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, - VkExtent2D extent, vk::RenderPass& rd); + [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, + VkImageView image_view, + VkFormat current_view_format); private: - struct BufferData; - - void CreateStaticResources(); - void CreateShaders(); - void CreateDescriptorPool(); - void CreateRenderPass(); - vk::RenderPass CreateRenderPassImpl(VkFormat format); - void CreateDescriptorSetLayout(); - void CreateDescriptorSets(); - void CreatePipelineLayout(); - void CreateGraphicsPipeline(); - void CreateSampler(); - - void CreateDynamicResources(); - - void RefreshResources(const Tegra::FramebufferConfig& framebuffer); - void ReleaseRawImages(); - void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); - void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); - - void UpdateDescriptorSet(VkImageView image_view, bool nn) const; - void UpdateAADescriptorSet(VkImageView image_view, bool nn) const; - void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const; - void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, - const Layout::FramebufferLayout layout) const; - - void CreateSMAA(VkExtent2D smaa_size); - void CreateFSR(); - - u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; - u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const; + void WaitIdle(); + void SetWindowAdaptPass(); + vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, + VkRenderPass render_pass); Tegra::MaxwellDeviceMemoryManager& device_memory; - Core::Frontend::EmuWindow& render_window; const Device& device; MemoryAllocator& memory_allocator; - Swapchain& swapchain; PresentManager& present_manager; Scheduler& scheduler; - std::size_t image_count; + std::size_t image_count{}; std::size_t image_index{}; - const ScreenInfo& screen_info; - - vk::ShaderModule vertex_shader; - vk::ShaderModule fxaa_vertex_shader; - vk::ShaderModule fxaa_fragment_shader; - vk::ShaderModule bilinear_fragment_shader; - vk::ShaderModule bicubic_fragment_shader; - vk::ShaderModule gaussian_fragment_shader; - vk::ShaderModule scaleforce_fragment_shader; - vk::DescriptorPool descriptor_pool; - vk::DescriptorSetLayout descriptor_set_layout; - vk::PipelineLayout pipeline_layout; - vk::Pipeline nearest_neighbor_pipeline; - vk::Pipeline bilinear_pipeline; - vk::Pipeline bicubic_pipeline; - vk::Pipeline gaussian_pipeline; - vk::Pipeline scaleforce_pipeline; - vk::RenderPass renderpass; - vk::DescriptorSets descriptor_sets; - vk::Sampler nn_sampler; - vk::Sampler sampler; - - vk::Buffer buffer; - - std::vector<u64> resource_ticks; - - std::vector<vk::Image> raw_images; - std::vector<vk::ImageView> raw_image_views; - - vk::DescriptorPool aa_descriptor_pool; - vk::DescriptorSetLayout aa_descriptor_set_layout; - vk::PipelineLayout aa_pipeline_layout; - vk::Pipeline aa_pipeline; - vk::RenderPass aa_renderpass; - vk::Framebuffer aa_framebuffer; - vk::DescriptorSets aa_descriptor_sets; - vk::Image aa_image; - vk::ImageView aa_image_view; - - u32 raw_width = 0; - u32 raw_height = 0; - Service::android::PixelFormat pixel_format{}; - VkFormat framebuffer_view_format; - VkFormat swapchain_view_format; - - std::unique_ptr<FSR> fsr; - std::unique_ptr<SMAA> smaa; + VkFormat swapchain_view_format{}; + + Settings::ScalingFilter scaling_filter{}; + std::unique_ptr<WindowAdaptPass> window_adapt{}; + std::list<Layer> layers{}; }; } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_fsr.cpp b/src/video_core/renderer_vulkan/vk_fsr.cpp deleted file mode 100644 index f7a05fbc0..000000000 --- a/src/video_core/renderer_vulkan/vk_fsr.cpp +++ /dev/null @@ -1,420 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#include "common/common_types.h" -#include "common/div_ceil.h" -#include "common/settings.h" - -#include "video_core/fsr.h" -#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" -#include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" -#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h" -#include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_comp_spv.h" -#include "video_core/renderer_vulkan/vk_fsr.h" -#include "video_core/renderer_vulkan/vk_scheduler.h" -#include "video_core/renderer_vulkan/vk_shader_util.h" -#include "video_core/vulkan_common/vulkan_device.h" - -namespace Vulkan { -using namespace FSR; - -FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_, - VkExtent2D output_size_) - : device{device_}, memory_allocator{memory_allocator_}, image_count{image_count_}, - output_size{output_size_} { - - CreateImages(); - CreateSampler(); - CreateShaders(); - CreateDescriptorPool(); - CreateDescriptorSetLayout(); - CreateDescriptorSets(); - CreatePipelineLayout(); - CreatePipeline(); -} - -VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, - VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect) { - - UpdateDescriptorSet(image_index, image_view); - - scheduler.Record([this, image_index, input_image_extent, crop_rect](vk::CommandBuffer cmdbuf) { - const VkImageMemoryBarrier base_barrier{ - .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, - .pNext = nullptr, - .srcAccessMask = 0, - .dstAccessMask = 0, - .oldLayout = VK_IMAGE_LAYOUT_GENERAL, - .newLayout = VK_IMAGE_LAYOUT_GENERAL, - .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = {}, - .subresourceRange = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }; - - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline); - - const f32 input_image_width = static_cast<f32>(input_image_extent.width); - const f32 input_image_height = static_cast<f32>(input_image_extent.height); - const f32 output_image_width = static_cast<f32>(output_size.width); - const f32 output_image_height = static_cast<f32>(output_size.height); - const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; - const f32 viewport_x = crop_rect.left * input_image_width; - const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; - const f32 viewport_y = crop_rect.top * input_image_height; - - std::array<u32, 4 * 4> push_constants; - FsrEasuConOffset(push_constants.data() + 0, push_constants.data() + 4, - push_constants.data() + 8, push_constants.data() + 12, - - viewport_width, viewport_height, input_image_width, input_image_height, - output_image_width, output_image_height, viewport_x, viewport_y); - cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); - - { - VkImageMemoryBarrier fsr_write_barrier = base_barrier; - fsr_write_barrier.image = *images[image_index]; - fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, fsr_write_barrier); - } - - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0, - descriptor_sets[image_index * 2], {}); - cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u), - Common::DivCeil(output_size.height, 16u), 1); - - cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline); - - const float sharpening = - static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; - - FsrRcasCon(push_constants.data(), sharpening); - cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); - - { - std::array<VkImageMemoryBarrier, 2> barriers; - auto& fsr_read_barrier = barriers[0]; - auto& blit_write_barrier = barriers[1]; - - fsr_read_barrier = base_barrier; - fsr_read_barrier.image = *images[image_index]; - fsr_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - fsr_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - blit_write_barrier = base_barrier; - blit_write_barrier.image = *images[image_count + image_index]; - blit_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; - blit_write_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; - - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, {}, {}, barriers); - } - - cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0, - descriptor_sets[image_index * 2 + 1], {}); - cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u), - Common::DivCeil(output_size.height, 16u), 1); - - { - std::array<VkImageMemoryBarrier, 1> barriers; - auto& blit_read_barrier = barriers[0]; - - blit_read_barrier = base_barrier; - blit_read_barrier.image = *images[image_count + image_index]; - blit_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; - blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; - - cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, - VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, {}, {}, barriers); - } - }); - - return *image_views[image_count + image_index]; -} - -void FSR::CreateDescriptorPool() { - const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ - { - .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = static_cast<u32>(image_count * 2), - }, - { - .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .descriptorCount = static_cast<u32>(image_count * 2), - }, - }}; - - const VkDescriptorPoolCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .maxSets = static_cast<u32>(image_count * 2), - .poolSizeCount = static_cast<u32>(pool_sizes.size()), - .pPoolSizes = pool_sizes.data(), - }; - descriptor_pool = device.GetLogical().CreateDescriptorPool(ci); -} - -void FSR::CreateDescriptorSetLayout() { - const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{ - { - .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, - .pImmutableSamplers = sampler.address(), - }, - { - .binding = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, - .pImmutableSamplers = sampler.address(), - }, - }}; - - const VkDescriptorSetLayoutCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .bindingCount = static_cast<u32>(layout_bindings.size()), - .pBindings = layout_bindings.data(), - }; - - descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); -} - -void FSR::CreateDescriptorSets() { - const u32 sets = static_cast<u32>(image_count * 2); - const std::vector layouts(sets, *descriptor_set_layout); - - const VkDescriptorSetAllocateInfo ai{ - .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, - .pNext = nullptr, - .descriptorPool = *descriptor_pool, - .descriptorSetCount = sets, - .pSetLayouts = layouts.data(), - }; - - descriptor_sets = descriptor_pool.Allocate(ai); -} - -void FSR::CreateImages() { - images.resize(image_count * 2); - image_views.resize(image_count * 2); - - for (size_t i = 0; i < image_count * 2; ++i) { - images[i] = memory_allocator.CreateImage(VkImageCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .imageType = VK_IMAGE_TYPE_2D, - .format = VK_FORMAT_R16G16B16A16_SFLOAT, - .extent = - { - .width = output_size.width, - .height = output_size.height, - .depth = 1, - }, - .mipLevels = 1, - .arrayLayers = 1, - .samples = VK_SAMPLE_COUNT_1_BIT, - .tiling = VK_IMAGE_TILING_OPTIMAL, - .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT | - VK_IMAGE_USAGE_SAMPLED_BIT, - .sharingMode = VK_SHARING_MODE_EXCLUSIVE, - .queueFamilyIndexCount = 0, - .pQueueFamilyIndices = nullptr, - .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, - }); - image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ - .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .image = *images[i], - .viewType = VK_IMAGE_VIEW_TYPE_2D, - .format = VK_FORMAT_R16G16B16A16_SFLOAT, - .components = - { - .r = VK_COMPONENT_SWIZZLE_IDENTITY, - .g = VK_COMPONENT_SWIZZLE_IDENTITY, - .b = VK_COMPONENT_SWIZZLE_IDENTITY, - .a = VK_COMPONENT_SWIZZLE_IDENTITY, - }, - .subresourceRange = - { - .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, - .baseMipLevel = 0, - .levelCount = 1, - .baseArrayLayer = 0, - .layerCount = 1, - }, - }); - } -} - -void FSR::CreatePipelineLayout() { - VkPushConstantRange push_const{ - .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, - .offset = 0, - .size = sizeof(std::array<u32, 4 * 4>), - }; - VkPipelineLayoutCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .setLayoutCount = 1, - .pSetLayouts = descriptor_set_layout.address(), - .pushConstantRangeCount = 1, - .pPushConstantRanges = &push_const, - }; - - pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); -} - -void FSR::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const { - const auto fsr_image_view = *image_views[image_index]; - const auto blit_image_view = *image_views[image_count + image_index]; - - const VkDescriptorImageInfo image_info{ - .sampler = VK_NULL_HANDLE, - .imageView = image_view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - const VkDescriptorImageInfo fsr_image_info{ - .sampler = VK_NULL_HANDLE, - .imageView = fsr_image_view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - const VkDescriptorImageInfo blit_image_info{ - .sampler = VK_NULL_HANDLE, - .imageView = blit_image_view, - .imageLayout = VK_IMAGE_LAYOUT_GENERAL, - }; - - VkWriteDescriptorSet sampler_write{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = descriptor_sets[image_index * 2], - .dstBinding = 0, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, - .pImageInfo = &image_info, - .pBufferInfo = nullptr, - .pTexelBufferView = nullptr, - }; - - VkWriteDescriptorSet output_write{ - .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, - .pNext = nullptr, - .dstSet = descriptor_sets[image_index * 2], - .dstBinding = 1, - .dstArrayElement = 0, - .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, - .pImageInfo = &fsr_image_info, - .pBufferInfo = nullptr, - .pTexelBufferView = nullptr, - }; - - device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {}); - - sampler_write.dstSet = descriptor_sets[image_index * 2 + 1]; - sampler_write.pImageInfo = &fsr_image_info; - output_write.dstSet = descriptor_sets[image_index * 2 + 1]; - output_write.pImageInfo = &blit_image_info; - - device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {}); -} - -void FSR::CreateSampler() { - const VkSamplerCreateInfo ci{ - .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .magFilter = VK_FILTER_LINEAR, - .minFilter = VK_FILTER_LINEAR, - .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, - .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, - .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, - .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, - .mipLodBias = 0.0f, - .anisotropyEnable = VK_FALSE, - .maxAnisotropy = 0.0f, - .compareEnable = VK_FALSE, - .compareOp = VK_COMPARE_OP_NEVER, - .minLod = 0.0f, - .maxLod = 0.0f, - .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, - .unnormalizedCoordinates = VK_FALSE, - }; - - sampler = device.GetLogical().CreateSampler(ci); -} - -void FSR::CreateShaders() { - if (device.IsFloat16Supported()) { - easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP16_COMP_SPV); - rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_COMP_SPV); - } else { - easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP32_COMP_SPV); - rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_COMP_SPV); - } -} - -void FSR::CreatePipeline() { - VkPipelineShaderStageCreateInfo shader_stage_easu{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_COMPUTE_BIT, - .module = *easu_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }; - - VkPipelineShaderStageCreateInfo shader_stage_rcas{ - .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = VK_SHADER_STAGE_COMPUTE_BIT, - .module = *rcas_shader, - .pName = "main", - .pSpecializationInfo = nullptr, - }; - - VkComputePipelineCreateInfo pipeline_ci_easu{ - .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = shader_stage_easu, - .layout = *pipeline_layout, - .basePipelineHandle = VK_NULL_HANDLE, - .basePipelineIndex = 0, - }; - - VkComputePipelineCreateInfo pipeline_ci_rcas{ - .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, - .pNext = nullptr, - .flags = 0, - .stage = shader_stage_rcas, - .layout = *pipeline_layout, - .basePipelineHandle = VK_NULL_HANDLE, - .basePipelineIndex = 0, - }; - - easu_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci_easu); - rcas_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci_rcas); -} - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_fsr.h b/src/video_core/renderer_vulkan/vk_fsr.h deleted file mode 100644 index 3505c1416..000000000 --- a/src/video_core/renderer_vulkan/vk_fsr.h +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/math_util.h" -#include "video_core/vulkan_common/vulkan_memory_allocator.h" -#include "video_core/vulkan_common/vulkan_wrapper.h" - -namespace Vulkan { - -class Device; -class Scheduler; - -class FSR { -public: - explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, - VkExtent2D output_size); - VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, - VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect); - -private: - void CreateDescriptorPool(); - void CreateDescriptorSetLayout(); - void CreateDescriptorSets(); - void CreateImages(); - void CreateSampler(); - void CreateShaders(); - void CreatePipeline(); - void CreatePipelineLayout(); - - void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const; - - const Device& device; - MemoryAllocator& memory_allocator; - size_t image_count; - VkExtent2D output_size; - - vk::DescriptorPool descriptor_pool; - vk::DescriptorSetLayout descriptor_set_layout; - vk::DescriptorSets descriptor_sets; - vk::PipelineLayout pipeline_layout; - vk::ShaderModule easu_shader; - vk::ShaderModule rcas_shader; - vk::Pipeline easu_pipeline; - vk::Pipeline rcas_pipeline; - vk::Sampler sampler; - std::vector<vk::Image> images; - std::vector<vk::ImageView> image_views; -}; - -} // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index 1e1821b10..20f7a9702 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp @@ -381,8 +381,9 @@ PipelineCache::PipelineCache(Tegra::MaxwellDeviceMemoryManager& device_memory_, .support_float64 = device.IsFloat64Supported(), .support_float16 = device.IsFloat16Supported(), .support_int64 = device.IsShaderInt64Supported(), - .needs_demote_reorder = - driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, + .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || + driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE || + driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY, .support_snorm_render_buffer = true, .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 5bf41b81f..aa0a027bb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -165,10 +165,9 @@ DrawParams MakeDrawParams(const MaxwellDrawState& draw_state, u32 num_instances, RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, Tegra::MaxwellDeviceMemoryManager& device_memory_, - ScreenInfo& screen_info_, const Device& device_, - MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, - Scheduler& scheduler_) - : gpu{gpu_}, device_memory{device_memory_}, screen_info{screen_info_}, device{device_}, + const Device& device_, MemoryAllocator& memory_allocator_, + StateTracker& state_tracker_, Scheduler& scheduler_) + : gpu{gpu_}, device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), guest_descriptor_queue(device, scheduler), compute_pass_descriptor_queue(device, scheduler), @@ -783,23 +782,29 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si query_cache.InvalidateRegion(*cpu_addr, copy_size); } -bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, - DAddr framebuffer_addr, u32 pixel_stride) { +std::optional<FramebufferTextureInfo> RasterizerVulkan::AccelerateDisplay( + const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, u32 pixel_stride) { if (!framebuffer_addr) { - return false; + return {}; } std::scoped_lock lock{texture_cache.mutex}; - ImageView* const image_view = + const auto [image_view, scaled] = texture_cache.TryFindFramebufferImageView(config, framebuffer_addr); if (!image_view) { - return false; + return {}; } query_cache.NotifySegment(false); - screen_info.image = image_view->ImageHandle(); - screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D); - screen_info.width = image_view->size.width; - screen_info.height = image_view->size.height; - return true; + + const auto& resolution = Settings::values.resolution_info; + + FramebufferTextureInfo info{}; + info.image = image_view->ImageHandle(); + info.image_view = image_view->Handle(Shader::TextureType::Color2D); + info.width = image_view->size.width; + info.height = image_view->size.height; + info.scaled_width = scaled ? resolution.ScaleUp(info.width) : info.width; + info.scaled_height = scaled ? resolution.ScaleUp(info.height) : info.height; + return info; } void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_loading, diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 881ee0993..0617b37f0 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -43,7 +43,7 @@ class Maxwell3D; namespace Vulkan { -struct ScreenInfo; +struct FramebufferTextureInfo; class StateTracker; @@ -78,9 +78,8 @@ class RasterizerVulkan final : public VideoCore::RasterizerInterface, public: explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, Tegra::MaxwellDeviceMemoryManager& device_memory_, - ScreenInfo& screen_info_, const Device& device_, - MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, - Scheduler& scheduler_); + const Device& device_, MemoryAllocator& memory_allocator_, + StateTracker& state_tracker_, Scheduler& scheduler_); ~RasterizerVulkan() override; void Draw(bool is_indexed, u32 instance_count) override; @@ -126,8 +125,6 @@ public: Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, std::span<const u8> memory) override; - bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, - u32 pixel_stride) override; void LoadDiskResources(u64 title_id, std::stop_token stop_loading, const VideoCore::DiskResourceLoadCallback& callback) override; @@ -137,6 +134,10 @@ public: void ReleaseChannel(s32 channel_id) override; + std::optional<FramebufferTextureInfo> AccelerateDisplay(const Tegra::FramebufferConfig& config, + VAddr framebuffer_addr, + u32 pixel_stride); + private: static constexpr size_t MAX_TEXTURES = 192; static constexpr size_t MAX_IMAGES = 48; @@ -182,7 +183,6 @@ private: Tegra::GPU& gpu; Tegra::MaxwellDeviceMemoryManager& device_memory; - ScreenInfo& screen_info; const Device& device; MemoryAllocator& memory_allocator; StateTracker& state_tracker; diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index a7400adfa..a20c956ff 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -713,12 +713,12 @@ bool TextureCache<P>::BlitImage(const Tegra::Engines::Fermi2D::Surface& dst, } template <class P> -typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView( +std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImageView( const Tegra::FramebufferConfig& config, DAddr cpu_addr) { // TODO: Properly implement this const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS); if (it == page_table.end()) { - return nullptr; + return {}; } const auto& image_map_ids = it->second; boost::container::small_vector<ImageId, 4> valid_image_ids; @@ -747,7 +747,8 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView( const auto GetImageViewForFramebuffer = [&](ImageId image_id) { const ImageViewInfo info{ImageViewType::e2D, view_format}; - return &slot_image_views[FindOrEmplaceImageView(image_id, info)]; + return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)], + slot_images[image_id].IsRescaled()); }; if (valid_image_ids.size() == 1) [[likely]] { @@ -761,7 +762,7 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView( return GetImageViewForFramebuffer(*most_recent); } - return nullptr; + return {}; } template <class P> diff --git a/src/video_core/texture_cache/texture_cache_base.h b/src/video_core/texture_cache/texture_cache_base.h index f9aebb293..e7b910121 100644 --- a/src/video_core/texture_cache/texture_cache_base.h +++ b/src/video_core/texture_cache/texture_cache_base.h @@ -212,8 +212,8 @@ public: const Tegra::Engines::Fermi2D::Config& copy); /// Try to find a cached image view in the given CPU address - [[nodiscard]] ImageView* TryFindFramebufferImageView(const Tegra::FramebufferConfig& config, - DAddr cpu_addr); + [[nodiscard]] std::pair<ImageView*, bool> TryFindFramebufferImageView( + const Tegra::FramebufferConfig& config, DAddr cpu_addr); /// Return true when there are uncommitted images to be downloaded [[nodiscard]] bool HasUncommittedFlushes() const noexcept; diff --git a/src/video_core/vulkan_common/vulkan_device.cpp b/src/video_core/vulkan_common/vulkan_device.cpp index 727bbd98d..d7216d349 100644 --- a/src/video_core/vulkan_common/vulkan_device.cpp +++ b/src/video_core/vulkan_common/vulkan_device.cpp @@ -868,6 +868,8 @@ std::string Device::GetDriverName() const { return "Qualcomm"; case VK_DRIVER_ID_ARM_PROPRIETARY: return "Mali"; + case VK_DRIVER_ID_SAMSUNG_PROPRIETARY: + return "Xclipse"; case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: return "SwiftShader"; case VK_DRIVER_ID_BROADCOM_PROPRIETARY: diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 9d38ab812..4dbe801a9 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -73,8 +73,11 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles")); + // Only show Linux tab on Unix + linux_tab->setVisible(false); #ifdef __unix__ + linux_tab->setVisible(true); ui->tabWidget->addTab(linux_tab.get(), tr("Linux")); #endif diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 303d84a1f..13381fea8 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1353,6 +1353,13 @@ void GMainWindow::InitializeHotkeys() { LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true); LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true); LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true); + LinkActionShortcut(ui->action_View_Lobby, + QStringLiteral("Multiplayer Browse Public Game Lobby")); + LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room")); + LinkActionShortcut(ui->action_Connect_To_Room, + QStringLiteral("Multiplayer Direct Connect to Room")); + LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room")); + LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room")); static const QString main_window = QStringLiteral("Main Window"); const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { diff --git a/src/yuzu/multiplayer/lobby.cpp b/src/yuzu/multiplayer/lobby.cpp index 41692c05b..77ac84295 100644 --- a/src/yuzu/multiplayer/lobby.cpp +++ b/src/yuzu/multiplayer/lobby.cpp @@ -77,16 +77,23 @@ Lobby::Lobby(QWidget* parent, QStandardItemModel* list, // UI Buttons connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); + connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty); connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); - connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom); // Actions connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this, &Lobby::OnRefreshLobby); + + // Load persistent filters after events are connected to make sure they apply + ui->search->setText( + QString::fromStdString(UISettings::values.multiplayer_filter_text.GetValue())); + ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned.GetValue()); + ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty.GetValue()); + ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full.GetValue()); } Lobby::~Lobby() = default; @@ -204,6 +211,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { // Save settings UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString(); + UISettings::values.multiplayer_filter_text = ui->search->text().toStdString(); + UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked(); + UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked(); + UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked(); UISettings::values.multiplayer_ip = proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString(); UISettings::values.multiplayer_port = diff --git a/src/yuzu/multiplayer/lobby_p.h b/src/yuzu/multiplayer/lobby_p.h index 068c95aca..398833e7a 100644 --- a/src/yuzu/multiplayer/lobby_p.h +++ b/src/yuzu/multiplayer/lobby_p.h @@ -193,12 +193,29 @@ public: } QVariant data(int role) const override { - if (role != Qt::DisplayRole) { + switch (role) { + case Qt::DisplayRole: { + auto members = data(MemberListRole).toList(); + return QStringLiteral("%1 / %2 ") + .arg(QString::number(members.size()), data(MaxPlayerRole).toString()); + } + case Qt::ForegroundRole: { + auto members = data(MemberListRole).toList(); + auto max_players = data(MaxPlayerRole).toInt(); + if (members.size() >= max_players) { + return QBrush(QColor(255, 48, 32)); + } else if (members.size() == (max_players - 1)) { + return QBrush(QColor(255, 140, 32)); + } else if (members.size() == 0) { + return QBrush(QColor(128, 128, 128)); + } + // FIXME: How to return a value that tells Qt not to modify the + // text color from the default (as if Qt::ForegroundRole wasn't overridden)? + return QBrush(nullptr); + } + default: return LobbyItem::data(role); } - auto members = data(MemberListRole).toList(); - return QStringLiteral("%1 / %2 ") - .arg(QString::number(members.size()), data(MaxPlayerRole).toString()); } bool operator<(const QStandardItem& other) const override { diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h index f9906be33..03e42b930 100644 --- a/src/yuzu/uisettings.h +++ b/src/yuzu/uisettings.h @@ -169,6 +169,13 @@ struct Values { // multiplayer settings Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer}; + Setting<std::string> multiplayer_filter_text{linkage, {}, "filter_text", Category::Multiplayer}; + Setting<bool> multiplayer_filter_games_owned{linkage, false, "filter_games_owned", + Category::Multiplayer}; + Setting<bool> multiplayer_filter_hide_empty{linkage, false, "filter_games_hide_empty", + Category::Multiplayer}; + Setting<bool> multiplayer_filter_hide_full{linkage, false, "filter_games_hide_full", + Category::Multiplayer}; Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer}; Setting<u16, true> multiplayer_port{linkage, 24872, 0, UINT16_MAX, "port", Category::Multiplayer}; @@ -222,7 +229,7 @@ void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig); // This must be in alphabetical order according to action name as it must have the same order as // UISetting::values.shortcuts, which is alphabetically ordered. // clang-format off -const std::array<Shortcut, 23> default_hotkeys{{ +const std::array<Shortcut, 28> default_hotkeys{{ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Mute/Unmute")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+M"), std::string("Home+Dpad_Right"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Down")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("-"), std::string("Home+Dpad_Down"), Qt::ApplicationShortcut, true}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Audio Volume Up")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("="), std::string("Home+Dpad_Up"), Qt::ApplicationShortcut, true}}, @@ -236,6 +243,11 @@ const std::array<Shortcut, 23> default_hotkeys{{ {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Fullscreen")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F11"), std::string("Home+B"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load File")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+O"), std::string(""), Qt::WidgetWithChildrenShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Load/Remove Amiibo")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F2"), std::string("Home+A"), Qt::WidgetWithChildrenShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Browse Public Game Lobby")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+B"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Create Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+N"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Direct Connect to Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+C"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Leave Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+L"), std::string(""), Qt::ApplicationShortcut, false}}, + {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Multiplayer Show Current Room")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+R"), std::string(""), Qt::ApplicationShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Restart Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F6"), std::string("R+Plus+Minus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Stop Emulation")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("F5"), std::string("L+Plus+Minus"), Qt::WindowShortcut, false}}, {QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "TAS Record")).toStdString(), QStringLiteral(QT_TRANSLATE_NOOP("Hotkeys", "Main Window")).toStdString(), {std::string("Ctrl+F7"), std::string(""), Qt::ApplicationShortcut, false}}, |