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 @@ | |||
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include <vector> | 10 | #include <vector> |
| 11 | #include <fmt/format.h> | 11 | #include <fmt/format.h> |
| 12 | #include "common/assert.h" | ||
| 12 | #include "common/common_types.h" | 13 | #include "common/common_types.h" |
| 13 | 14 | ||
| 14 | namespace Common { | 15 | namespace Common { |
| @@ -29,6 +30,8 @@ namespace Common { | |||
| 29 | 30 | ||
| 30 | template <std::size_t Size, bool le = false> | 31 | template <std::size_t Size, bool le = false> |
| 31 | [[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) { | 32 | [[nodiscard]] constexpr std::array<u8, Size> HexStringToArray(std::string_view str) { |
| 33 | ASSERT_MSG(Size * 2 <= str.size(), "Invalid string size"); | ||
| 34 | |||
| 32 | std::array<u8, Size> out{}; | 35 | std::array<u8, Size> out{}; |
| 33 | if constexpr (le) { | 36 | if constexpr (le) { |
| 34 | for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) { | 37 | 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>); | |||
| 186 | static_assert(std::is_trivially_destructible_v<VirtualAddress>); | 186 | static_assert(std::is_trivially_destructible_v<VirtualAddress>); |
| 187 | static_assert(std::is_trivially_destructible_v<ProcessAddress>); | 187 | static_assert(std::is_trivially_destructible_v<ProcessAddress>); |
| 188 | 188 | ||
| 189 | static_assert(Null<uint64_t> == 0); | 189 | static_assert(Null<uint64_t> == 0U); |
| 190 | static_assert(Null<PhysicalAddress> == Null<uint64_t>); | 190 | static_assert(Null<PhysicalAddress> == Null<uint64_t>); |
| 191 | static_assert(Null<VirtualAddress> == Null<uint64_t>); | 191 | static_assert(Null<VirtualAddress> == Null<uint64_t>); |
| 192 | static_assert(Null<ProcessAddress> == Null<uint64_t>); | 192 | static_assert(Null<ProcessAddress> == Null<uint64_t>); |
| 193 | 193 | ||
| 194 | // Constructor/assignment validations. | 194 | // Constructor/assignment validations. |
| 195 | static_assert([] { | 195 | static_assert([] { |
| 196 | const PhysicalAddress a(5); | 196 | const PhysicalAddress a(5U); |
| 197 | PhysicalAddress b(a); | 197 | PhysicalAddress b(a); |
| 198 | return b; | 198 | return b; |
| 199 | }() == PhysicalAddress(5)); | 199 | }() == PhysicalAddress(5U)); |
| 200 | static_assert([] { | 200 | static_assert([] { |
| 201 | const PhysicalAddress a(5); | 201 | const PhysicalAddress a(5U); |
| 202 | PhysicalAddress b(10); | 202 | PhysicalAddress b(10U); |
| 203 | b = a; | 203 | b = a; |
| 204 | return b; | 204 | return b; |
| 205 | }() == PhysicalAddress(5)); | 205 | }() == PhysicalAddress(5U)); |
| 206 | 206 | ||
| 207 | // Arithmetic validations. | 207 | // Arithmetic validations. |
| 208 | static_assert(PhysicalAddress(10) + 5 == PhysicalAddress(15)); | 208 | static_assert(PhysicalAddress(10U) + 5U == PhysicalAddress(15U)); |
| 209 | static_assert(PhysicalAddress(10) - 5 == PhysicalAddress(5)); | 209 | static_assert(PhysicalAddress(10U) - 5U == PhysicalAddress(5U)); |
| 210 | static_assert([] { | 210 | static_assert([] { |
| 211 | PhysicalAddress v(10); | 211 | PhysicalAddress v(10U); |
| 212 | v += 5; | 212 | v += 5U; |
| 213 | return v; | 213 | return v; |
| 214 | }() == PhysicalAddress(15)); | 214 | }() == PhysicalAddress(15U)); |
| 215 | static_assert([] { | 215 | static_assert([] { |
| 216 | PhysicalAddress v(10); | 216 | PhysicalAddress v(10U); |
| 217 | v -= 5; | 217 | v -= 5U; |
| 218 | return v; | 218 | return v; |
| 219 | }() == PhysicalAddress(5)); | 219 | }() == PhysicalAddress(5U)); |
| 220 | static_assert(PhysicalAddress(10)++ == PhysicalAddress(10)); | 220 | static_assert(PhysicalAddress(10U)++ == PhysicalAddress(10U)); |
| 221 | static_assert(++PhysicalAddress(10) == PhysicalAddress(11)); | 221 | static_assert(++PhysicalAddress(10U) == PhysicalAddress(11U)); |
| 222 | static_assert(PhysicalAddress(10)-- == PhysicalAddress(10)); | 222 | static_assert(PhysicalAddress(10U)-- == PhysicalAddress(10U)); |
| 223 | static_assert(--PhysicalAddress(10) == PhysicalAddress(9)); | 223 | static_assert(--PhysicalAddress(10U) == PhysicalAddress(9U)); |
| 224 | 224 | ||
| 225 | // Logical validations. | 225 | // Logical validations. |
| 226 | static_assert((PhysicalAddress(0b11111111) >> 1) == 0b01111111); | 226 | static_assert((PhysicalAddress(0b11111111U) >> 1) == 0b01111111U); |
| 227 | static_assert((PhysicalAddress(0b10101010) >> 1) == 0b01010101); | 227 | static_assert((PhysicalAddress(0b10101010U) >> 1) == 0b01010101U); |
| 228 | static_assert((PhysicalAddress(0b11111111) << 1) == 0b111111110); | 228 | static_assert((PhysicalAddress(0b11111111U) << 1) == 0b111111110U); |
| 229 | static_assert((PhysicalAddress(0b01010101) << 1) == 0b10101010); | 229 | static_assert((PhysicalAddress(0b01010101U) << 1) == 0b10101010U); |
| 230 | static_assert((PhysicalAddress(0b11111111) & 0b01010101) == 0b01010101); | 230 | static_assert((PhysicalAddress(0b11111111U) & 0b01010101U) == 0b01010101U); |
| 231 | static_assert((PhysicalAddress(0b11111111) & 0b10101010) == 0b10101010); | 231 | static_assert((PhysicalAddress(0b11111111U) & 0b10101010U) == 0b10101010U); |
| 232 | static_assert((PhysicalAddress(0b01010101) & 0b10101010) == 0b00000000); | 232 | static_assert((PhysicalAddress(0b01010101U) & 0b10101010U) == 0b00000000U); |
| 233 | static_assert((PhysicalAddress(0b00000000) | 0b01010101) == 0b01010101); | 233 | static_assert((PhysicalAddress(0b00000000U) | 0b01010101U) == 0b01010101U); |
| 234 | static_assert((PhysicalAddress(0b11111111) | 0b01010101) == 0b11111111); | 234 | static_assert((PhysicalAddress(0b11111111U) | 0b01010101U) == 0b11111111U); |
| 235 | static_assert((PhysicalAddress(0b10101010) | 0b01010101) == 0b11111111); | 235 | static_assert((PhysicalAddress(0b10101010U) | 0b01010101U) == 0b11111111U); |
| 236 | 236 | ||
| 237 | // Comparisons. | 237 | // Comparisons. |
| 238 | static_assert(PhysicalAddress(0) == PhysicalAddress(0)); | 238 | static_assert(PhysicalAddress(0U) == PhysicalAddress(0U)); |
| 239 | static_assert(PhysicalAddress(0) != PhysicalAddress(1)); | 239 | static_assert(PhysicalAddress(0U) != PhysicalAddress(1U)); |
| 240 | static_assert(PhysicalAddress(0) < PhysicalAddress(1)); | 240 | static_assert(PhysicalAddress(0U) < PhysicalAddress(1U)); |
| 241 | static_assert(PhysicalAddress(0) <= PhysicalAddress(1)); | 241 | static_assert(PhysicalAddress(0U) <= PhysicalAddress(1U)); |
| 242 | static_assert(PhysicalAddress(1) > PhysicalAddress(0)); | 242 | static_assert(PhysicalAddress(1U) > PhysicalAddress(0U)); |
| 243 | static_assert(PhysicalAddress(1) >= PhysicalAddress(0)); | 243 | static_assert(PhysicalAddress(1U) >= PhysicalAddress(0U)); |
| 244 | 244 | ||
| 245 | static_assert(!(PhysicalAddress(0) == PhysicalAddress(1))); | 245 | static_assert(!(PhysicalAddress(0U) == PhysicalAddress(1U))); |
| 246 | static_assert(!(PhysicalAddress(0) != PhysicalAddress(0))); | 246 | static_assert(!(PhysicalAddress(0U) != PhysicalAddress(0U))); |
| 247 | static_assert(!(PhysicalAddress(1) < PhysicalAddress(0))); | 247 | static_assert(!(PhysicalAddress(1U) < PhysicalAddress(0U))); |
| 248 | static_assert(!(PhysicalAddress(1) <= PhysicalAddress(0))); | 248 | static_assert(!(PhysicalAddress(1U) <= PhysicalAddress(0U))); |
| 249 | static_assert(!(PhysicalAddress(0) > PhysicalAddress(1))); | 249 | static_assert(!(PhysicalAddress(0U) > PhysicalAddress(1U))); |
| 250 | static_assert(!(PhysicalAddress(0) >= PhysicalAddress(1))); | 250 | static_assert(!(PhysicalAddress(0U) >= PhysicalAddress(1U))); |
| 251 | 251 | ||
| 252 | } // namespace Common | 252 | } // namespace Common |
| 253 | 253 | ||
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 | |||
| 775 | hle/service/nvnflinger/graphic_buffer_producer.h | 775 | hle/service/nvnflinger/graphic_buffer_producer.h |
| 776 | hle/service/nvnflinger/hos_binder_driver_server.cpp | 776 | hle/service/nvnflinger/hos_binder_driver_server.cpp |
| 777 | hle/service/nvnflinger/hos_binder_driver_server.h | 777 | hle/service/nvnflinger/hos_binder_driver_server.h |
| 778 | hle/service/nvnflinger/hardware_composer.cpp | ||
| 779 | hle/service/nvnflinger/hardware_composer.h | ||
| 780 | hle/service/nvnflinger/hwc_layer.h | ||
| 778 | hle/service/nvnflinger/nvnflinger.cpp | 781 | hle/service/nvnflinger/nvnflinger.cpp |
| 779 | hle/service/nvnflinger/nvnflinger.h | 782 | hle/service/nvnflinger/nvnflinger.h |
| 780 | hle/service/nvnflinger/parcel.h | 783 | 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 | |||
| 383 | } else if (id == CPSR_REGISTER) { | 383 | } else if (id == CPSR_REGISTER) { |
| 384 | return ValueToHex(context.pstate); | 384 | return ValueToHex(context.pstate); |
| 385 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { | 385 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { |
| 386 | return ValueToHex(fprs[id - D0_REGISTER][0]); | 386 | return ValueToHex(fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2]); |
| 387 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { | 387 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { |
| 388 | return ValueToHex(fprs[id - Q0_REGISTER]); | 388 | return ValueToHex(fprs[id - Q0_REGISTER]); |
| 389 | } else if (id == FPSCR_REGISTER) { | 389 | } else if (id == FPSCR_REGISTER) { |
| @@ -406,7 +406,7 @@ void GDBStubA32::RegWrite(Kernel::KThread* thread, size_t id, std::string_view v | |||
| 406 | } else if (id == CPSR_REGISTER) { | 406 | } else if (id == CPSR_REGISTER) { |
| 407 | context.pstate = HexToValue<u32>(value); | 407 | context.pstate = HexToValue<u32>(value); |
| 408 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { | 408 | } else if (id >= D0_REGISTER && id < Q0_REGISTER) { |
| 409 | fprs[id - D0_REGISTER] = {HexToValue<u64>(value), 0}; | 409 | fprs[(id - D0_REGISTER) / 2][(id - D0_REGISTER) % 2] = HexToValue<u64>(value); |
| 410 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { | 410 | } else if (id >= Q0_REGISTER && id < FPSCR_REGISTER) { |
| 411 | fprs[id - Q0_REGISTER] = HexToValue<u128>(value); | 411 | fprs[id - Q0_REGISTER] = HexToValue<u128>(value); |
| 412 | } else if (id == FPSCR_REGISTER) { | 412 | } 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 @@ | |||
| 5 | #include "core/hle/service/caps/caps_a.h" | 5 | #include "core/hle/service/caps/caps_a.h" |
| 6 | #include "core/hle/service/caps/caps_manager.h" | 6 | #include "core/hle/service/caps/caps_manager.h" |
| 7 | #include "core/hle/service/caps/caps_result.h" | 7 | #include "core/hle/service/caps/caps_result.h" |
| 8 | #include "core/hle/service/caps/caps_types.h" | 8 | #include "core/hle/service/cmif_serialization.h" |
| 9 | #include "core/hle/service/ipc_helpers.h" | 9 | #include "core/hle/service/ipc_helpers.h" |
| 10 | 10 | ||
| 11 | namespace Service::Capture { | 11 | namespace Service::Capture { |
| @@ -18,9 +18,9 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, | |||
| 18 | {0, nullptr, "GetAlbumFileCount"}, | 18 | {0, nullptr, "GetAlbumFileCount"}, |
| 19 | {1, nullptr, "GetAlbumFileList"}, | 19 | {1, nullptr, "GetAlbumFileList"}, |
| 20 | {2, nullptr, "LoadAlbumFile"}, | 20 | {2, nullptr, "LoadAlbumFile"}, |
| 21 | {3, &IAlbumAccessorService::DeleteAlbumFile, "DeleteAlbumFile"}, | 21 | {3, C<&IAlbumAccessorService::DeleteAlbumFile>, "DeleteAlbumFile"}, |
| 22 | {4, nullptr, "StorageCopyAlbumFile"}, | 22 | {4, nullptr, "StorageCopyAlbumFile"}, |
| 23 | {5, &IAlbumAccessorService::IsAlbumMounted, "IsAlbumMounted"}, | 23 | {5, C<&IAlbumAccessorService::IsAlbumMounted>, "IsAlbumMounted"}, |
| 24 | {6, nullptr, "GetAlbumUsage"}, | 24 | {6, nullptr, "GetAlbumUsage"}, |
| 25 | {7, nullptr, "GetAlbumFileSize"}, | 25 | {7, nullptr, "GetAlbumFileSize"}, |
| 26 | {8, nullptr, "LoadAlbumFileThumbnail"}, | 26 | {8, nullptr, "LoadAlbumFileThumbnail"}, |
| @@ -33,18 +33,18 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, | |||
| 33 | {15, nullptr, "GetAlbumUsage3"}, | 33 | {15, nullptr, "GetAlbumUsage3"}, |
| 34 | {16, nullptr, "GetAlbumMountResult"}, | 34 | {16, nullptr, "GetAlbumMountResult"}, |
| 35 | {17, nullptr, "GetAlbumUsage16"}, | 35 | {17, nullptr, "GetAlbumUsage16"}, |
| 36 | {18, &IAlbumAccessorService::Unknown18, "Unknown18"}, | 36 | {18, C<&IAlbumAccessorService::Unknown18>, "Unknown18"}, |
| 37 | {19, nullptr, "Unknown19"}, | 37 | {19, nullptr, "Unknown19"}, |
| 38 | {100, nullptr, "GetAlbumFileCountEx0"}, | 38 | {100, nullptr, "GetAlbumFileCountEx0"}, |
| 39 | {101, &IAlbumAccessorService::GetAlbumFileListEx0, "GetAlbumFileListEx0"}, | 39 | {101, C<&IAlbumAccessorService::GetAlbumFileListEx0>, "GetAlbumFileListEx0"}, |
| 40 | {202, nullptr, "SaveEditedScreenShot"}, | 40 | {202, nullptr, "SaveEditedScreenShot"}, |
| 41 | {301, nullptr, "GetLastThumbnail"}, | 41 | {301, nullptr, "GetLastThumbnail"}, |
| 42 | {302, nullptr, "GetLastOverlayMovieThumbnail"}, | 42 | {302, nullptr, "GetLastOverlayMovieThumbnail"}, |
| 43 | {401, &IAlbumAccessorService::GetAutoSavingStorage, "GetAutoSavingStorage"}, | 43 | {401, C<&IAlbumAccessorService::GetAutoSavingStorage>, "GetAutoSavingStorage"}, |
| 44 | {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, | 44 | {501, nullptr, "GetRequiredStorageSpaceSizeToCopyAll"}, |
| 45 | {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, | 45 | {1001, nullptr, "LoadAlbumScreenShotThumbnailImageEx0"}, |
| 46 | {1002, &IAlbumAccessorService::LoadAlbumScreenShotImageEx1, "LoadAlbumScreenShotImageEx1"}, | 46 | {1002, C<&IAlbumAccessorService::LoadAlbumScreenShotImageEx1>, "LoadAlbumScreenShotImageEx1"}, |
| 47 | {1003, &IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1, "LoadAlbumScreenShotThumbnailImageEx1"}, | 47 | {1003, C<&IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1>, "LoadAlbumScreenShotThumbnailImageEx1"}, |
| 48 | {8001, nullptr, "ForceAlbumUnmounted"}, | 48 | {8001, nullptr, "ForceAlbumUnmounted"}, |
| 49 | {8002, nullptr, "ResetAlbumMountStatus"}, | 49 | {8002, nullptr, "ResetAlbumMountStatus"}, |
| 50 | {8011, nullptr, "RefreshAlbumCache"}, | 50 | {8011, nullptr, "RefreshAlbumCache"}, |
| @@ -62,138 +62,70 @@ IAlbumAccessorService::IAlbumAccessorService(Core::System& system_, | |||
| 62 | 62 | ||
| 63 | IAlbumAccessorService::~IAlbumAccessorService() = default; | 63 | IAlbumAccessorService::~IAlbumAccessorService() = default; |
| 64 | 64 | ||
| 65 | void IAlbumAccessorService::DeleteAlbumFile(HLERequestContext& ctx) { | 65 | Result IAlbumAccessorService::DeleteAlbumFile(AlbumFileId file_id) { |
| 66 | IPC::RequestParser rp{ctx}; | ||
| 67 | const auto file_id{rp.PopRaw<AlbumFileId>()}; | ||
| 68 | |||
| 69 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", | 66 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}", |
| 70 | file_id.application_id, file_id.storage, file_id.type); | 67 | file_id.application_id, file_id.storage, file_id.type); |
| 71 | 68 | ||
| 72 | Result result = manager->DeleteAlbumFile(file_id); | 69 | const Result result = manager->DeleteAlbumFile(file_id); |
| 73 | result = TranslateResult(result); | 70 | R_RETURN(TranslateResult(result)); |
| 74 | |||
| 75 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 76 | rb.Push(result); | ||
| 77 | } | 71 | } |
| 78 | 72 | ||
| 79 | void IAlbumAccessorService::IsAlbumMounted(HLERequestContext& ctx) { | 73 | Result IAlbumAccessorService::IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage) { |
| 80 | IPC::RequestParser rp{ctx}; | ||
| 81 | const auto storage{rp.PopEnum<AlbumStorage>()}; | ||
| 82 | |||
| 83 | LOG_INFO(Service_Capture, "called, storage={}", storage); | 74 | LOG_INFO(Service_Capture, "called, storage={}", storage); |
| 84 | 75 | ||
| 85 | Result result = manager->IsAlbumMounted(storage); | 76 | const Result result = manager->IsAlbumMounted(storage); |
| 86 | const bool is_mounted = result.IsSuccess(); | 77 | *out_is_mounted = result.IsSuccess(); |
| 87 | result = TranslateResult(result); | 78 | R_RETURN(TranslateResult(result)); |
| 88 | |||
| 89 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 90 | rb.Push(result); | ||
| 91 | rb.Push<u8>(is_mounted); | ||
| 92 | } | 79 | } |
| 93 | 80 | ||
| 94 | void IAlbumAccessorService::Unknown18(HLERequestContext& ctx) { | 81 | Result IAlbumAccessorService::Unknown18( |
| 95 | struct UnknownBuffer { | 82 | Out<u32> out_buffer_size, |
| 96 | INSERT_PADDING_BYTES(0x10); | 83 | OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_buffer) { |
| 97 | }; | ||
| 98 | static_assert(sizeof(UnknownBuffer) == 0x10, "UnknownBuffer is an invalid size"); | ||
| 99 | |||
| 100 | LOG_WARNING(Service_Capture, "(STUBBED) called"); | 84 | LOG_WARNING(Service_Capture, "(STUBBED) called"); |
| 101 | 85 | *out_buffer_size = 0; | |
| 102 | std::vector<UnknownBuffer> buffer{}; | 86 | R_SUCCEED(); |
| 103 | |||
| 104 | if (!buffer.empty()) { | ||
| 105 | ctx.WriteBuffer(buffer); | ||
| 106 | } | ||
| 107 | |||
| 108 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 109 | rb.Push(ResultSuccess); | ||
| 110 | rb.Push(static_cast<u32>(buffer.size())); | ||
| 111 | } | 87 | } |
| 112 | 88 | ||
| 113 | void IAlbumAccessorService::GetAlbumFileListEx0(HLERequestContext& ctx) { | 89 | Result IAlbumAccessorService::GetAlbumFileListEx0( |
| 114 | IPC::RequestParser rp{ctx}; | 90 | Out<u64> out_entries_size, AlbumStorage storage, u8 flags, |
| 115 | const auto storage{rp.PopEnum<AlbumStorage>()}; | 91 | OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries) { |
| 116 | const auto flags{rp.Pop<u8>()}; | ||
| 117 | const auto album_entry_size{ctx.GetWriteBufferNumElements<AlbumEntry>()}; | ||
| 118 | |||
| 119 | LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags); | 92 | LOG_INFO(Service_Capture, "called, storage={}, flags={}", storage, flags); |
| 120 | 93 | ||
| 121 | std::vector<AlbumEntry> entries; | 94 | const Result result = manager->GetAlbumFileList(out_entries, *out_entries_size, storage, flags); |
| 122 | Result result = manager->GetAlbumFileList(entries, storage, flags); | 95 | R_RETURN(TranslateResult(result)); |
| 123 | result = TranslateResult(result); | ||
| 124 | |||
| 125 | entries.resize(std::min(album_entry_size, entries.size())); | ||
| 126 | |||
| 127 | if (!entries.empty()) { | ||
| 128 | ctx.WriteBuffer(entries); | ||
| 129 | } | ||
| 130 | |||
| 131 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 132 | rb.Push(result); | ||
| 133 | rb.Push<u64>(entries.size()); | ||
| 134 | } | 96 | } |
| 135 | 97 | ||
| 136 | void IAlbumAccessorService::GetAutoSavingStorage(HLERequestContext& ctx) { | 98 | Result IAlbumAccessorService::GetAutoSavingStorage(Out<bool> out_is_autosaving) { |
| 137 | LOG_WARNING(Service_Capture, "(STUBBED) called"); | 99 | LOG_WARNING(Service_Capture, "(STUBBED) called"); |
| 138 | 100 | ||
| 139 | bool is_autosaving{}; | 101 | const Result result = manager->GetAutoSavingStorage(*out_is_autosaving); |
| 140 | Result result = manager->GetAutoSavingStorage(is_autosaving); | 102 | R_RETURN(TranslateResult(result)); |
| 141 | result = TranslateResult(result); | ||
| 142 | |||
| 143 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 144 | rb.Push(result); | ||
| 145 | rb.Push<u8>(is_autosaving); | ||
| 146 | } | 103 | } |
| 147 | 104 | ||
| 148 | void IAlbumAccessorService::LoadAlbumScreenShotImageEx1(HLERequestContext& ctx) { | 105 | Result IAlbumAccessorService::LoadAlbumScreenShotImageEx1( |
| 149 | IPC::RequestParser rp{ctx}; | 106 | const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options, |
| 150 | const auto file_id{rp.PopRaw<AlbumFileId>()}; | 107 | OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output, |
| 151 | const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; | 108 | OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image, |
| 152 | const auto image_buffer_size{ctx.GetWriteBufferSize(1)}; | 109 | OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) { |
| 153 | |||
| 154 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", | 110 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", |
| 155 | file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); | 111 | file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); |
| 156 | 112 | ||
| 157 | std::vector<u8> image; | 113 | const Result result = |
| 158 | LoadAlbumScreenShotImageOutput image_output; | 114 | manager->LoadAlbumScreenShotImage(*out_image_output, out_image, file_id, decoder_options); |
| 159 | Result result = | 115 | R_RETURN(TranslateResult(result)); |
| 160 | manager->LoadAlbumScreenShotImage(image_output, image, file_id, decoder_options); | ||
| 161 | result = TranslateResult(result); | ||
| 162 | |||
| 163 | if (image.size() > image_buffer_size) { | ||
| 164 | result = ResultWorkMemoryError; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (result.IsSuccess()) { | ||
| 168 | ctx.WriteBuffer(image_output, 0); | ||
| 169 | ctx.WriteBuffer(image, 1); | ||
| 170 | } | ||
| 171 | |||
| 172 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 173 | rb.Push(result); | ||
| 174 | } | 116 | } |
| 175 | 117 | ||
| 176 | void IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx) { | 118 | Result IAlbumAccessorService::LoadAlbumScreenShotThumbnailImageEx1( |
| 177 | IPC::RequestParser rp{ctx}; | 119 | const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options, |
| 178 | const auto file_id{rp.PopRaw<AlbumFileId>()}; | 120 | OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output, |
| 179 | const auto decoder_options{rp.PopRaw<ScreenShotDecodeOption>()}; | 121 | OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image, |
| 180 | 122 | OutArray<u8, BufferAttr_HipcMapAlias> out_buffer) { | |
| 181 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", | 123 | LOG_INFO(Service_Capture, "called, application_id=0x{:0x}, storage={}, type={}, flags={}", |
| 182 | file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); | 124 | file_id.application_id, file_id.storage, file_id.type, decoder_options.flags); |
| 183 | 125 | ||
| 184 | std::vector<u8> image(ctx.GetWriteBufferSize(1)); | 126 | const Result result = manager->LoadAlbumScreenShotThumbnail(*out_image_output, out_image, |
| 185 | LoadAlbumScreenShotImageOutput image_output; | 127 | file_id, decoder_options); |
| 186 | Result result = | 128 | R_RETURN(TranslateResult(result)); |
| 187 | manager->LoadAlbumScreenShotThumbnail(image_output, image, file_id, decoder_options); | ||
| 188 | result = TranslateResult(result); | ||
| 189 | |||
| 190 | if (result.IsSuccess()) { | ||
| 191 | ctx.WriteBuffer(image_output, 0); | ||
| 192 | ctx.WriteBuffer(image, 1); | ||
| 193 | } | ||
| 194 | |||
| 195 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 196 | rb.Push(result); | ||
| 197 | } | 129 | } |
| 198 | 130 | ||
| 199 | Result IAlbumAccessorService::TranslateResult(Result in_result) { | 131 | 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 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/caps/caps_types.h" | ||
| 7 | #include "core/hle/service/cmif_types.h" | ||
| 6 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 7 | 9 | ||
| 8 | namespace Core { | 10 | namespace Core { |
| @@ -19,13 +21,31 @@ public: | |||
| 19 | ~IAlbumAccessorService() override; | 21 | ~IAlbumAccessorService() override; |
| 20 | 22 | ||
| 21 | private: | 23 | private: |
| 22 | void DeleteAlbumFile(HLERequestContext& ctx); | 24 | Result DeleteAlbumFile(AlbumFileId file_id); |
| 23 | void IsAlbumMounted(HLERequestContext& ctx); | 25 | |
| 24 | void Unknown18(HLERequestContext& ctx); | 26 | Result IsAlbumMounted(Out<bool> out_is_mounted, AlbumStorage storage); |
| 25 | void GetAlbumFileListEx0(HLERequestContext& ctx); | 27 | |
| 26 | void GetAutoSavingStorage(HLERequestContext& ctx); | 28 | Result Unknown18( |
| 27 | void LoadAlbumScreenShotImageEx1(HLERequestContext& ctx); | 29 | Out<u32> out_buffer_size, |
| 28 | void LoadAlbumScreenShotThumbnailImageEx1(HLERequestContext& ctx); | 30 | OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> |
| 31 | out_buffer); | ||
| 32 | |||
| 33 | Result GetAlbumFileListEx0(Out<u64> out_entries_size, AlbumStorage storage, u8 flags, | ||
| 34 | OutArray<AlbumEntry, BufferAttr_HipcMapAlias> out_entries); | ||
| 35 | |||
| 36 | Result GetAutoSavingStorage(Out<bool> out_is_autosaving); | ||
| 37 | |||
| 38 | Result LoadAlbumScreenShotImageEx1( | ||
| 39 | const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options, | ||
| 40 | OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output, | ||
| 41 | OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image, | ||
| 42 | OutArray<u8, BufferAttr_HipcMapAlias> out_buffer); | ||
| 43 | |||
| 44 | Result LoadAlbumScreenShotThumbnailImageEx1( | ||
| 45 | const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options, | ||
| 46 | OutLargeData<LoadAlbumScreenShotImageOutput, BufferAttr_HipcMapAlias> out_image_output, | ||
| 47 | OutArray<u8, BufferAttr_HipcMapAlias | BufferAttr_HipcMapTransferAllowsNonSecure> out_image, | ||
| 48 | OutArray<u8, BufferAttr_HipcMapAlias> out_buffer); | ||
| 29 | 49 | ||
| 30 | Result TranslateResult(Result in_result); | 50 | Result TranslateResult(Result in_result); |
| 31 | 51 | ||
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 @@ | |||
| 6 | #include "core/hle/service/caps/caps_manager.h" | 6 | #include "core/hle/service/caps/caps_manager.h" |
| 7 | #include "core/hle/service/caps/caps_result.h" | 7 | #include "core/hle/service/caps/caps_result.h" |
| 8 | #include "core/hle/service/caps/caps_types.h" | 8 | #include "core/hle/service/caps/caps_types.h" |
| 9 | #include "core/hle/service/cmif_serialization.h" | ||
| 9 | #include "core/hle/service/ipc_helpers.h" | 10 | #include "core/hle/service/ipc_helpers.h" |
| 10 | 11 | ||
| 11 | namespace Service::Capture { | 12 | namespace Service::Capture { |
| @@ -17,7 +18,7 @@ IAlbumControlService::IAlbumControlService(Core::System& system_, | |||
| 17 | static const FunctionInfo functions[] = { | 18 | static const FunctionInfo functions[] = { |
| 18 | {1, nullptr, "CaptureRawImage"}, | 19 | {1, nullptr, "CaptureRawImage"}, |
| 19 | {2, nullptr, "CaptureRawImageWithTimeout"}, | 20 | {2, nullptr, "CaptureRawImageWithTimeout"}, |
| 20 | {33, &IAlbumControlService::SetShimLibraryVersion, "SetShimLibraryVersion"}, | 21 | {33, C<&IAlbumControlService::SetShimLibraryVersion>, "SetShimLibraryVersion"}, |
| 21 | {1001, nullptr, "RequestTakingScreenShot"}, | 22 | {1001, nullptr, "RequestTakingScreenShot"}, |
| 22 | {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, | 23 | {1002, nullptr, "RequestTakingScreenShotWithTimeout"}, |
| 23 | {1011, nullptr, "NotifyTakingScreenShotRefused"}, | 24 | {1011, nullptr, "NotifyTakingScreenShotRefused"}, |
| @@ -42,16 +43,11 @@ IAlbumControlService::IAlbumControlService(Core::System& system_, | |||
| 42 | 43 | ||
| 43 | IAlbumControlService::~IAlbumControlService() = default; | 44 | IAlbumControlService::~IAlbumControlService() = default; |
| 44 | 45 | ||
| 45 | void IAlbumControlService::SetShimLibraryVersion(HLERequestContext& ctx) { | 46 | Result IAlbumControlService::SetShimLibraryVersion(ShimLibraryVersion library_version, |
| 46 | IPC::RequestParser rp{ctx}; | 47 | ClientAppletResourceUserId aruid) { |
| 47 | const auto library_version{rp.Pop<u64>()}; | ||
| 48 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 49 | |||
| 50 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", | 48 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", |
| 51 | library_version, applet_resource_user_id); | 49 | library_version, aruid.pid); |
| 52 | 50 | R_SUCCEED(); | |
| 53 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 54 | rb.Push(ResultSuccess); | ||
| 55 | } | 51 | } |
| 56 | 52 | ||
| 57 | } // namespace Service::Capture | 53 | } // 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 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 6 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 7 | 8 | ||
| 8 | namespace Core { | 9 | namespace Core { |
| @@ -11,6 +12,7 @@ class System; | |||
| 11 | 12 | ||
| 12 | namespace Service::Capture { | 13 | namespace Service::Capture { |
| 13 | class AlbumManager; | 14 | class AlbumManager; |
| 15 | enum class ShimLibraryVersion : u64; | ||
| 14 | 16 | ||
| 15 | class IAlbumControlService final : public ServiceFramework<IAlbumControlService> { | 17 | class IAlbumControlService final : public ServiceFramework<IAlbumControlService> { |
| 16 | public: | 18 | public: |
| @@ -19,7 +21,8 @@ public: | |||
| 19 | ~IAlbumControlService() override; | 21 | ~IAlbumControlService() override; |
| 20 | 22 | ||
| 21 | private: | 23 | private: |
| 22 | void SetShimLibraryVersion(HLERequestContext& ctx); | 24 | Result SetShimLibraryVersion(ShimLibraryVersion library_version, |
| 25 | ClientAppletResourceUserId aruid); | ||
| 23 | 26 | ||
| 24 | std::shared_ptr<AlbumManager> manager = nullptr; | 27 | std::shared_ptr<AlbumManager> manager = nullptr; |
| 25 | }; | 28 | }; |
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) { | |||
| 58 | return is_mounted ? ResultSuccess : ResultIsNotMounted; | 58 | return is_mounted ? ResultSuccess : ResultIsNotMounted; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, | 61 | Result AlbumManager::GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count, |
| 62 | u8 flags) const { | 62 | AlbumStorage storage, u8 flags) const { |
| 63 | if (storage > AlbumStorage::Sd) { | 63 | if (storage > AlbumStorage::Sd) { |
| 64 | return ResultInvalidStorage; | 64 | return ResultInvalidStorage; |
| 65 | } | 65 | } |
| @@ -72,51 +72,55 @@ Result AlbumManager::GetAlbumFileList(std::vector<AlbumEntry>& out_entries, Albu | |||
| 72 | if (file_id.storage != storage) { | 72 | if (file_id.storage != storage) { |
| 73 | continue; | 73 | continue; |
| 74 | } | 74 | } |
| 75 | if (out_entries.size() >= SdAlbumFileLimit) { | 75 | if (out_entries_count >= SdAlbumFileLimit) { |
| 76 | break; | ||
| 77 | } | ||
| 78 | if (out_entries_count >= out_entries.size()) { | ||
| 76 | break; | 79 | break; |
| 77 | } | 80 | } |
| 78 | 81 | ||
| 79 | const auto entry_size = Common::FS::GetSize(path); | 82 | const auto entry_size = Common::FS::GetSize(path); |
| 80 | out_entries.push_back({ | 83 | out_entries[out_entries_count++] = { |
| 81 | .entry_size = entry_size, | 84 | .entry_size = entry_size, |
| 82 | .file_id = file_id, | 85 | .file_id = file_id, |
| 83 | }); | 86 | }; |
| 84 | } | 87 | } |
| 85 | 88 | ||
| 86 | return ResultSuccess; | 89 | return ResultSuccess; |
| 87 | } | 90 | } |
| 88 | 91 | ||
| 89 | Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, | 92 | Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries, |
| 90 | ContentType content_type, s64 start_posix_time, | 93 | u64& out_entries_count, ContentType content_type, |
| 91 | s64 end_posix_time, u64 aruid) const { | 94 | s64 start_posix_time, s64 end_posix_time, u64 aruid) const { |
| 92 | if (!is_mounted) { | 95 | if (!is_mounted) { |
| 93 | return ResultIsNotMounted; | 96 | return ResultIsNotMounted; |
| 94 | } | 97 | } |
| 95 | 98 | ||
| 96 | std::vector<ApplicationAlbumEntry> album_entries; | 99 | std::vector<ApplicationAlbumEntry> album_entries(out_entries.size()); |
| 97 | const auto start_date = ConvertToAlbumDateTime(start_posix_time); | 100 | const auto start_date = ConvertToAlbumDateTime(start_posix_time); |
| 98 | const auto end_date = ConvertToAlbumDateTime(end_posix_time); | 101 | const auto end_date = ConvertToAlbumDateTime(end_posix_time); |
| 99 | const auto result = GetAlbumFileList(album_entries, content_type, start_date, end_date, aruid); | 102 | const auto result = GetAlbumFileList(album_entries, out_entries_count, content_type, start_date, |
| 103 | end_date, aruid); | ||
| 100 | 104 | ||
| 101 | if (result.IsError()) { | 105 | if (result.IsError()) { |
| 102 | return result; | 106 | return result; |
| 103 | } | 107 | } |
| 104 | 108 | ||
| 105 | for (const auto& album_entry : album_entries) { | 109 | for (std::size_t i = 0; i < out_entries_count; i++) { |
| 106 | ApplicationAlbumFileEntry entry{ | 110 | out_entries[i] = { |
| 107 | .entry = album_entry, | 111 | .entry = album_entries[i], |
| 108 | .datetime = album_entry.datetime, | 112 | .datetime = album_entries[i].datetime, |
| 109 | .unknown = {}, | 113 | .unknown = {}, |
| 110 | }; | 114 | }; |
| 111 | out_entries.push_back(entry); | ||
| 112 | } | 115 | } |
| 113 | 116 | ||
| 114 | return ResultSuccess; | 117 | return ResultSuccess; |
| 115 | } | 118 | } |
| 116 | 119 | ||
| 117 | Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, | 120 | Result AlbumManager::GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries, |
| 118 | ContentType content_type, AlbumFileDateTime start_date, | 121 | u64& out_entries_count, ContentType content_type, |
| 119 | AlbumFileDateTime end_date, u64 aruid) const { | 122 | AlbumFileDateTime start_date, AlbumFileDateTime end_date, |
| 123 | u64 aruid) const { | ||
| 120 | if (!is_mounted) { | 124 | if (!is_mounted) { |
| 121 | return ResultIsNotMounted; | 125 | return ResultIsNotMounted; |
| 122 | } | 126 | } |
| @@ -131,12 +135,15 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en | |||
| 131 | if (file_id.date < end_date) { | 135 | if (file_id.date < end_date) { |
| 132 | continue; | 136 | continue; |
| 133 | } | 137 | } |
| 134 | if (out_entries.size() >= SdAlbumFileLimit) { | 138 | if (out_entries_count >= SdAlbumFileLimit) { |
| 139 | break; | ||
| 140 | } | ||
| 141 | if (out_entries_count >= out_entries.size()) { | ||
| 135 | break; | 142 | break; |
| 136 | } | 143 | } |
| 137 | 144 | ||
| 138 | const auto entry_size = Common::FS::GetSize(path); | 145 | const auto entry_size = Common::FS::GetSize(path); |
| 139 | ApplicationAlbumEntry entry{ | 146 | out_entries[out_entries_count++] = { |
| 140 | .size = entry_size, | 147 | .size = entry_size, |
| 141 | .hash{}, | 148 | .hash{}, |
| 142 | .datetime = file_id.date, | 149 | .datetime = file_id.date, |
| @@ -144,7 +151,6 @@ Result AlbumManager::GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_en | |||
| 144 | .content = content_type, | 151 | .content = content_type, |
| 145 | .unknown = 1, | 152 | .unknown = 1, |
| 146 | }; | 153 | }; |
| 147 | out_entries.push_back(entry); | ||
| 148 | } | 154 | } |
| 149 | 155 | ||
| 150 | return ResultSuccess; | 156 | return ResultSuccess; |
| @@ -156,8 +162,7 @@ Result AlbumManager::GetAutoSavingStorage(bool& out_is_autosaving) const { | |||
| 156 | } | 162 | } |
| 157 | 163 | ||
| 158 | Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, | 164 | Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, |
| 159 | std::vector<u8>& out_image, | 165 | std::span<u8> out_image, const AlbumFileId& file_id, |
| 160 | const AlbumFileId& file_id, | ||
| 161 | const ScreenShotDecodeOption& decoder_options) const { | 166 | const ScreenShotDecodeOption& decoder_options) const { |
| 162 | if (file_id.storage > AlbumStorage::Sd) { | 167 | if (file_id.storage > AlbumStorage::Sd) { |
| 163 | return ResultInvalidStorage; | 168 | return ResultInvalidStorage; |
| @@ -176,7 +181,9 @@ Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& ou | |||
| 176 | .orientation = AlbumImageOrientation::None, | 181 | .orientation = AlbumImageOrientation::None, |
| 177 | .unknown_1{}, | 182 | .unknown_1{}, |
| 178 | .unknown_2{}, | 183 | .unknown_2{}, |
| 184 | .pad163{}, | ||
| 179 | }, | 185 | }, |
| 186 | .pad179{}, | ||
| 180 | }; | 187 | }; |
| 181 | 188 | ||
| 182 | std::filesystem::path path; | 189 | std::filesystem::path path; |
| @@ -186,14 +193,12 @@ Result AlbumManager::LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& ou | |||
| 186 | return result; | 193 | return result; |
| 187 | } | 194 | } |
| 188 | 195 | ||
| 189 | out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha); | ||
| 190 | |||
| 191 | return LoadImage(out_image, path, static_cast<int>(out_image_output.width), | 196 | return LoadImage(out_image, path, static_cast<int>(out_image_output.width), |
| 192 | +static_cast<int>(out_image_output.height), decoder_options.flags); | 197 | +static_cast<int>(out_image_output.height), decoder_options.flags); |
| 193 | } | 198 | } |
| 194 | 199 | ||
| 195 | Result AlbumManager::LoadAlbumScreenShotThumbnail( | 200 | Result AlbumManager::LoadAlbumScreenShotThumbnail( |
| 196 | LoadAlbumScreenShotImageOutput& out_image_output, std::vector<u8>& out_image, | 201 | LoadAlbumScreenShotImageOutput& out_image_output, std::span<u8> out_image, |
| 197 | const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const { | 202 | const AlbumFileId& file_id, const ScreenShotDecodeOption& decoder_options) const { |
| 198 | if (file_id.storage > AlbumStorage::Sd) { | 203 | if (file_id.storage > AlbumStorage::Sd) { |
| 199 | return ResultInvalidStorage; | 204 | return ResultInvalidStorage; |
| @@ -212,7 +217,9 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail( | |||
| 212 | .orientation = AlbumImageOrientation::None, | 217 | .orientation = AlbumImageOrientation::None, |
| 213 | .unknown_1{}, | 218 | .unknown_1{}, |
| 214 | .unknown_2{}, | 219 | .unknown_2{}, |
| 220 | .pad163{}, | ||
| 215 | }, | 221 | }, |
| 222 | .pad179{}, | ||
| 216 | }; | 223 | }; |
| 217 | 224 | ||
| 218 | std::filesystem::path path; | 225 | std::filesystem::path path; |
| @@ -222,8 +229,6 @@ Result AlbumManager::LoadAlbumScreenShotThumbnail( | |||
| 222 | return result; | 229 | return result; |
| 223 | } | 230 | } |
| 224 | 231 | ||
| 225 | out_image.resize(out_image_output.height * out_image_output.width * STBI_rgb_alpha); | ||
| 226 | |||
| 227 | return LoadImage(out_image, path, static_cast<int>(out_image_output.width), | 232 | return LoadImage(out_image, path, static_cast<int>(out_image_output.width), |
| 228 | +static_cast<int>(out_image_output.height), decoder_options.flags); | 233 | +static_cast<int>(out_image_output.height), decoder_options.flags); |
| 229 | } | 234 | } |
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: | |||
| 42 | 42 | ||
| 43 | Result DeleteAlbumFile(const AlbumFileId& file_id); | 43 | Result DeleteAlbumFile(const AlbumFileId& file_id); |
| 44 | Result IsAlbumMounted(AlbumStorage storage); | 44 | Result IsAlbumMounted(AlbumStorage storage); |
| 45 | Result GetAlbumFileList(std::vector<AlbumEntry>& out_entries, AlbumStorage storage, | 45 | Result GetAlbumFileList(std::span<AlbumEntry> out_entries, u64& out_entries_count, |
| 46 | u8 flags) const; | 46 | AlbumStorage storage, u8 flags) const; |
| 47 | Result GetAlbumFileList(std::vector<ApplicationAlbumFileEntry>& out_entries, | 47 | Result GetAlbumFileList(std::span<ApplicationAlbumFileEntry> out_entries, |
| 48 | ContentType content_type, s64 start_posix_time, s64 end_posix_time, | 48 | u64& out_entries_count, ContentType content_type, s64 start_posix_time, |
| 49 | u64 aruid) const; | 49 | s64 end_posix_time, u64 aruid) const; |
| 50 | Result GetAlbumFileList(std::vector<ApplicationAlbumEntry>& out_entries, | 50 | Result GetAlbumFileList(std::span<ApplicationAlbumEntry> out_entries, u64& out_entries_count, |
| 51 | ContentType content_type, AlbumFileDateTime start_date, | 51 | ContentType content_type, AlbumFileDateTime start_date, |
| 52 | AlbumFileDateTime end_date, u64 aruid) const; | 52 | AlbumFileDateTime end_date, u64 aruid) const; |
| 53 | Result GetAutoSavingStorage(bool& out_is_autosaving) const; | 53 | Result GetAutoSavingStorage(bool& out_is_autosaving) const; |
| 54 | Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, | 54 | Result LoadAlbumScreenShotImage(LoadAlbumScreenShotImageOutput& out_image_output, |
| 55 | std::vector<u8>& out_image, const AlbumFileId& file_id, | 55 | std::span<u8> out_image, const AlbumFileId& file_id, |
| 56 | const ScreenShotDecodeOption& decoder_options) const; | 56 | const ScreenShotDecodeOption& decoder_options) const; |
| 57 | Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output, | 57 | Result LoadAlbumScreenShotThumbnail(LoadAlbumScreenShotImageOutput& out_image_output, |
| 58 | std::vector<u8>& out_image, const AlbumFileId& file_id, | 58 | std::span<u8> out_image, const AlbumFileId& file_id, |
| 59 | const ScreenShotDecodeOption& decoder_options) const; | 59 | const ScreenShotDecodeOption& decoder_options) const; |
| 60 | 60 | ||
| 61 | Result SaveScreenShot(ApplicationAlbumEntry& out_entry, const ScreenShotAttribute& attribute, | 61 | 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 @@ | |||
| 3 | 3 | ||
| 4 | #include "common/logging/log.h" | 4 | #include "common/logging/log.h" |
| 5 | #include "core/hle/service/caps/caps_manager.h" | 5 | #include "core/hle/service/caps/caps_manager.h" |
| 6 | #include "core/hle/service/caps/caps_types.h" | ||
| 7 | #include "core/hle/service/ipc_helpers.h" | ||
| 8 | |||
| 9 | #include "core/hle/service/caps/caps_ss.h" | 6 | #include "core/hle/service/caps/caps_ss.h" |
| 7 | #include "core/hle/service/cmif_serialization.h" | ||
| 8 | #include "core/hle/service/ipc_helpers.h" | ||
| 10 | 9 | ||
| 11 | namespace Service::Capture { | 10 | namespace Service::Capture { |
| 12 | 11 | ||
| @@ -17,9 +16,9 @@ IScreenShotService::IScreenShotService(Core::System& system_, | |||
| 17 | static const FunctionInfo functions[] = { | 16 | static const FunctionInfo functions[] = { |
| 18 | {201, nullptr, "SaveScreenShot"}, | 17 | {201, nullptr, "SaveScreenShot"}, |
| 19 | {202, nullptr, "SaveEditedScreenShot"}, | 18 | {202, nullptr, "SaveEditedScreenShot"}, |
| 20 | {203, &IScreenShotService::SaveScreenShotEx0, "SaveScreenShotEx0"}, | 19 | {203, C<&IScreenShotService::SaveScreenShotEx0>, "SaveScreenShotEx0"}, |
| 21 | {204, nullptr, "SaveEditedScreenShotEx0"}, | 20 | {204, nullptr, "SaveEditedScreenShotEx0"}, |
| 22 | {206, &IScreenShotService::SaveEditedScreenShotEx1, "SaveEditedScreenShotEx1"}, | 21 | {206, C<&IScreenShotService::SaveEditedScreenShotEx1>, "SaveEditedScreenShotEx1"}, |
| 23 | {208, nullptr, "SaveScreenShotOfMovieEx1"}, | 22 | {208, nullptr, "SaveScreenShotOfMovieEx1"}, |
| 24 | {1000, nullptr, "Unknown1000"}, | 23 | {1000, nullptr, "Unknown1000"}, |
| 25 | }; | 24 | }; |
| @@ -30,69 +29,38 @@ IScreenShotService::IScreenShotService(Core::System& system_, | |||
| 30 | 29 | ||
| 31 | IScreenShotService::~IScreenShotService() = default; | 30 | IScreenShotService::~IScreenShotService() = default; |
| 32 | 31 | ||
| 33 | void IScreenShotService::SaveScreenShotEx0(HLERequestContext& ctx) { | 32 | Result IScreenShotService::SaveScreenShotEx0( |
| 34 | IPC::RequestParser rp{ctx}; | 33 | Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, |
| 35 | struct Parameters { | 34 | AlbumReportOption report_option, ClientAppletResourceUserId aruid, |
| 36 | ScreenShotAttribute attribute{}; | 35 | InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> |
| 37 | AlbumReportOption report_option{}; | 36 | image_data_buffer) { |
| 38 | INSERT_PADDING_BYTES(0x4); | ||
| 39 | u64 applet_resource_user_id{}; | ||
| 40 | }; | ||
| 41 | static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size."); | ||
| 42 | |||
| 43 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 44 | const auto image_data_buffer = ctx.ReadBuffer(); | ||
| 45 | |||
| 46 | LOG_INFO(Service_Capture, | 37 | LOG_INFO(Service_Capture, |
| 47 | "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", | 38 | "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", |
| 48 | parameters.report_option, image_data_buffer.size(), | 39 | report_option, image_data_buffer.size(), aruid.pid); |
| 49 | parameters.applet_resource_user_id); | ||
| 50 | 40 | ||
| 51 | ApplicationAlbumEntry entry{}; | ||
| 52 | manager->FlipVerticallyOnWrite(false); | 41 | manager->FlipVerticallyOnWrite(false); |
| 53 | const auto result = | 42 | R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer, |
| 54 | manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, | 43 | aruid.pid)); |
| 55 | image_data_buffer, parameters.applet_resource_user_id); | ||
| 56 | |||
| 57 | IPC::ResponseBuilder rb{ctx, 10}; | ||
| 58 | rb.Push(result); | ||
| 59 | rb.PushRaw(entry); | ||
| 60 | } | 44 | } |
| 61 | 45 | ||
| 62 | void IScreenShotService::SaveEditedScreenShotEx1(HLERequestContext& ctx) { | 46 | Result IScreenShotService::SaveEditedScreenShotEx1( |
| 63 | IPC::RequestParser rp{ctx}; | 47 | Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width, |
| 64 | struct Parameters { | 48 | u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id, |
| 65 | ScreenShotAttribute attribute; | 49 | const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer, |
| 66 | u64 width; | 50 | const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> |
| 67 | u64 height; | 51 | image_data_buffer, |
| 68 | u64 thumbnail_width; | 52 | const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> |
| 69 | u64 thumbnail_height; | 53 | thumbnail_image_data_buffer) { |
| 70 | AlbumFileId file_id; | ||
| 71 | }; | ||
| 72 | static_assert(sizeof(Parameters) == 0x78, "Parameters has incorrect size."); | ||
| 73 | |||
| 74 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 75 | const auto application_data_buffer = ctx.ReadBuffer(0); | ||
| 76 | const auto image_data_buffer = ctx.ReadBuffer(1); | ||
| 77 | const auto thumbnail_image_data_buffer = ctx.ReadBuffer(2); | ||
| 78 | |||
| 79 | LOG_INFO(Service_Capture, | 54 | LOG_INFO(Service_Capture, |
| 80 | "called, width={}, height={}, thumbnail_width={}, thumbnail_height={}, " | 55 | "called, width={}, height={}, thumbnail_width={}, thumbnail_height={}, " |
| 81 | "application_id={:016x}, storage={}, type={}, app_data_buffer_size={}, " | 56 | "application_id={:016x}, storage={}, type={}, " |
| 82 | "image_data_buffer_size={}, thumbnail_image_buffer_size={}", | 57 | "image_data_buffer_size={}, thumbnail_image_buffer_size={}", |
| 83 | parameters.width, parameters.height, parameters.thumbnail_width, | 58 | width, height, thumbnail_width, thumbnail_height, file_id.application_id, |
| 84 | parameters.thumbnail_height, parameters.file_id.application_id, | 59 | file_id.storage, file_id.type, image_data_buffer.size(), |
| 85 | parameters.file_id.storage, parameters.file_id.type, application_data_buffer.size(), | 60 | thumbnail_image_data_buffer.size()); |
| 86 | image_data_buffer.size(), thumbnail_image_data_buffer.size()); | ||
| 87 | 61 | ||
| 88 | ApplicationAlbumEntry entry{}; | ||
| 89 | manager->FlipVerticallyOnWrite(false); | 62 | manager->FlipVerticallyOnWrite(false); |
| 90 | const auto result = manager->SaveEditedScreenShot(entry, parameters.attribute, | 63 | R_RETURN(manager->SaveEditedScreenShot(*out_entry, attribute, file_id, image_data_buffer)); |
| 91 | parameters.file_id, image_data_buffer); | ||
| 92 | |||
| 93 | IPC::ResponseBuilder rb{ctx, 10}; | ||
| 94 | rb.Push(result); | ||
| 95 | rb.PushRaw(entry); | ||
| 96 | } | 64 | } |
| 97 | 65 | ||
| 98 | } // namespace Service::Capture | 66 | } // 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 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/caps/caps_types.h" | ||
| 7 | #include "core/hle/service/cmif_types.h" | ||
| 6 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 7 | 9 | ||
| 8 | namespace Core { | 10 | namespace Core { |
| @@ -17,8 +19,20 @@ public: | |||
| 17 | ~IScreenShotService() override; | 19 | ~IScreenShotService() override; |
| 18 | 20 | ||
| 19 | private: | 21 | private: |
| 20 | void SaveScreenShotEx0(HLERequestContext& ctx); | 22 | Result SaveScreenShotEx0( |
| 21 | void SaveEditedScreenShotEx1(HLERequestContext& ctx); | 23 | Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, |
| 24 | AlbumReportOption report_option, ClientAppletResourceUserId aruid, | ||
| 25 | InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> | ||
| 26 | image_data_buffer); | ||
| 27 | |||
| 28 | Result SaveEditedScreenShotEx1( | ||
| 29 | Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, u64 width, | ||
| 30 | u64 height, u64 thumbnail_width, u64 thumbnail_height, const AlbumFileId& file_id, | ||
| 31 | const InLargeData<std::array<u8, 0x400>, BufferAttr_HipcMapAlias> application_data_buffer, | ||
| 32 | const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> | ||
| 33 | image_data_buffer, | ||
| 34 | const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> | ||
| 35 | thumbnail_image_data_buffer); | ||
| 22 | 36 | ||
| 23 | std::shared_ptr<AlbumManager> manager; | 37 | std::shared_ptr<AlbumManager> manager; |
| 24 | }; | 38 | }; |
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 @@ | |||
| 6 | #include "core/hle/service/caps/caps_manager.h" | 6 | #include "core/hle/service/caps/caps_manager.h" |
| 7 | #include "core/hle/service/caps/caps_su.h" | 7 | #include "core/hle/service/caps/caps_su.h" |
| 8 | #include "core/hle/service/caps/caps_types.h" | 8 | #include "core/hle/service/caps/caps_types.h" |
| 9 | #include "core/hle/service/cmif_serialization.h" | ||
| 9 | #include "core/hle/service/ipc_helpers.h" | 10 | #include "core/hle/service/ipc_helpers.h" |
| 10 | #include "video_core/renderer_base.h" | 11 | #include "video_core/renderer_base.h" |
| 11 | 12 | ||
| @@ -16,10 +17,10 @@ IScreenShotApplicationService::IScreenShotApplicationService( | |||
| 16 | : ServiceFramework{system_, "caps:su"}, manager{album_manager} { | 17 | : ServiceFramework{system_, "caps:su"}, manager{album_manager} { |
| 17 | // clang-format off | 18 | // clang-format off |
| 18 | static const FunctionInfo functions[] = { | 19 | static const FunctionInfo functions[] = { |
| 19 | {32, &IScreenShotApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, | 20 | {32, C<&IScreenShotApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"}, |
| 20 | {201, nullptr, "SaveScreenShot"}, | 21 | {201, nullptr, "SaveScreenShot"}, |
| 21 | {203, &IScreenShotApplicationService::SaveScreenShotEx0, "SaveScreenShotEx0"}, | 22 | {203, C<&IScreenShotApplicationService::SaveScreenShotEx0>, "SaveScreenShotEx0"}, |
| 22 | {205, &IScreenShotApplicationService::SaveScreenShotEx1, "SaveScreenShotEx1"}, | 23 | {205, C<&IScreenShotApplicationService::SaveScreenShotEx1>, "SaveScreenShotEx1"}, |
| 23 | {210, nullptr, "SaveScreenShotEx2"}, | 24 | {210, nullptr, "SaveScreenShotEx2"}, |
| 24 | }; | 25 | }; |
| 25 | // clang-format on | 26 | // clang-format on |
| @@ -29,77 +30,40 @@ IScreenShotApplicationService::IScreenShotApplicationService( | |||
| 29 | 30 | ||
| 30 | IScreenShotApplicationService::~IScreenShotApplicationService() = default; | 31 | IScreenShotApplicationService::~IScreenShotApplicationService() = default; |
| 31 | 32 | ||
| 32 | void IScreenShotApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { | 33 | Result IScreenShotApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version, |
| 33 | IPC::RequestParser rp{ctx}; | 34 | ClientAppletResourceUserId aruid) { |
| 34 | const auto library_version{rp.Pop<u64>()}; | ||
| 35 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 36 | |||
| 37 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", | 35 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", |
| 38 | library_version, applet_resource_user_id); | 36 | library_version, aruid.pid); |
| 39 | 37 | R_SUCCEED(); | |
| 40 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 41 | rb.Push(ResultSuccess); | ||
| 42 | } | 38 | } |
| 43 | 39 | ||
| 44 | void IScreenShotApplicationService::SaveScreenShotEx0(HLERequestContext& ctx) { | 40 | Result IScreenShotApplicationService::SaveScreenShotEx0( |
| 45 | IPC::RequestParser rp{ctx}; | 41 | Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, |
| 46 | struct Parameters { | 42 | AlbumReportOption report_option, ClientAppletResourceUserId aruid, |
| 47 | ScreenShotAttribute attribute{}; | 43 | InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> |
| 48 | AlbumReportOption report_option{}; | 44 | image_data_buffer) { |
| 49 | INSERT_PADDING_BYTES(0x4); | ||
| 50 | u64 applet_resource_user_id{}; | ||
| 51 | }; | ||
| 52 | static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size."); | ||
| 53 | |||
| 54 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 55 | const auto image_data_buffer = ctx.ReadBuffer(); | ||
| 56 | |||
| 57 | LOG_INFO(Service_Capture, | 45 | LOG_INFO(Service_Capture, |
| 58 | "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", | 46 | "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", |
| 59 | parameters.report_option, image_data_buffer.size(), | 47 | report_option, image_data_buffer.size(), aruid.pid); |
| 60 | parameters.applet_resource_user_id); | ||
| 61 | 48 | ||
| 62 | ApplicationAlbumEntry entry{}; | ||
| 63 | manager->FlipVerticallyOnWrite(false); | 49 | manager->FlipVerticallyOnWrite(false); |
| 64 | const auto result = | 50 | R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, image_data_buffer, |
| 65 | manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, | 51 | aruid.pid)); |
| 66 | image_data_buffer, parameters.applet_resource_user_id); | ||
| 67 | |||
| 68 | IPC::ResponseBuilder rb{ctx, 10}; | ||
| 69 | rb.Push(result); | ||
| 70 | rb.PushRaw(entry); | ||
| 71 | } | 52 | } |
| 72 | 53 | ||
| 73 | void IScreenShotApplicationService::SaveScreenShotEx1(HLERequestContext& ctx) { | 54 | Result IScreenShotApplicationService::SaveScreenShotEx1( |
| 74 | IPC::RequestParser rp{ctx}; | 55 | Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, |
| 75 | struct Parameters { | 56 | AlbumReportOption report_option, ClientAppletResourceUserId aruid, |
| 76 | ScreenShotAttribute attribute{}; | 57 | const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer, |
| 77 | AlbumReportOption report_option{}; | 58 | const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> |
| 78 | INSERT_PADDING_BYTES(0x4); | 59 | image_data_buffer) { |
| 79 | u64 applet_resource_user_id{}; | ||
| 80 | }; | ||
| 81 | static_assert(sizeof(Parameters) == 0x50, "Parameters has incorrect size."); | ||
| 82 | |||
| 83 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 84 | const auto app_data_buffer = ctx.ReadBuffer(0); | ||
| 85 | const auto image_data_buffer = ctx.ReadBuffer(1); | ||
| 86 | |||
| 87 | LOG_INFO(Service_Capture, | 60 | LOG_INFO(Service_Capture, |
| 88 | "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", | 61 | "called, report_option={}, image_data_buffer_size={}, applet_resource_user_id={}", |
| 89 | parameters.report_option, image_data_buffer.size(), | 62 | report_option, image_data_buffer.size(), aruid.pid); |
| 90 | parameters.applet_resource_user_id); | ||
| 91 | 63 | ||
| 92 | ApplicationAlbumEntry entry{}; | ||
| 93 | ApplicationData app_data{}; | ||
| 94 | std::memcpy(&app_data, app_data_buffer.data(), sizeof(ApplicationData)); | ||
| 95 | manager->FlipVerticallyOnWrite(false); | 64 | manager->FlipVerticallyOnWrite(false); |
| 96 | const auto result = | 65 | R_RETURN(manager->SaveScreenShot(*out_entry, attribute, report_option, *app_data_buffer, |
| 97 | manager->SaveScreenShot(entry, parameters.attribute, parameters.report_option, app_data, | 66 | image_data_buffer, aruid.pid)); |
| 98 | image_data_buffer, parameters.applet_resource_user_id); | ||
| 99 | |||
| 100 | IPC::ResponseBuilder rb{ctx, 10}; | ||
| 101 | rb.Push(result); | ||
| 102 | rb.PushRaw(entry); | ||
| 103 | } | 67 | } |
| 104 | 68 | ||
| 105 | void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) { | 69 | void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption report_option) { |
| @@ -112,6 +76,7 @@ void IScreenShotApplicationService::CaptureAndSaveScreenshot(AlbumReportOption r | |||
| 112 | .orientation = Capture::AlbumImageOrientation::None, | 76 | .orientation = Capture::AlbumImageOrientation::None, |
| 113 | .unknown_1{}, | 77 | .unknown_1{}, |
| 114 | .unknown_2{}, | 78 | .unknown_2{}, |
| 79 | .pad163{}, | ||
| 115 | }; | 80 | }; |
| 116 | 81 | ||
| 117 | renderer.RequestScreenshot( | 82 | 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 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/caps/caps_types.h" | ||
| 7 | #include "core/hle/service/cmif_types.h" | ||
| 6 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 7 | 9 | ||
| 8 | namespace Core { | 10 | namespace Core { |
| @@ -26,9 +28,19 @@ private: | |||
| 26 | static constexpr std::size_t screenshot_height = 720; | 28 | static constexpr std::size_t screenshot_height = 720; |
| 27 | static constexpr std::size_t bytes_per_pixel = 4; | 29 | static constexpr std::size_t bytes_per_pixel = 4; |
| 28 | 30 | ||
| 29 | void SetShimLibraryVersion(HLERequestContext& ctx); | 31 | Result SetShimLibraryVersion(ShimLibraryVersion library_version, |
| 30 | void SaveScreenShotEx0(HLERequestContext& ctx); | 32 | ClientAppletResourceUserId aruid); |
| 31 | void SaveScreenShotEx1(HLERequestContext& ctx); | 33 | Result SaveScreenShotEx0( |
| 34 | Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, | ||
| 35 | AlbumReportOption report_option, ClientAppletResourceUserId aruid, | ||
| 36 | InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> | ||
| 37 | image_data_buffer); | ||
| 38 | Result SaveScreenShotEx1( | ||
| 39 | Out<ApplicationAlbumEntry> out_entry, const ScreenShotAttribute& attribute, | ||
| 40 | AlbumReportOption report_option, ClientAppletResourceUserId aruid, | ||
| 41 | const InLargeData<ApplicationData, BufferAttr_HipcMapAlias> app_data_buffer, | ||
| 42 | const InBuffer<BufferAttr_HipcMapTransferAllowsNonSecure | BufferAttr_HipcMapAlias> | ||
| 43 | image_data_buffer); | ||
| 32 | 44 | ||
| 33 | std::array<u8, screenshot_width * screenshot_height * bytes_per_pixel> image_data; | 45 | std::array<u8, screenshot_width * screenshot_height * bytes_per_pixel> image_data; |
| 34 | 46 | ||
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 { | |||
| 41 | EnableBlockSmoothing = 1 << 1, | 41 | EnableBlockSmoothing = 1 << 1, |
| 42 | }; | 42 | }; |
| 43 | 43 | ||
| 44 | enum class ShimLibraryVersion : u64 { | ||
| 45 | Version1 = 1, | ||
| 46 | }; | ||
| 47 | |||
| 44 | // This is nn::capsrv::AlbumFileDateTime | 48 | // This is nn::capsrv::AlbumFileDateTime |
| 45 | struct AlbumFileDateTime { | 49 | struct AlbumFileDateTime { |
| 46 | s16 year{}; | 50 | s16 year{}; |
| @@ -144,19 +148,23 @@ static_assert(sizeof(ApplicationAlbumFileEntry) == 0x30, | |||
| 144 | "ApplicationAlbumFileEntry has incorrect size."); | 148 | "ApplicationAlbumFileEntry has incorrect size."); |
| 145 | 149 | ||
| 146 | struct ApplicationData { | 150 | struct ApplicationData { |
| 147 | std::array<u8, 0x400> data{}; | 151 | std::array<u8, 0x400> data; |
| 148 | u32 data_size{}; | 152 | u32 data_size; |
| 149 | }; | 153 | }; |
| 150 | static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size"); | 154 | static_assert(sizeof(ApplicationData) == 0x404, "ApplicationData is an invalid size"); |
| 155 | static_assert(std::is_trivial_v<ApplicationData>, | ||
| 156 | "ApplicationData type must be trivially copyable."); | ||
| 151 | 157 | ||
| 152 | struct ScreenShotAttribute { | 158 | struct ScreenShotAttribute { |
| 153 | u32 unknown_0{}; | 159 | u32 unknown_0; |
| 154 | AlbumImageOrientation orientation{}; | 160 | AlbumImageOrientation orientation; |
| 155 | u32 unknown_1{}; | 161 | u32 unknown_1; |
| 156 | u32 unknown_2{}; | 162 | u32 unknown_2; |
| 157 | INSERT_PADDING_BYTES(0x30); | 163 | INSERT_PADDING_BYTES_NOINIT(0x30); |
| 158 | }; | 164 | }; |
| 159 | static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size"); | 165 | static_assert(sizeof(ScreenShotAttribute) == 0x40, "ScreenShotAttribute is an invalid size"); |
| 166 | static_assert(std::is_trivial_v<ScreenShotAttribute>, | ||
| 167 | "ScreenShotAttribute type must be trivially copyable."); | ||
| 160 | 168 | ||
| 161 | struct ScreenShotDecodeOption { | 169 | struct ScreenShotDecodeOption { |
| 162 | ScreenShotDecoderFlag flags{}; | 170 | ScreenShotDecoderFlag flags{}; |
| @@ -165,13 +173,15 @@ struct ScreenShotDecodeOption { | |||
| 165 | static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size"); | 173 | static_assert(sizeof(ScreenShotDecodeOption) == 0x20, "ScreenShotDecodeOption is an invalid size"); |
| 166 | 174 | ||
| 167 | struct LoadAlbumScreenShotImageOutput { | 175 | struct LoadAlbumScreenShotImageOutput { |
| 168 | s64 width{}; | 176 | s64 width; |
| 169 | s64 height{}; | 177 | s64 height; |
| 170 | ScreenShotAttribute attribute{}; | 178 | ScreenShotAttribute attribute; |
| 171 | INSERT_PADDING_BYTES(0x400); | 179 | INSERT_PADDING_BYTES_NOINIT(0x400); |
| 172 | }; | 180 | }; |
| 173 | static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450, | 181 | static_assert(sizeof(LoadAlbumScreenShotImageOutput) == 0x450, |
| 174 | "LoadAlbumScreenShotImageOutput is an invalid size"); | 182 | "LoadAlbumScreenShotImageOutput is an invalid size"); |
| 183 | static_assert(std::is_trivial_v<LoadAlbumScreenShotImageOutput>, | ||
| 184 | "LoadAlbumScreenShotImageOutput type must be trivially copyable."); | ||
| 175 | 185 | ||
| 176 | struct LoadAlbumScreenShotImageOutputForApplication { | 186 | struct LoadAlbumScreenShotImageOutputForApplication { |
| 177 | s64 width{}; | 187 | 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 @@ | |||
| 5 | #include "core/hle/service/caps/caps_manager.h" | 5 | #include "core/hle/service/caps/caps_manager.h" |
| 6 | #include "core/hle/service/caps/caps_types.h" | 6 | #include "core/hle/service/caps/caps_types.h" |
| 7 | #include "core/hle/service/caps/caps_u.h" | 7 | #include "core/hle/service/caps/caps_u.h" |
| 8 | #include "core/hle/service/cmif_serialization.h" | ||
| 8 | #include "core/hle/service/ipc_helpers.h" | 9 | #include "core/hle/service/ipc_helpers.h" |
| 9 | 10 | ||
| 10 | namespace Service::Capture { | 11 | namespace Service::Capture { |
| @@ -14,8 +15,8 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_, | |||
| 14 | : ServiceFramework{system_, "caps:u"}, manager{album_manager} { | 15 | : ServiceFramework{system_, "caps:u"}, manager{album_manager} { |
| 15 | // clang-format off | 16 | // clang-format off |
| 16 | static const FunctionInfo functions[] = { | 17 | static const FunctionInfo functions[] = { |
| 17 | {32, &IAlbumApplicationService::SetShimLibraryVersion, "SetShimLibraryVersion"}, | 18 | {32, C<&IAlbumApplicationService::SetShimLibraryVersion>, "SetShimLibraryVersion"}, |
| 18 | {102, &IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated, "GetAlbumFileList0AafeAruidDeprecated"}, | 19 | {102, C<&IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated>, "GetAlbumFileList0AafeAruidDeprecated"}, |
| 19 | {103, nullptr, "DeleteAlbumFileByAruid"}, | 20 | {103, nullptr, "DeleteAlbumFileByAruid"}, |
| 20 | {104, nullptr, "GetAlbumFileSizeByAruid"}, | 21 | {104, nullptr, "GetAlbumFileSizeByAruid"}, |
| 21 | {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, | 22 | {105, nullptr, "DeleteAlbumFileByAruidForDebug"}, |
| @@ -24,7 +25,7 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_, | |||
| 24 | {130, nullptr, "PrecheckToCreateContentsByAruid"}, | 25 | {130, nullptr, "PrecheckToCreateContentsByAruid"}, |
| 25 | {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, | 26 | {140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"}, |
| 26 | {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, | 27 | {141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"}, |
| 27 | {142, &IAlbumApplicationService::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"}, | 28 | {142, C<&IAlbumApplicationService::GetAlbumFileList3AaeAruid>, "GetAlbumFileList3AaeAruid"}, |
| 28 | {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, | 29 | {143, nullptr, "GetAlbumFileList4AaeUidAruid"}, |
| 29 | {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, | 30 | {144, nullptr, "GetAllAlbumFileList3AaeAruid"}, |
| 30 | {60002, nullptr, "OpenAccessorSessionForApplication"}, | 31 | {60002, nullptr, "OpenAccessorSessionForApplication"}, |
| @@ -36,101 +37,40 @@ IAlbumApplicationService::IAlbumApplicationService(Core::System& system_, | |||
| 36 | 37 | ||
| 37 | IAlbumApplicationService::~IAlbumApplicationService() = default; | 38 | IAlbumApplicationService::~IAlbumApplicationService() = default; |
| 38 | 39 | ||
| 39 | void IAlbumApplicationService::SetShimLibraryVersion(HLERequestContext& ctx) { | 40 | Result IAlbumApplicationService::SetShimLibraryVersion(ShimLibraryVersion library_version, |
| 40 | IPC::RequestParser rp{ctx}; | 41 | ClientAppletResourceUserId aruid) { |
| 41 | const auto library_version{rp.Pop<u64>()}; | ||
| 42 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 43 | |||
| 44 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", | 42 | LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}", |
| 45 | library_version, applet_resource_user_id); | 43 | library_version, aruid.pid); |
| 46 | 44 | R_SUCCEED(); | |
| 47 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 48 | rb.Push(ResultSuccess); | ||
| 49 | } | 45 | } |
| 50 | 46 | ||
| 51 | void IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx) { | 47 | Result IAlbumApplicationService::GetAlbumFileList0AafeAruidDeprecated( |
| 52 | IPC::RequestParser rp{ctx}; | 48 | Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time, s64 end_posix_time, |
| 53 | struct Parameters { | 49 | ClientAppletResourceUserId aruid, |
| 54 | ContentType content_type; | 50 | OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries) { |
| 55 | INSERT_PADDING_BYTES(7); | ||
| 56 | s64 start_posix_time; | ||
| 57 | s64 end_posix_time; | ||
| 58 | u64 applet_resource_user_id; | ||
| 59 | }; | ||
| 60 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 61 | |||
| 62 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 63 | |||
| 64 | LOG_WARNING(Service_Capture, | 51 | LOG_WARNING(Service_Capture, |
| 65 | "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, " | 52 | "(STUBBED) called. content_type={}, start_posix_time={}, end_posix_time={}, " |
| 66 | "applet_resource_user_id={}", | 53 | "applet_resource_user_id={}", |
| 67 | parameters.content_type, parameters.start_posix_time, parameters.end_posix_time, | 54 | content_type, start_posix_time, end_posix_time, aruid.pid); |
| 68 | parameters.applet_resource_user_id); | ||
| 69 | |||
| 70 | Result result = ResultSuccess; | ||
| 71 | |||
| 72 | if (result.IsSuccess()) { | ||
| 73 | result = manager->IsAlbumMounted(AlbumStorage::Sd); | ||
| 74 | } | ||
| 75 | |||
| 76 | std::vector<ApplicationAlbumFileEntry> entries; | ||
| 77 | if (result.IsSuccess()) { | ||
| 78 | result = manager->GetAlbumFileList(entries, parameters.content_type, | ||
| 79 | parameters.start_posix_time, parameters.end_posix_time, | ||
| 80 | parameters.applet_resource_user_id); | ||
| 81 | } | ||
| 82 | 55 | ||
| 83 | if (!entries.empty()) { | 56 | R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd)); |
| 84 | ctx.WriteBuffer(entries); | 57 | R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type, |
| 85 | } | 58 | start_posix_time, end_posix_time, aruid.pid)); |
| 86 | |||
| 87 | IPC::ResponseBuilder rb{ctx, 4}; | ||
| 88 | rb.Push(result); | ||
| 89 | rb.Push<u64>(entries.size()); | ||
| 90 | } | 59 | } |
| 91 | 60 | ||
| 92 | void IAlbumApplicationService::GetAlbumFileList3AaeAruid(HLERequestContext& ctx) { | 61 | Result IAlbumApplicationService::GetAlbumFileList3AaeAruid( |
| 93 | IPC::RequestParser rp{ctx}; | 62 | Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time, |
| 94 | struct Parameters { | 63 | AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid, |
| 95 | ContentType content_type; | 64 | OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries) { |
| 96 | INSERT_PADDING_BYTES(1); | ||
| 97 | AlbumFileDateTime start_date_time; | ||
| 98 | AlbumFileDateTime end_date_time; | ||
| 99 | INSERT_PADDING_BYTES(6); | ||
| 100 | u64 applet_resource_user_id; | ||
| 101 | }; | ||
| 102 | static_assert(sizeof(Parameters) == 0x20, "Parameters has incorrect size."); | ||
| 103 | |||
| 104 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 105 | |||
| 106 | LOG_WARNING(Service_Capture, | 65 | LOG_WARNING(Service_Capture, |
| 107 | "(STUBBED) called. content_type={}, start_date={}/{}/{}, " | 66 | "(STUBBED) called. content_type={}, start_date={}/{}/{}, " |
| 108 | "end_date={}/{}/{}, applet_resource_user_id={}", | 67 | "end_date={}/{}/{}, applet_resource_user_id={}", |
| 109 | parameters.content_type, parameters.start_date_time.year, | 68 | content_type, start_date_time.year, start_date_time.month, start_date_time.day, |
| 110 | parameters.start_date_time.month, parameters.start_date_time.day, | 69 | end_date_time.year, end_date_time.month, end_date_time.day, aruid.pid); |
| 111 | parameters.end_date_time.year, parameters.end_date_time.month, | ||
| 112 | parameters.end_date_time.day, parameters.applet_resource_user_id); | ||
| 113 | |||
| 114 | Result result = ResultSuccess; | ||
| 115 | |||
| 116 | if (result.IsSuccess()) { | ||
| 117 | result = manager->IsAlbumMounted(AlbumStorage::Sd); | ||
| 118 | } | ||
| 119 | |||
| 120 | std::vector<ApplicationAlbumEntry> entries; | ||
| 121 | if (result.IsSuccess()) { | ||
| 122 | result = | ||
| 123 | manager->GetAlbumFileList(entries, parameters.content_type, parameters.start_date_time, | ||
| 124 | parameters.end_date_time, parameters.applet_resource_user_id); | ||
| 125 | } | ||
| 126 | |||
| 127 | if (!entries.empty()) { | ||
| 128 | ctx.WriteBuffer(entries); | ||
| 129 | } | ||
| 130 | 70 | ||
| 131 | IPC::ResponseBuilder rb{ctx, 4}; | 71 | R_TRY(manager->IsAlbumMounted(AlbumStorage::Sd)); |
| 132 | rb.Push(result); | 72 | R_RETURN(manager->GetAlbumFileList(out_entries, *out_entries_count, content_type, |
| 133 | rb.Push<u64>(entries.size()); | 73 | start_date_time, end_date_time, aruid.pid)); |
| 134 | } | 74 | } |
| 135 | 75 | ||
| 136 | } // namespace Service::Capture | 76 | } // 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 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 6 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 7 | 8 | ||
| 8 | namespace Core { | 9 | namespace Core { |
| @@ -19,9 +20,18 @@ public: | |||
| 19 | ~IAlbumApplicationService() override; | 20 | ~IAlbumApplicationService() override; |
| 20 | 21 | ||
| 21 | private: | 22 | private: |
| 22 | void SetShimLibraryVersion(HLERequestContext& ctx); | 23 | Result SetShimLibraryVersion(ShimLibraryVersion library_version, |
| 23 | void GetAlbumFileList0AafeAruidDeprecated(HLERequestContext& ctx); | 24 | ClientAppletResourceUserId aruid); |
| 24 | void GetAlbumFileList3AaeAruid(HLERequestContext& ctx); | 25 | |
| 26 | Result GetAlbumFileList0AafeAruidDeprecated( | ||
| 27 | Out<u64> out_entries_count, ContentType content_type, s64 start_posix_time, | ||
| 28 | s64 end_posix_time, ClientAppletResourceUserId aruid, | ||
| 29 | OutArray<ApplicationAlbumFileEntry, BufferAttr_HipcMapAlias> out_entries); | ||
| 30 | |||
| 31 | Result GetAlbumFileList3AaeAruid( | ||
| 32 | Out<u64> out_entries_count, ContentType content_type, AlbumFileDateTime start_date_time, | ||
| 33 | AlbumFileDateTime end_date_time, ClientAppletResourceUserId aruid, | ||
| 34 | OutArray<ApplicationAlbumEntry, BufferAttr_HipcMapAlias> out_entries); | ||
| 25 | 35 | ||
| 26 | std::shared_ptr<AlbumManager> manager = nullptr; | 36 | std::shared_ptr<AlbumManager> manager = nullptr; |
| 27 | }; | 37 | }; |
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 { | |||
| 115 | static constexpr ArgumentType Type = ArgumentType::InData; | 115 | static constexpr ArgumentType Type = ArgumentType::InData; |
| 116 | }; | 116 | }; |
| 117 | 117 | ||
| 118 | template <typename... Ts> | ||
| 119 | consteval bool ConstIfReference() { | ||
| 120 | return ((!std::is_reference_v<Ts> || std::is_const_v<std::remove_reference_t<Ts>>) && ... && true); | ||
| 121 | } | ||
| 122 | |||
| 118 | struct RequestLayout { | 123 | struct RequestLayout { |
| 119 | u32 copy_handle_count; | 124 | u32 copy_handle_count; |
| 120 | u32 move_handle_count; | 125 | u32 move_handle_count; |
| @@ -435,6 +440,7 @@ void CmifReplyWrapImpl(HLERequestContext& ctx, T& t, Result (T::*f)(A...)) { | |||
| 435 | } | 440 | } |
| 436 | const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false; | 441 | const bool is_domain = Domain ? ctx.GetManager()->IsDomain() : false; |
| 437 | 442 | ||
| 443 | static_assert(ConstIfReference<A...>(), "Arguments taken by reference must be const"); | ||
| 438 | using MethodArguments = std::tuple<std::remove_cvref_t<A>...>; | 444 | using MethodArguments = std::tuple<std::remove_cvref_t<A>...>; |
| 439 | 445 | ||
| 440 | OutTemporaryBuffers buffers{}; | 446 | 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 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <span> | ||
| 7 | 8 | ||
| 8 | #include "common/common_funcs.h" | ||
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/hle/service/hle_ipc.h" | ||
| 11 | 10 | ||
| 12 | namespace Service { | 11 | namespace Service { |
| 13 | 12 | ||
| @@ -22,8 +21,10 @@ class Out { | |||
| 22 | public: | 21 | public: |
| 23 | using Type = T; | 22 | using Type = T; |
| 24 | 23 | ||
| 24 | /* implicit */ Out(const Out& t) : raw(t.raw) {} | ||
| 25 | /* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {} | 25 | /* implicit */ Out(AutoOut<Type>& t) : raw(&t.raw) {} |
| 26 | /* implicit */ Out(Type* t) : raw(t) {} | 26 | /* implicit */ Out(Type* t) : raw(t) {} |
| 27 | Out& operator=(const Out&) = delete; | ||
| 27 | 28 | ||
| 28 | Type* Get() const { | 29 | Type* Get() const { |
| 29 | return raw; | 30 | return raw; |
| @@ -37,6 +38,10 @@ public: | |||
| 37 | return raw; | 38 | return raw; |
| 38 | } | 39 | } |
| 39 | 40 | ||
| 41 | operator Type*() const { | ||
| 42 | return raw; | ||
| 43 | } | ||
| 44 | |||
| 40 | private: | 45 | private: |
| 41 | Type* raw; | 46 | Type* raw; |
| 42 | }; | 47 | }; |
| @@ -113,8 +118,10 @@ class OutCopyHandle { | |||
| 113 | public: | 118 | public: |
| 114 | using Type = T*; | 119 | using Type = T*; |
| 115 | 120 | ||
| 121 | /* implicit */ OutCopyHandle(const OutCopyHandle& t) : raw(t.raw) {} | ||
| 116 | /* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {} | 122 | /* implicit */ OutCopyHandle(AutoOut<Type>& t) : raw(&t.raw) {} |
| 117 | /* implicit */ OutCopyHandle(Type* t) : raw(t) {} | 123 | /* implicit */ OutCopyHandle(Type* t) : raw(t) {} |
| 124 | OutCopyHandle& operator=(const OutCopyHandle&) = delete; | ||
| 118 | 125 | ||
| 119 | Type* Get() const { | 126 | Type* Get() const { |
| 120 | return raw; | 127 | return raw; |
| @@ -128,6 +135,10 @@ public: | |||
| 128 | return raw; | 135 | return raw; |
| 129 | } | 136 | } |
| 130 | 137 | ||
| 138 | operator Type*() const { | ||
| 139 | return raw; | ||
| 140 | } | ||
| 141 | |||
| 131 | private: | 142 | private: |
| 132 | Type* raw; | 143 | Type* raw; |
| 133 | }; | 144 | }; |
| @@ -137,8 +148,10 @@ class OutMoveHandle { | |||
| 137 | public: | 148 | public: |
| 138 | using Type = T*; | 149 | using Type = T*; |
| 139 | 150 | ||
| 151 | /* implicit */ OutMoveHandle(const OutMoveHandle& t) : raw(t.raw) {} | ||
| 140 | /* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {} | 152 | /* implicit */ OutMoveHandle(AutoOut<Type>& t) : raw(&t.raw) {} |
| 141 | /* implicit */ OutMoveHandle(Type* t) : raw(t) {} | 153 | /* implicit */ OutMoveHandle(Type* t) : raw(t) {} |
| 154 | OutMoveHandle& operator=(const OutMoveHandle&) = delete; | ||
| 142 | 155 | ||
| 143 | Type* Get() const { | 156 | Type* Get() const { |
| 144 | return raw; | 157 | return raw; |
| @@ -152,6 +165,10 @@ public: | |||
| 152 | return raw; | 165 | return raw; |
| 153 | } | 166 | } |
| 154 | 167 | ||
| 168 | operator Type*() const { | ||
| 169 | return raw; | ||
| 170 | } | ||
| 171 | |||
| 155 | private: | 172 | private: |
| 156 | Type* raw; | 173 | Type* raw; |
| 157 | }; | 174 | }; |
| @@ -248,8 +265,10 @@ public: | |||
| 248 | static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize); | 265 | static constexpr BufferAttr Attr = static_cast<BufferAttr>(A | BufferAttr_In | BufferAttr_FixedSize); |
| 249 | using Type = T; | 266 | using Type = T; |
| 250 | 267 | ||
| 268 | /* implicit */ OutLargeData(const OutLargeData& t) : raw(t.raw) {} | ||
| 251 | /* implicit */ OutLargeData(Type* t) : raw(t) {} | 269 | /* implicit */ OutLargeData(Type* t) : raw(t) {} |
| 252 | /* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {} | 270 | /* implicit */ OutLargeData(AutoOut<T>& t) : raw(&t.raw) {} |
| 271 | OutLargeData& operator=(const OutLargeData&) = delete; | ||
| 253 | 272 | ||
| 254 | Type* Get() const { | 273 | Type* Get() const { |
| 255 | return raw; | 274 | return raw; |
| @@ -263,6 +282,10 @@ public: | |||
| 263 | return raw; | 282 | return raw; |
| 264 | } | 283 | } |
| 265 | 284 | ||
| 285 | operator Type*() const { | ||
| 286 | return raw; | ||
| 287 | } | ||
| 288 | |||
| 266 | private: | 289 | private: |
| 267 | Type* raw; | 290 | Type* raw; |
| 268 | }; | 291 | }; |
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: | |||
| 115 | if (type->GetName() == "save") { | 115 | if (type->GetName() == "save") { |
| 116 | for (const auto& save_id : type->GetSubdirectories()) { | 116 | for (const auto& save_id : type->GetSubdirectories()) { |
| 117 | for (const auto& user_id : save_id->GetSubdirectories()) { | 117 | for (const auto& user_id : save_id->GetSubdirectories()) { |
| 118 | // Skip non user id subdirectories | ||
| 119 | if (user_id->GetName().size() != 0x20) { | ||
| 120 | continue; | ||
| 121 | } | ||
| 122 | |||
| 118 | const auto save_id_numeric = stoull_be(save_id->GetName()); | 123 | const auto save_id_numeric = stoull_be(save_id->GetName()); |
| 119 | auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); | 124 | auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName()); |
| 120 | std::reverse(user_id_numeric.begin(), user_id_numeric.end()); | 125 | std::reverse(user_id_numeric.begin(), user_id_numeric.end()); |
| @@ -160,6 +165,10 @@ private: | |||
| 160 | } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { | 165 | } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) { |
| 161 | // Temporary Storage | 166 | // Temporary Storage |
| 162 | for (const auto& user_id : type->GetSubdirectories()) { | 167 | for (const auto& user_id : type->GetSubdirectories()) { |
| 168 | // Skip non user id subdirectories | ||
| 169 | if (user_id->GetName().size() != 0x20) { | ||
| 170 | continue; | ||
| 171 | } | ||
| 163 | for (const auto& title_id : user_id->GetSubdirectories()) { | 172 | for (const auto& title_id : user_id->GetSubdirectories()) { |
| 164 | if (!title_id->GetFiles().empty() || | 173 | if (!title_id->GetFiles().empty() || |
| 165 | !title_id->GetSubdirectories().empty()) { | 174 | !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) { | |||
| 31 | // Error Context | 31 | // Error Context |
| 32 | server_manager->RegisterNamedService("ectx:aw", std::make_shared<ECTX_AW>(system)); | 32 | server_manager->RegisterNamedService("ectx:aw", std::make_shared<ECTX_AW>(system)); |
| 33 | 33 | ||
| 34 | // Notification Services for application | 34 | // Notification Services |
| 35 | server_manager->RegisterNamedService("notif:a", std::make_shared<NOTIF_A>(system)); | 35 | server_manager->RegisterNamedService( |
| 36 | "notif:a", std::make_shared<INotificationServicesForApplication>(system)); | ||
| 37 | server_manager->RegisterNamedService("notif:s", | ||
| 38 | std::make_shared<INotificationServices>(system)); | ||
| 36 | 39 | ||
| 37 | // Time | 40 | // Time |
| 38 | auto time = std::make_shared<Time::TimeManager>(system); | 41 | 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 @@ | |||
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/hle/service/cmif_serialization.h" | ||
| 9 | #include "core/hle/service/glue/notif.h" | 10 | #include "core/hle/service/glue/notif.h" |
| 10 | #include "core/hle/service/ipc_helpers.h" | 11 | #include "core/hle/service/ipc_helpers.h" |
| 12 | #include "core/hle/service/kernel_helpers.h" | ||
| 11 | 13 | ||
| 12 | namespace Service::Glue { | 14 | namespace Service::Glue { |
| 13 | 15 | ||
| 14 | NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} { | 16 | namespace { |
| 15 | // clang-format off | ||
| 16 | static const FunctionInfo functions[] = { | ||
| 17 | {500, &NOTIF_A::RegisterAlarmSetting, "RegisterAlarmSetting"}, | ||
| 18 | {510, &NOTIF_A::UpdateAlarmSetting, "UpdateAlarmSetting"}, | ||
| 19 | {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"}, | ||
| 20 | {530, &NOTIF_A::LoadApplicationParameter, "LoadApplicationParameter"}, | ||
| 21 | {540, &NOTIF_A::DeleteAlarmSetting, "DeleteAlarmSetting"}, | ||
| 22 | {1000, &NOTIF_A::Initialize, "Initialize"}, | ||
| 23 | }; | ||
| 24 | // clang-format on | ||
| 25 | |||
| 26 | RegisterHandlers(functions); | ||
| 27 | } | ||
| 28 | |||
| 29 | NOTIF_A::~NOTIF_A() = default; | ||
| 30 | 17 | ||
| 31 | void NOTIF_A::RegisterAlarmSetting(HLERequestContext& ctx) { | 18 | constexpr inline std::size_t MaxAlarms = 8; |
| 32 | const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); | ||
| 33 | const auto application_parameter_size = ctx.GetReadBufferSize(1); | ||
| 34 | |||
| 35 | ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), | ||
| 36 | "alarm_setting_buffer_size is not 0x40 bytes"); | ||
| 37 | ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), | ||
| 38 | "application_parameter_size is bigger than 0x400 bytes"); | ||
| 39 | 19 | ||
| 40 | AlarmSetting new_alarm{}; | 20 | } |
| 41 | memcpy(&new_alarm, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); | ||
| 42 | 21 | ||
| 43 | // TODO: Count alarms per game id | 22 | Result NotificationServiceImpl::RegisterAlarmSetting(AlarmSettingId* out_alarm_setting_id, |
| 44 | if (alarms.size() >= max_alarms) { | 23 | const AlarmSetting& alarm_setting, |
| 24 | std::span<const u8> application_parameter) { | ||
| 25 | if (alarms.size() > MaxAlarms) { | ||
| 45 | LOG_ERROR(Service_NOTIF, "Alarm limit reached"); | 26 | LOG_ERROR(Service_NOTIF, "Alarm limit reached"); |
| 46 | IPC::ResponseBuilder rb{ctx, 2}; | 27 | R_THROW(ResultUnknown); |
| 47 | rb.Push(ResultUnknown); | ||
| 48 | return; | ||
| 49 | } | 28 | } |
| 50 | 29 | ||
| 30 | ASSERT_MSG(application_parameter.size() <= sizeof(ApplicationParameter), | ||
| 31 | "application_parameter_size is bigger than 0x400 bytes"); | ||
| 32 | |||
| 33 | AlarmSetting new_alarm = alarm_setting; | ||
| 51 | new_alarm.alarm_setting_id = last_alarm_setting_id++; | 34 | new_alarm.alarm_setting_id = last_alarm_setting_id++; |
| 52 | alarms.push_back(new_alarm); | 35 | alarms.push_back(new_alarm); |
| 53 | 36 | ||
| @@ -55,100 +38,82 @@ void NOTIF_A::RegisterAlarmSetting(HLERequestContext& ctx) { | |||
| 55 | 38 | ||
| 56 | LOG_WARNING(Service_NOTIF, | 39 | LOG_WARNING(Service_NOTIF, |
| 57 | "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", | 40 | "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", |
| 58 | application_parameter_size, new_alarm.alarm_setting_id, new_alarm.kind, | 41 | application_parameter.size(), new_alarm.alarm_setting_id, new_alarm.kind, |
| 59 | new_alarm.muted); | 42 | new_alarm.muted); |
| 60 | 43 | ||
| 61 | IPC::ResponseBuilder rb{ctx, 2}; | 44 | *out_alarm_setting_id = new_alarm.alarm_setting_id; |
| 62 | rb.Push(ResultSuccess); | 45 | R_SUCCEED(); |
| 63 | rb.Push(new_alarm.alarm_setting_id); | ||
| 64 | } | 46 | } |
| 65 | 47 | ||
| 66 | void NOTIF_A::UpdateAlarmSetting(HLERequestContext& ctx) { | 48 | Result NotificationServiceImpl::UpdateAlarmSetting(const AlarmSetting& alarm_setting, |
| 67 | const auto alarm_setting_buffer_size = ctx.GetReadBufferSize(0); | 49 | std::span<const u8> application_parameter) { |
| 68 | const auto application_parameter_size = ctx.GetReadBufferSize(1); | 50 | ASSERT_MSG(application_parameter.size() <= sizeof(ApplicationParameter), |
| 69 | |||
| 70 | ASSERT_MSG(alarm_setting_buffer_size == sizeof(AlarmSetting), | ||
| 71 | "alarm_setting_buffer_size is not 0x40 bytes"); | ||
| 72 | ASSERT_MSG(application_parameter_size <= sizeof(ApplicationParameter), | ||
| 73 | "application_parameter_size is bigger than 0x400 bytes"); | 51 | "application_parameter_size is bigger than 0x400 bytes"); |
| 74 | 52 | ||
| 75 | AlarmSetting alarm_setting{}; | ||
| 76 | memcpy(&alarm_setting, ctx.ReadBuffer(0).data(), sizeof(AlarmSetting)); | ||
| 77 | |||
| 78 | const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id); | 53 | const auto alarm_it = GetAlarmFromId(alarm_setting.alarm_setting_id); |
| 79 | if (alarm_it != alarms.end()) { | 54 | if (alarm_it != alarms.end()) { |
| 80 | LOG_DEBUG(Service_NOTIF, "Alarm updated"); | 55 | LOG_DEBUG(Service_NOTIF, "Alarm updated"); |
| 81 | *alarm_it = alarm_setting; | 56 | *alarm_it = alarm_setting; |
| 82 | // TODO: Save application parameter data | ||
| 83 | } | 57 | } |
| 84 | 58 | ||
| 85 | LOG_WARNING(Service_NOTIF, | 59 | LOG_WARNING(Service_NOTIF, |
| 86 | "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", | 60 | "(STUBBED) called, application_parameter_size={}, setting_id={}, kind={}, muted={}", |
| 87 | application_parameter_size, alarm_setting.alarm_setting_id, alarm_setting.kind, | 61 | application_parameter.size(), alarm_setting.alarm_setting_id, alarm_setting.kind, |
| 88 | alarm_setting.muted); | 62 | alarm_setting.muted); |
| 89 | 63 | R_SUCCEED(); | |
| 90 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 91 | rb.Push(ResultSuccess); | ||
| 92 | } | 64 | } |
| 93 | 65 | ||
| 94 | void NOTIF_A::ListAlarmSettings(HLERequestContext& ctx) { | 66 | Result NotificationServiceImpl::ListAlarmSettings(s32* out_count, |
| 67 | std::span<AlarmSetting> out_alarms) { | ||
| 95 | LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size()); | 68 | LOG_INFO(Service_NOTIF, "called, alarm_count={}", alarms.size()); |
| 96 | 69 | ||
| 97 | // TODO: Only return alarms of this game id | 70 | const auto count = std::min(out_alarms.size(), alarms.size()); |
| 98 | ctx.WriteBuffer(alarms); | 71 | for (size_t i = 0; i < count; i++) { |
| 72 | out_alarms[i] = alarms[i]; | ||
| 73 | } | ||
| 99 | 74 | ||
| 100 | IPC::ResponseBuilder rb{ctx, 3}; | 75 | *out_count = static_cast<s32>(count); |
| 101 | rb.Push(ResultSuccess); | 76 | R_SUCCEED(); |
| 102 | rb.Push(static_cast<u32>(alarms.size())); | ||
| 103 | } | 77 | } |
| 104 | 78 | ||
| 105 | void NOTIF_A::LoadApplicationParameter(HLERequestContext& ctx) { | 79 | Result NotificationServiceImpl::LoadApplicationParameter(u32* out_size, |
| 106 | IPC::RequestParser rp{ctx}; | 80 | std::span<u8> out_application_parameter, |
| 107 | const auto alarm_setting_id{rp.Pop<AlarmSettingId>()}; | 81 | AlarmSettingId alarm_setting_id) { |
| 108 | |||
| 109 | const auto alarm_it = GetAlarmFromId(alarm_setting_id); | 82 | const auto alarm_it = GetAlarmFromId(alarm_setting_id); |
| 110 | if (alarm_it == alarms.end()) { | 83 | if (alarm_it == alarms.end()) { |
| 111 | LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id); | 84 | LOG_ERROR(Service_NOTIF, "Invalid alarm setting id={}", alarm_setting_id); |
| 112 | IPC::ResponseBuilder rb{ctx, 2}; | 85 | R_THROW(ResultUnknown); |
| 113 | rb.Push(ResultUnknown); | ||
| 114 | return; | ||
| 115 | } | 86 | } |
| 116 | 87 | ||
| 117 | // TODO: Read application parameter related to this setting id | 88 | // TODO: Read application parameter related to this setting id |
| 118 | ApplicationParameter application_parameter{}; | 89 | ApplicationParameter application_parameter{}; |
| 119 | 90 | ||
| 120 | LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id); | 91 | LOG_WARNING(Service_NOTIF, "(STUBBED) called, alarm_setting_id={}", alarm_setting_id); |
| 92 | std::memcpy(out_application_parameter.data(), application_parameter.data(), | ||
| 93 | std::min(sizeof(application_parameter), out_application_parameter.size())); | ||
| 121 | 94 | ||
| 122 | ctx.WriteBuffer(application_parameter); | 95 | *out_size = static_cast<u32>(application_parameter.size()); |
| 123 | 96 | R_SUCCEED(); | |
| 124 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 125 | rb.Push(ResultSuccess); | ||
| 126 | rb.Push(static_cast<u32>(application_parameter.size())); | ||
| 127 | } | 97 | } |
| 128 | 98 | ||
| 129 | void NOTIF_A::DeleteAlarmSetting(HLERequestContext& ctx) { | 99 | Result NotificationServiceImpl::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) { |
| 130 | IPC::RequestParser rp{ctx}; | ||
| 131 | const auto alarm_setting_id{rp.Pop<AlarmSettingId>()}; | ||
| 132 | |||
| 133 | std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) { | 100 | std::erase_if(alarms, [alarm_setting_id](const AlarmSetting& alarm) { |
| 134 | return alarm.alarm_setting_id == alarm_setting_id; | 101 | return alarm.alarm_setting_id == alarm_setting_id; |
| 135 | }); | 102 | }); |
| 136 | 103 | ||
| 137 | LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id); | 104 | LOG_INFO(Service_NOTIF, "called, alarm_setting_id={}", alarm_setting_id); |
| 138 | 105 | ||
| 139 | IPC::ResponseBuilder rb{ctx, 2}; | 106 | R_SUCCEED(); |
| 140 | rb.Push(ResultSuccess); | ||
| 141 | } | 107 | } |
| 142 | 108 | ||
| 143 | void NOTIF_A::Initialize(HLERequestContext& ctx) { | 109 | Result NotificationServiceImpl::Initialize(u64 aruid) { |
| 144 | // TODO: Load previous alarms from config | 110 | // TODO: Load previous alarms from config |
| 145 | 111 | ||
| 146 | LOG_WARNING(Service_NOTIF, "(STUBBED) called"); | 112 | LOG_WARNING(Service_NOTIF, "(STUBBED) called"); |
| 147 | IPC::ResponseBuilder rb{ctx, 2}; | 113 | R_SUCCEED(); |
| 148 | rb.Push(ResultSuccess); | ||
| 149 | } | 114 | } |
| 150 | 115 | ||
| 151 | std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId( | 116 | std::vector<AlarmSetting>::iterator NotificationServiceImpl::GetAlarmFromId( |
| 152 | AlarmSettingId alarm_setting_id) { | 117 | AlarmSettingId alarm_setting_id) { |
| 153 | return std::find_if(alarms.begin(), alarms.end(), | 118 | return std::find_if(alarms.begin(), alarms.end(), |
| 154 | [alarm_setting_id](const AlarmSetting& alarm) { | 119 | [alarm_setting_id](const AlarmSetting& alarm) { |
| @@ -156,4 +121,174 @@ std::vector<NOTIF_A::AlarmSetting>::iterator NOTIF_A::GetAlarmFromId( | |||
| 156 | }); | 121 | }); |
| 157 | } | 122 | } |
| 158 | 123 | ||
| 124 | INotificationServicesForApplication::INotificationServicesForApplication(Core::System& system_) | ||
| 125 | : ServiceFramework{system_, "notif:a"} { | ||
| 126 | // clang-format off | ||
| 127 | static const FunctionInfo functions[] = { | ||
| 128 | {500, D<&INotificationServicesForApplication::RegisterAlarmSetting>, "RegisterAlarmSetting"}, | ||
| 129 | {510, D<&INotificationServicesForApplication::UpdateAlarmSetting>, "UpdateAlarmSetting"}, | ||
| 130 | {520, D<&INotificationServicesForApplication::ListAlarmSettings>, "ListAlarmSettings"}, | ||
| 131 | {530, D<&INotificationServicesForApplication::LoadApplicationParameter>, "LoadApplicationParameter"}, | ||
| 132 | {540, D<&INotificationServicesForApplication::DeleteAlarmSetting>, "DeleteAlarmSetting"}, | ||
| 133 | {1000, D<&INotificationServicesForApplication::Initialize>, "Initialize"}, | ||
| 134 | }; | ||
| 135 | // clang-format on | ||
| 136 | |||
| 137 | RegisterHandlers(functions); | ||
| 138 | } | ||
| 139 | |||
| 140 | INotificationServicesForApplication::~INotificationServicesForApplication() = default; | ||
| 141 | |||
| 142 | Result INotificationServicesForApplication::RegisterAlarmSetting( | ||
| 143 | Out<AlarmSettingId> out_alarm_setting_id, | ||
| 144 | InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, | ||
| 145 | InBuffer<BufferAttr_HipcMapAlias> application_parameter) { | ||
| 146 | R_RETURN(impl.RegisterAlarmSetting(out_alarm_setting_id.Get(), *alarm_setting, | ||
| 147 | application_parameter)); | ||
| 148 | } | ||
| 149 | |||
| 150 | Result INotificationServicesForApplication::UpdateAlarmSetting( | ||
| 151 | InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, | ||
| 152 | InBuffer<BufferAttr_HipcMapAlias> application_parameter) { | ||
| 153 | R_RETURN(impl.UpdateAlarmSetting(*alarm_setting, application_parameter)); | ||
| 154 | } | ||
| 155 | |||
| 156 | Result INotificationServicesForApplication::ListAlarmSettings( | ||
| 157 | Out<s32> out_count, OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms) { | ||
| 158 | R_RETURN(impl.ListAlarmSettings(out_count.Get(), out_alarms)); | ||
| 159 | } | ||
| 160 | |||
| 161 | Result INotificationServicesForApplication::LoadApplicationParameter( | ||
| 162 | Out<u32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter, | ||
| 163 | AlarmSettingId alarm_setting_id) { | ||
| 164 | R_RETURN( | ||
| 165 | impl.LoadApplicationParameter(out_size.Get(), out_application_parameter, alarm_setting_id)); | ||
| 166 | } | ||
| 167 | |||
| 168 | Result INotificationServicesForApplication::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) { | ||
| 169 | R_RETURN(impl.DeleteAlarmSetting(alarm_setting_id)); | ||
| 170 | } | ||
| 171 | |||
| 172 | Result INotificationServicesForApplication::Initialize(ClientAppletResourceUserId aruid) { | ||
| 173 | R_RETURN(impl.Initialize(*aruid)); | ||
| 174 | } | ||
| 175 | |||
| 176 | class INotificationSystemEventAccessor final | ||
| 177 | : public ServiceFramework<INotificationSystemEventAccessor> { | ||
| 178 | public: | ||
| 179 | explicit INotificationSystemEventAccessor(Core::System& system_) | ||
| 180 | : ServiceFramework{system_, "INotificationSystemEventAccessor"}, | ||
| 181 | service_context{system_, "INotificationSystemEventAccessor"} { | ||
| 182 | // clang-format off | ||
| 183 | static const FunctionInfo functions[] = { | ||
| 184 | {0, D<&INotificationSystemEventAccessor::GetSystemEvent>, "GetSystemEvent"}, | ||
| 185 | }; | ||
| 186 | // clang-format on | ||
| 187 | |||
| 188 | RegisterHandlers(functions); | ||
| 189 | |||
| 190 | notification_event = | ||
| 191 | service_context.CreateEvent("INotificationSystemEventAccessor:NotificationEvent"); | ||
| 192 | } | ||
| 193 | |||
| 194 | ~INotificationSystemEventAccessor() { | ||
| 195 | service_context.CloseEvent(notification_event); | ||
| 196 | } | ||
| 197 | |||
| 198 | private: | ||
| 199 | Result GetSystemEvent(OutCopyHandle<Kernel::KReadableEvent> out_readable_event) { | ||
| 200 | LOG_WARNING(Service_NOTIF, "(STUBBED) called"); | ||
| 201 | |||
| 202 | *out_readable_event = ¬ification_event->GetReadableEvent(); | ||
| 203 | R_SUCCEED(); | ||
| 204 | } | ||
| 205 | |||
| 206 | KernelHelpers::ServiceContext service_context; | ||
| 207 | Kernel::KEvent* notification_event; | ||
| 208 | }; | ||
| 209 | |||
| 210 | INotificationServices::INotificationServices(Core::System& system_) | ||
| 211 | : ServiceFramework{system_, "notif:s"} { | ||
| 212 | // clang-format off | ||
| 213 | static const FunctionInfo functions[] = { | ||
| 214 | {500, D<&INotificationServices::RegisterAlarmSetting>, "RegisterAlarmSetting"}, | ||
| 215 | {510, D<&INotificationServices::UpdateAlarmSetting>, "UpdateAlarmSetting"}, | ||
| 216 | {520, D<&INotificationServices::ListAlarmSettings>, "ListAlarmSettings"}, | ||
| 217 | {530, D<&INotificationServices::LoadApplicationParameter>, "LoadApplicationParameter"}, | ||
| 218 | {540, D<&INotificationServices::DeleteAlarmSetting>, "DeleteAlarmSetting"}, | ||
| 219 | {1000, D<&INotificationServices::Initialize>, "Initialize"}, | ||
| 220 | {1010, nullptr, "ListNotifications"}, | ||
| 221 | {1020, nullptr, "DeleteNotification"}, | ||
| 222 | {1030, nullptr, "ClearNotifications"}, | ||
| 223 | {1040, D<&INotificationServices::OpenNotificationSystemEventAccessor>, "OpenNotificationSystemEventAccessor"}, | ||
| 224 | {1500, nullptr, "SetNotificationPresentationSetting"}, | ||
| 225 | {1510, D<&INotificationServices::GetNotificationPresentationSetting>, "GetNotificationPresentationSetting"}, | ||
| 226 | {2000, nullptr, "GetAlarmSetting"}, | ||
| 227 | {2001, nullptr, "GetAlarmSettingWithApplicationParameter"}, | ||
| 228 | {2010, nullptr, "MuteAlarmSetting"}, | ||
| 229 | {2020, nullptr, "IsAlarmSettingReady"}, | ||
| 230 | {8000, nullptr, "RegisterAppletResourceUserId"}, | ||
| 231 | {8010, nullptr, "UnregisterAppletResourceUserId"}, | ||
| 232 | {8999, nullptr, "GetCurrentTime"}, | ||
| 233 | {9000, nullptr, "GetAlarmSettingNextNotificationTime"}, | ||
| 234 | }; | ||
| 235 | // clang-format on | ||
| 236 | |||
| 237 | RegisterHandlers(functions); | ||
| 238 | } | ||
| 239 | |||
| 240 | INotificationServices::~INotificationServices() = default; | ||
| 241 | |||
| 242 | Result INotificationServices::RegisterAlarmSetting( | ||
| 243 | Out<AlarmSettingId> out_alarm_setting_id, | ||
| 244 | InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, | ||
| 245 | InBuffer<BufferAttr_HipcMapAlias> application_parameter) { | ||
| 246 | R_RETURN(impl.RegisterAlarmSetting(out_alarm_setting_id.Get(), *alarm_setting, | ||
| 247 | application_parameter)); | ||
| 248 | } | ||
| 249 | |||
| 250 | Result INotificationServices::UpdateAlarmSetting( | ||
| 251 | InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, | ||
| 252 | InBuffer<BufferAttr_HipcMapAlias> application_parameter) { | ||
| 253 | R_RETURN(impl.UpdateAlarmSetting(*alarm_setting, application_parameter)); | ||
| 254 | } | ||
| 255 | |||
| 256 | Result INotificationServices::ListAlarmSettings( | ||
| 257 | Out<s32> out_count, OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms) { | ||
| 258 | R_RETURN(impl.ListAlarmSettings(out_count.Get(), out_alarms)); | ||
| 259 | } | ||
| 260 | |||
| 261 | Result INotificationServices::LoadApplicationParameter( | ||
| 262 | Out<u32> out_size, OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter, | ||
| 263 | AlarmSettingId alarm_setting_id) { | ||
| 264 | R_RETURN( | ||
| 265 | impl.LoadApplicationParameter(out_size.Get(), out_application_parameter, alarm_setting_id)); | ||
| 266 | } | ||
| 267 | |||
| 268 | Result INotificationServices::DeleteAlarmSetting(AlarmSettingId alarm_setting_id) { | ||
| 269 | R_RETURN(impl.DeleteAlarmSetting(alarm_setting_id)); | ||
| 270 | } | ||
| 271 | |||
| 272 | Result INotificationServices::Initialize(ClientAppletResourceUserId aruid) { | ||
| 273 | R_RETURN(impl.Initialize(*aruid)); | ||
| 274 | } | ||
| 275 | |||
| 276 | Result INotificationServices::OpenNotificationSystemEventAccessor( | ||
| 277 | Out<SharedPointer<INotificationSystemEventAccessor>> out_notification_system_event_accessor) { | ||
| 278 | LOG_WARNING(Service_NOTIF, "(STUBBED) called"); | ||
| 279 | |||
| 280 | *out_notification_system_event_accessor = | ||
| 281 | std::make_shared<INotificationSystemEventAccessor>(system); | ||
| 282 | R_SUCCEED(); | ||
| 283 | } | ||
| 284 | |||
| 285 | Result INotificationServices::GetNotificationPresentationSetting( | ||
| 286 | Out<NotificationPresentationSetting> out_notification_presentation_setting, | ||
| 287 | NotificationChannel notification_channel) { | ||
| 288 | LOG_WARNING(Service_NOTIF, "(STUBBED) called"); | ||
| 289 | |||
| 290 | *out_notification_presentation_setting = {}; | ||
| 291 | R_SUCCEED(); | ||
| 292 | } | ||
| 293 | |||
| 159 | } // namespace Service::Glue | 294 | } // 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 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "common/uuid.h" | 9 | #include "common/uuid.h" |
| 10 | #include "core/hle/service/cmif_types.h" | ||
| 10 | #include "core/hle/service/service.h" | 11 | #include "core/hle/service/service.h" |
| 11 | 12 | ||
| 12 | namespace Core { | 13 | namespace Core { |
| @@ -15,58 +16,117 @@ class System; | |||
| 15 | 16 | ||
| 16 | namespace Service::Glue { | 17 | namespace Service::Glue { |
| 17 | 18 | ||
| 18 | class NOTIF_A final : public ServiceFramework<NOTIF_A> { | 19 | // This is nn::notification::AlarmSettingId |
| 20 | using AlarmSettingId = u16; | ||
| 21 | static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size"); | ||
| 22 | |||
| 23 | using ApplicationParameter = std::array<u8, 0x400>; | ||
| 24 | static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size"); | ||
| 25 | |||
| 26 | struct DailyAlarmSetting { | ||
| 27 | s8 hour; | ||
| 28 | s8 minute; | ||
| 29 | }; | ||
| 30 | static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size"); | ||
| 31 | |||
| 32 | struct WeeklyScheduleAlarmSetting { | ||
| 33 | INSERT_PADDING_BYTES_NOINIT(0xA); | ||
| 34 | std::array<DailyAlarmSetting, 0x7> day_of_week; | ||
| 35 | }; | ||
| 36 | static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18, | ||
| 37 | "WeeklyScheduleAlarmSetting is an invalid size"); | ||
| 38 | |||
| 39 | // This is nn::notification::AlarmSetting | ||
| 40 | struct AlarmSetting { | ||
| 41 | AlarmSettingId alarm_setting_id; | ||
| 42 | u8 kind; | ||
| 43 | u8 muted; | ||
| 44 | INSERT_PADDING_BYTES_NOINIT(0x4); | ||
| 45 | Common::UUID account_id; | ||
| 46 | u64 application_id; | ||
| 47 | INSERT_PADDING_BYTES_NOINIT(0x8); | ||
| 48 | WeeklyScheduleAlarmSetting schedule; | ||
| 49 | }; | ||
| 50 | static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size"); | ||
| 51 | |||
| 52 | enum class NotificationChannel : u8 { | ||
| 53 | Unknown0 = 0, | ||
| 54 | }; | ||
| 55 | |||
| 56 | struct NotificationPresentationSetting { | ||
| 57 | INSERT_PADDING_BYTES_NOINIT(0x10); | ||
| 58 | }; | ||
| 59 | static_assert(sizeof(NotificationPresentationSetting) == 0x10, | ||
| 60 | "NotificationPresentationSetting is an invalid size"); | ||
| 61 | |||
| 62 | class NotificationServiceImpl { | ||
| 19 | public: | 63 | public: |
| 20 | explicit NOTIF_A(Core::System& system_); | 64 | Result RegisterAlarmSetting(AlarmSettingId* out_alarm_setting_id, |
| 21 | ~NOTIF_A() override; | 65 | const AlarmSetting& alarm_setting, |
| 66 | std::span<const u8> application_parameter); | ||
| 67 | Result UpdateAlarmSetting(const AlarmSetting& alarm_setting, | ||
| 68 | std::span<const u8> application_parameter); | ||
| 69 | Result ListAlarmSettings(s32* out_count, std::span<AlarmSetting> out_alarms); | ||
| 70 | Result LoadApplicationParameter(u32* out_size, std::span<u8> out_application_parameter, | ||
| 71 | AlarmSettingId alarm_setting_id); | ||
| 72 | Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id); | ||
| 73 | Result Initialize(u64 aruid); | ||
| 22 | 74 | ||
| 23 | private: | 75 | private: |
| 24 | static constexpr std::size_t max_alarms = 8; | ||
| 25 | |||
| 26 | // This is nn::notification::AlarmSettingId | ||
| 27 | using AlarmSettingId = u16; | ||
| 28 | static_assert(sizeof(AlarmSettingId) == 0x2, "AlarmSettingId is an invalid size"); | ||
| 29 | |||
| 30 | using ApplicationParameter = std::array<u8, 0x400>; | ||
| 31 | static_assert(sizeof(ApplicationParameter) == 0x400, "ApplicationParameter is an invalid size"); | ||
| 32 | |||
| 33 | struct DailyAlarmSetting { | ||
| 34 | s8 hour; | ||
| 35 | s8 minute; | ||
| 36 | }; | ||
| 37 | static_assert(sizeof(DailyAlarmSetting) == 0x2, "DailyAlarmSetting is an invalid size"); | ||
| 38 | |||
| 39 | struct WeeklyScheduleAlarmSetting { | ||
| 40 | INSERT_PADDING_BYTES(0xA); | ||
| 41 | std::array<DailyAlarmSetting, 0x7> day_of_week; | ||
| 42 | }; | ||
| 43 | static_assert(sizeof(WeeklyScheduleAlarmSetting) == 0x18, | ||
| 44 | "WeeklyScheduleAlarmSetting is an invalid size"); | ||
| 45 | |||
| 46 | // This is nn::notification::AlarmSetting | ||
| 47 | struct AlarmSetting { | ||
| 48 | AlarmSettingId alarm_setting_id; | ||
| 49 | u8 kind; | ||
| 50 | u8 muted; | ||
| 51 | INSERT_PADDING_BYTES(0x4); | ||
| 52 | Common::UUID account_id; | ||
| 53 | u64 application_id; | ||
| 54 | INSERT_PADDING_BYTES(0x8); | ||
| 55 | WeeklyScheduleAlarmSetting schedule; | ||
| 56 | }; | ||
| 57 | static_assert(sizeof(AlarmSetting) == 0x40, "AlarmSetting is an invalid size"); | ||
| 58 | |||
| 59 | void RegisterAlarmSetting(HLERequestContext& ctx); | ||
| 60 | void UpdateAlarmSetting(HLERequestContext& ctx); | ||
| 61 | void ListAlarmSettings(HLERequestContext& ctx); | ||
| 62 | void LoadApplicationParameter(HLERequestContext& ctx); | ||
| 63 | void DeleteAlarmSetting(HLERequestContext& ctx); | ||
| 64 | void Initialize(HLERequestContext& ctx); | ||
| 65 | |||
| 66 | std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id); | 76 | std::vector<AlarmSetting>::iterator GetAlarmFromId(AlarmSettingId alarm_setting_id); |
| 67 | |||
| 68 | std::vector<AlarmSetting> alarms{}; | 77 | std::vector<AlarmSetting> alarms{}; |
| 69 | AlarmSettingId last_alarm_setting_id{}; | 78 | AlarmSettingId last_alarm_setting_id{}; |
| 70 | }; | 79 | }; |
| 71 | 80 | ||
| 81 | class INotificationServicesForApplication final | ||
| 82 | : public ServiceFramework<INotificationServicesForApplication> { | ||
| 83 | public: | ||
| 84 | explicit INotificationServicesForApplication(Core::System& system_); | ||
| 85 | ~INotificationServicesForApplication() override; | ||
| 86 | |||
| 87 | private: | ||
| 88 | Result RegisterAlarmSetting(Out<AlarmSettingId> out_alarm_setting_id, | ||
| 89 | InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, | ||
| 90 | InBuffer<BufferAttr_HipcMapAlias> application_parameter); | ||
| 91 | Result UpdateAlarmSetting(InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, | ||
| 92 | InBuffer<BufferAttr_HipcMapAlias> application_parameter); | ||
| 93 | Result ListAlarmSettings(Out<s32> out_count, | ||
| 94 | OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms); | ||
| 95 | Result LoadApplicationParameter(Out<u32> out_size, | ||
| 96 | OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter, | ||
| 97 | AlarmSettingId alarm_setting_id); | ||
| 98 | Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id); | ||
| 99 | Result Initialize(ClientAppletResourceUserId aruid); | ||
| 100 | |||
| 101 | NotificationServiceImpl impl; | ||
| 102 | }; | ||
| 103 | |||
| 104 | class INotificationSystemEventAccessor; | ||
| 105 | |||
| 106 | class INotificationServices final : public ServiceFramework<INotificationServices> { | ||
| 107 | public: | ||
| 108 | explicit INotificationServices(Core::System& system_); | ||
| 109 | ~INotificationServices() override; | ||
| 110 | |||
| 111 | private: | ||
| 112 | Result RegisterAlarmSetting(Out<AlarmSettingId> out_alarm_setting_id, | ||
| 113 | InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, | ||
| 114 | InBuffer<BufferAttr_HipcMapAlias> application_parameter); | ||
| 115 | Result UpdateAlarmSetting(InLargeData<AlarmSetting, BufferAttr_HipcMapAlias> alarm_setting, | ||
| 116 | InBuffer<BufferAttr_HipcMapAlias> application_parameter); | ||
| 117 | Result ListAlarmSettings(Out<s32> out_count, | ||
| 118 | OutArray<AlarmSetting, BufferAttr_HipcMapAlias> out_alarms); | ||
| 119 | Result LoadApplicationParameter(Out<u32> out_size, | ||
| 120 | OutBuffer<BufferAttr_HipcMapAlias> out_application_parameter, | ||
| 121 | AlarmSettingId alarm_setting_id); | ||
| 122 | Result DeleteAlarmSetting(AlarmSettingId alarm_setting_id); | ||
| 123 | Result Initialize(ClientAppletResourceUserId aruid); | ||
| 124 | Result OpenNotificationSystemEventAccessor(Out<SharedPointer<INotificationSystemEventAccessor>> | ||
| 125 | out_notification_system_event_accessor); | ||
| 126 | Result GetNotificationPresentationSetting( | ||
| 127 | Out<NotificationPresentationSetting> out_notification_presentation_setting, | ||
| 128 | NotificationChannel notification_channel); | ||
| 129 | |||
| 130 | NotificationServiceImpl impl; | ||
| 131 | }; | ||
| 72 | } // namespace Service::Glue | 132 | } // 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( | |||
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( | 202 | Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( |
| 203 | Out<s64> out_time, Service::PSC::Time::SystemClockContext& context) { | 203 | Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context) { |
| 204 | SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); | 204 | SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); |
| 205 | 205 | ||
| 206 | R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context)); | 206 | R_RETURN(m_wrapped_service->CalculateMonotonicSystemClockBaseTimePoint(out_time, context)); |
| @@ -216,8 +216,8 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, | |||
| 216 | 216 | ||
| 217 | Result StaticService::GetClockSnapshotFromSystemClockContext( | 217 | Result StaticService::GetClockSnapshotFromSystemClockContext( |
| 218 | Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot, | 218 | Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot, |
| 219 | Service::PSC::Time::SystemClockContext& user_context, | 219 | const Service::PSC::Time::SystemClockContext& user_context, |
| 220 | Service::PSC::Time::SystemClockContext& network_context) { | 220 | const Service::PSC::Time::SystemClockContext& network_context) { |
| 221 | SCOPE_EXIT({ | 221 | SCOPE_EXIT({ |
| 222 | LOG_DEBUG(Service_Time, | 222 | LOG_DEBUG(Service_Time, |
| 223 | "called. type={} out_snapshot={} user_context={} network_context={}", type, | 223 | "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: | |||
| 58 | Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( | 58 | Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( |
| 59 | Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point); | 59 | Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point); |
| 60 | Result CalculateMonotonicSystemClockBaseTimePoint( | 60 | Result CalculateMonotonicSystemClockBaseTimePoint( |
| 61 | Out<s64> out_time, Service::PSC::Time::SystemClockContext& context); | 61 | Out<s64> out_time, const Service::PSC::Time::SystemClockContext& context); |
| 62 | Result GetClockSnapshot(OutClockSnapshot out_snapshot, Service::PSC::Time::TimeType type); | 62 | Result GetClockSnapshot(OutClockSnapshot out_snapshot, Service::PSC::Time::TimeType type); |
| 63 | Result GetClockSnapshotFromSystemClockContext( | 63 | Result GetClockSnapshotFromSystemClockContext( |
| 64 | Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot, | 64 | Service::PSC::Time::TimeType type, OutClockSnapshot out_snapshot, |
| 65 | Service::PSC::Time::SystemClockContext& user_context, | 65 | const Service::PSC::Time::SystemClockContext& user_context, |
| 66 | Service::PSC::Time::SystemClockContext& network_context); | 66 | const Service::PSC::Time::SystemClockContext& network_context); |
| 67 | Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference, | 67 | Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference, |
| 68 | InClockSnapshot a, InClockSnapshot b); | 68 | InClockSnapshot a, InClockSnapshot b); |
| 69 | Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b); | 69 | 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( | |||
| 62 | R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name)); | 62 | R_RETURN(m_wrapped_service->GetDeviceLocationName(out_location_name)); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | Result TimeZoneService::SetDeviceLocationName(Service::PSC::Time::LocationName& location_name) { | 65 | Result TimeZoneService::SetDeviceLocationName( |
| 66 | const Service::PSC::Time::LocationName& location_name) { | ||
| 66 | LOG_DEBUG(Service_Time, "called. location_name={}", location_name); | 67 | LOG_DEBUG(Service_Time, "called. location_name={}", location_name); |
| 67 | 68 | ||
| 68 | R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); | 69 | R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); |
| @@ -110,7 +111,8 @@ Result TimeZoneService::LoadLocationNameList( | |||
| 110 | R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index)); | 111 | R_RETURN(GetTimeZoneLocationList(*out_count, out_names, out_names.size(), index)); |
| 111 | } | 112 | } |
| 112 | 113 | ||
| 113 | Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, Service::PSC::Time::LocationName& name) { | 114 | Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, |
| 115 | const Service::PSC::Time::LocationName& name) { | ||
| 114 | LOG_DEBUG(Service_Time, "called. name={}", name); | 116 | LOG_DEBUG(Service_Time, "called. name={}", name); |
| 115 | 117 | ||
| 116 | std::scoped_lock l{m_mutex}; | 118 | std::scoped_lock l{m_mutex}; |
| @@ -139,7 +141,8 @@ Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( | |||
| 139 | } | 141 | } |
| 140 | 142 | ||
| 141 | Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule( | 143 | Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule( |
| 142 | Service::PSC::Time::LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) { | 144 | const Service::PSC::Time::LocationName& location_name, |
| 145 | InBuffer<BufferAttr_HipcAutoSelect> binary) { | ||
| 143 | LOG_DEBUG(Service_Time, "called. location_name={}", location_name); | 146 | LOG_DEBUG(Service_Time, "called. location_name={}", location_name); |
| 144 | 147 | ||
| 145 | R_UNLESS(m_can_write_timezone_device_location, Service::PSC::Time::ResultPermissionDenied); | 148 | 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: | |||
| 46 | ~TimeZoneService() override; | 46 | ~TimeZoneService() override; |
| 47 | 47 | ||
| 48 | Result GetDeviceLocationName(Out<Service::PSC::Time::LocationName> out_location_name); | 48 | Result GetDeviceLocationName(Out<Service::PSC::Time::LocationName> out_location_name); |
| 49 | Result SetDeviceLocationName(Service::PSC::Time::LocationName& location_name); | 49 | Result SetDeviceLocationName(const Service::PSC::Time::LocationName& location_name); |
| 50 | Result GetTotalLocationNameCount(Out<u32> out_count); | 50 | Result GetTotalLocationNameCount(Out<u32> out_count); |
| 51 | Result LoadLocationNameList( | 51 | Result LoadLocationNameList( |
| 52 | Out<u32> out_count, | 52 | Out<u32> out_count, |
| 53 | OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index); | 53 | OutArray<Service::PSC::Time::LocationName, BufferAttr_HipcMapAlias> out_names, u32 index); |
| 54 | Result LoadTimeZoneRule(OutRule out_rule, Service::PSC::Time::LocationName& location_name); | 54 | Result LoadTimeZoneRule(OutRule out_rule, |
| 55 | const Service::PSC::Time::LocationName& location_name); | ||
| 55 | Result GetTimeZoneRuleVersion(Out<Service::PSC::Time::RuleVersion> out_rule_version); | 56 | Result GetTimeZoneRuleVersion(Out<Service::PSC::Time::RuleVersion> out_rule_version); |
| 56 | Result GetDeviceLocationNameAndUpdatedTime( | 57 | Result GetDeviceLocationNameAndUpdatedTime( |
| 57 | Out<Service::PSC::Time::LocationName> location_name, | 58 | Out<Service::PSC::Time::LocationName> location_name, |
| 58 | Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point); | 59 | Out<Service::PSC::Time::SteadyClockTimePoint> out_time_point); |
| 59 | Result SetDeviceLocationNameWithTimeZoneRule(Service::PSC::Time::LocationName& location_name, | 60 | Result SetDeviceLocationNameWithTimeZoneRule( |
| 60 | InBuffer<BufferAttr_HipcAutoSelect> binary); | 61 | const Service::PSC::Time::LocationName& location_name, |
| 62 | InBuffer<BufferAttr_HipcAutoSelect> binary); | ||
| 61 | Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary); | 63 | Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary); |
| 62 | Result GetDeviceLocationNameOperationEventReadableHandle( | 64 | Result GetDeviceLocationNameOperationEventReadableHandle( |
| 63 | OutCopyHandle<Kernel::KReadableEvent> out_event); | 65 | 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) { | |||
| 65 | // Validate that the romfs is readable, using invalid firmware keys can cause this to get | 65 | // Validate that the romfs is readable, using invalid firmware keys can cause this to get |
| 66 | // set but the files to be garbage. In that case, we want to hit the next path and | 66 | // set but the files to be garbage. In that case, we want to hit the next path and |
| 67 | // synthesise them instead. | 67 | // synthesise them instead. |
| 68 | g_time_zone_binary_mount_result = ResultSuccess; | ||
| 68 | Service::PSC::Time::LocationName name{"Etc/GMT"}; | 69 | Service::PSC::Time::LocationName name{"Etc/GMT"}; |
| 69 | if (!IsTimeZoneBinaryValid(name)) { | 70 | if (!IsTimeZoneBinaryValid(name)) { |
| 70 | ResetTimeZoneBinary(); | 71 | ResetTimeZoneBinary(); |
| @@ -98,7 +99,7 @@ void GetTimeZoneBinaryVersionPath(std::string& out_path) { | |||
| 98 | out_path = "/version.txt"; | 99 | out_path = "/version.txt"; |
| 99 | } | 100 | } |
| 100 | 101 | ||
| 101 | void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name) { | 102 | void GetTimeZoneZonePath(std::string& out_path, const Service::PSC::Time::LocationName& name) { |
| 102 | if (g_time_zone_binary_mount_result != ResultSuccess) { | 103 | if (g_time_zone_binary_mount_result != ResultSuccess) { |
| 103 | return; | 104 | return; |
| 104 | } | 105 | } |
| @@ -106,7 +107,7 @@ void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName | |||
| 106 | out_path = fmt::format("/zoneinfo/{}", name.data()); | 107 | out_path = fmt::format("/zoneinfo/{}", name.data()); |
| 107 | } | 108 | } |
| 108 | 109 | ||
| 109 | bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name) { | 110 | bool IsTimeZoneBinaryValid(const Service::PSC::Time::LocationName& name) { |
| 110 | std::string path{}; | 111 | std::string path{}; |
| 111 | GetTimeZoneZonePath(path, name); | 112 | GetTimeZoneZonePath(path, name); |
| 112 | 113 | ||
| @@ -155,7 +156,7 @@ Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version) { | |||
| 155 | } | 156 | } |
| 156 | 157 | ||
| 157 | Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, | 158 | Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, |
| 158 | Service::PSC::Time::LocationName& name) { | 159 | const Service::PSC::Time::LocationName& name) { |
| 159 | std::string path{}; | 160 | std::string path{}; |
| 160 | GetTimeZoneZonePath(path, name); | 161 | GetTimeZoneZonePath(path, name); |
| 161 | 162 | ||
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(); | |||
| 19 | Result MountTimeZoneBinary(Core::System& system); | 19 | Result MountTimeZoneBinary(Core::System& system); |
| 20 | void GetTimeZoneBinaryListPath(std::string& out_path); | 20 | void GetTimeZoneBinaryListPath(std::string& out_path); |
| 21 | void GetTimeZoneBinaryVersionPath(std::string& out_path); | 21 | void GetTimeZoneBinaryVersionPath(std::string& out_path); |
| 22 | void GetTimeZoneZonePath(std::string& out_path, Service::PSC::Time::LocationName& name); | 22 | void GetTimeZoneZonePath(std::string& out_path, const Service::PSC::Time::LocationName& name); |
| 23 | bool IsTimeZoneBinaryValid(Service::PSC::Time::LocationName& name); | 23 | bool IsTimeZoneBinaryValid(const Service::PSC::Time::LocationName& name); |
| 24 | u32 GetTimeZoneCount(); | 24 | u32 GetTimeZoneCount(); |
| 25 | Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version); | 25 | Result GetTimeZoneVersion(Service::PSC::Time::RuleVersion& out_rule_version); |
| 26 | Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, | 26 | Result GetTimeZoneRule(std::span<const u8>& out_rule, size_t& out_rule_size, |
| 27 | Service::PSC::Time::LocationName& name); | 27 | const Service::PSC::Time::LocationName& name); |
| 28 | Result GetTimeZoneLocationList(u32& out_count, | 28 | Result GetTimeZoneLocationList(u32& out_count, |
| 29 | std::span<Service::PSC::Time::LocationName> out_names, | 29 | std::span<Service::PSC::Time::LocationName> out_names, |
| 30 | size_t max_names, u32 index); | 30 | 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 { | |||
| 18 | 18 | ||
| 19 | void LoopProcess(Core::System& system) { | 19 | void LoopProcess(Core::System& system) { |
| 20 | auto server_manager = std::make_unique<ServerManager>(system); | 20 | auto server_manager = std::make_unique<ServerManager>(system); |
| 21 | std::shared_ptr<ResourceManager> resource_manager = std::make_shared<ResourceManager>(system); | ||
| 22 | std::shared_ptr<HidFirmwareSettings> firmware_settings = | 21 | std::shared_ptr<HidFirmwareSettings> firmware_settings = |
| 23 | std::make_shared<HidFirmwareSettings>(system); | 22 | std::make_shared<HidFirmwareSettings>(system); |
| 23 | std::shared_ptr<ResourceManager> resource_manager = | ||
| 24 | std::make_shared<ResourceManager>(system, firmware_settings); | ||
| 24 | 25 | ||
| 25 | // TODO: Remove this hack when am is emulated properly. | 26 | // TODO: Remove this hack when am is emulated properly. |
| 26 | resource_manager->Initialize(); | 27 | resource_manager->Initialize(); |
| @@ -31,7 +32,7 @@ void LoopProcess(Core::System& system) { | |||
| 31 | server_manager->RegisterNamedService( | 32 | server_manager->RegisterNamedService( |
| 32 | "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings)); | 33 | "hid", std::make_shared<IHidServer>(system, resource_manager, firmware_settings)); |
| 33 | server_manager->RegisterNamedService( | 34 | server_manager->RegisterNamedService( |
| 34 | "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager)); | 35 | "hid:dbg", std::make_shared<IHidDebugServer>(system, resource_manager, firmware_settings)); |
| 35 | server_manager->RegisterNamedService( | 36 | server_manager->RegisterNamedService( |
| 36 | "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); | 37 | "hid:sys", std::make_shared<IHidSystemServer>(system, resource_manager, firmware_settings)); |
| 37 | 38 | ||
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | ||
| 5 | |||
| 6 | #include "core/hle/service/cmif_serialization.h" | ||
| 4 | #include "core/hle/service/hid/hid_debug_server.h" | 7 | #include "core/hle/service/hid/hid_debug_server.h" |
| 5 | #include "core/hle/service/ipc_helpers.h" | 8 | #include "core/hle/service/ipc_helpers.h" |
| 9 | #include "hid_core/hid_types.h" | ||
| 6 | #include "hid_core/resource_manager.h" | 10 | #include "hid_core/resource_manager.h" |
| 11 | #include "hid_core/resources/hid_firmware_settings.h" | ||
| 12 | |||
| 13 | #include "hid_core/resources/touch_screen/gesture.h" | ||
| 14 | #include "hid_core/resources/touch_screen/touch_screen.h" | ||
| 7 | 15 | ||
| 8 | namespace Service::HID { | 16 | namespace Service::HID { |
| 9 | 17 | ||
| 10 | IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource) | 18 | IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, |
| 11 | : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource} { | 19 | std::shared_ptr<HidFirmwareSettings> settings) |
| 20 | : ServiceFramework{system_, "hid:dbg"}, resource_manager{resource}, firmware_settings{ | ||
| 21 | settings} { | ||
| 12 | // clang-format off | 22 | // clang-format off |
| 13 | static const FunctionInfo functions[] = { | 23 | static const FunctionInfo functions[] = { |
| 14 | {0, nullptr, "DeactivateDebugPad"}, | 24 | {0, nullptr, "DeactivateDebugPad"}, |
| 15 | {1, nullptr, "SetDebugPadAutoPilotState"}, | 25 | {1, nullptr, "SetDebugPadAutoPilotState"}, |
| 16 | {2, nullptr, "UnsetDebugPadAutoPilotState"}, | 26 | {2, nullptr, "UnsetDebugPadAutoPilotState"}, |
| 17 | {10, nullptr, "DeactivateTouchScreen"}, | 27 | {10, C<&IHidDebugServer::DeactivateTouchScreen>, "DeactivateTouchScreen"}, |
| 18 | {11, nullptr, "SetTouchScreenAutoPilotState"}, | 28 | {11, C<&IHidDebugServer::SetTouchScreenAutoPilotState>, "SetTouchScreenAutoPilotState"}, |
| 19 | {12, nullptr, "UnsetTouchScreenAutoPilotState"}, | 29 | {12, C<&IHidDebugServer::UnsetTouchScreenAutoPilotState>, "UnsetTouchScreenAutoPilotState"}, |
| 20 | {13, nullptr, "GetTouchScreenConfiguration"}, | 30 | {13, C<&IHidDebugServer::GetTouchScreenConfiguration>, "GetTouchScreenConfiguration"}, |
| 21 | {14, nullptr, "ProcessTouchScreenAutoTune"}, | 31 | {14, C<&IHidDebugServer::ProcessTouchScreenAutoTune>, "ProcessTouchScreenAutoTune"}, |
| 22 | {15, nullptr, "ForceStopTouchScreenManagement"}, | 32 | {15, C<&IHidDebugServer::ForceStopTouchScreenManagement>, "ForceStopTouchScreenManagement"}, |
| 23 | {16, nullptr, "ForceRestartTouchScreenManagement"}, | 33 | {16, C<&IHidDebugServer::ForceRestartTouchScreenManagement>, "ForceRestartTouchScreenManagement"}, |
| 24 | {17, nullptr, "IsTouchScreenManaged"}, | 34 | {17, C<&IHidDebugServer::IsTouchScreenManaged>, "IsTouchScreenManaged"}, |
| 25 | {20, nullptr, "DeactivateMouse"}, | 35 | {20, nullptr, "DeactivateMouse"}, |
| 26 | {21, nullptr, "SetMouseAutoPilotState"}, | 36 | {21, nullptr, "SetMouseAutoPilotState"}, |
| 27 | {22, nullptr, "UnsetMouseAutoPilotState"}, | 37 | {22, nullptr, "UnsetMouseAutoPilotState"}, |
| @@ -37,7 +47,7 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource | |||
| 37 | {60, nullptr, "ClearNpadSystemCommonPolicy"}, | 47 | {60, nullptr, "ClearNpadSystemCommonPolicy"}, |
| 38 | {61, nullptr, "DeactivateNpad"}, | 48 | {61, nullptr, "DeactivateNpad"}, |
| 39 | {62, nullptr, "ForceDisconnectNpad"}, | 49 | {62, nullptr, "ForceDisconnectNpad"}, |
| 40 | {91, nullptr, "DeactivateGesture"}, | 50 | {91, C<&IHidDebugServer::DeactivateGesture>, "DeactivateGesture"}, |
| 41 | {110, nullptr, "DeactivateHomeButton"}, | 51 | {110, nullptr, "DeactivateHomeButton"}, |
| 42 | {111, nullptr, "SetHomeButtonAutoPilotState"}, | 52 | {111, nullptr, "SetHomeButtonAutoPilotState"}, |
| 43 | {112, nullptr, "UnsetHomeButtonAutoPilotState"}, | 53 | {112, nullptr, "UnsetHomeButtonAutoPilotState"}, |
| @@ -151,6 +161,123 @@ IHidDebugServer::IHidDebugServer(Core::System& system_, std::shared_ptr<Resource | |||
| 151 | 161 | ||
| 152 | IHidDebugServer::~IHidDebugServer() = default; | 162 | IHidDebugServer::~IHidDebugServer() = default; |
| 153 | 163 | ||
| 164 | Result IHidDebugServer::DeactivateTouchScreen() { | ||
| 165 | LOG_INFO(Service_HID, "called"); | ||
| 166 | |||
| 167 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 168 | R_RETURN(GetResourceManager()->GetTouchScreen()->Deactivate()); | ||
| 169 | } | ||
| 170 | |||
| 171 | R_SUCCEED(); | ||
| 172 | } | ||
| 173 | |||
| 174 | Result IHidDebugServer::SetTouchScreenAutoPilotState( | ||
| 175 | InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer) { | ||
| 176 | AutoPilotState auto_pilot{}; | ||
| 177 | |||
| 178 | auto_pilot.count = | ||
| 179 | static_cast<u64>(std::min(auto_pilot_buffer.size(), auto_pilot.state.size())); | ||
| 180 | memcpy(auto_pilot.state.data(), auto_pilot_buffer.data(), | ||
| 181 | auto_pilot.count * sizeof(TouchState)); | ||
| 182 | |||
| 183 | LOG_INFO(Service_HID, "called, auto_pilot_count={}", auto_pilot.count); | ||
| 184 | |||
| 185 | R_RETURN(GetResourceManager()->GetTouchScreen()->SetTouchScreenAutoPilotState(auto_pilot)); | ||
| 186 | } | ||
| 187 | |||
| 188 | Result IHidDebugServer::UnsetTouchScreenAutoPilotState() { | ||
| 189 | LOG_INFO(Service_HID, "called"); | ||
| 190 | R_RETURN(GetResourceManager()->GetTouchScreen()->UnsetTouchScreenAutoPilotState()); | ||
| 191 | } | ||
| 192 | |||
| 193 | Result IHidDebugServer::GetTouchScreenConfiguration( | ||
| 194 | Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config, | ||
| 195 | ClientAppletResourceUserId aruid) { | ||
| 196 | LOG_INFO(Service_HID, "called, applet_resource_user_id={}", aruid.pid); | ||
| 197 | |||
| 198 | R_TRY(GetResourceManager()->GetTouchScreen()->GetTouchScreenConfiguration( | ||
| 199 | *out_touchscreen_config, aruid.pid)); | ||
| 200 | |||
| 201 | if (out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Heat2 && | ||
| 202 | out_touchscreen_config->mode != Core::HID::TouchScreenModeForNx::Finger) { | ||
| 203 | out_touchscreen_config->mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; | ||
| 204 | } | ||
| 205 | |||
| 206 | R_SUCCEED(); | ||
| 207 | } | ||
| 208 | |||
| 209 | Result IHidDebugServer::ProcessTouchScreenAutoTune() { | ||
| 210 | LOG_INFO(Service_HID, "called"); | ||
| 211 | R_RETURN(GetResourceManager()->GetTouchScreen()->ProcessTouchScreenAutoTune()); | ||
| 212 | } | ||
| 213 | |||
| 214 | Result IHidDebugServer::ForceStopTouchScreenManagement() { | ||
| 215 | LOG_INFO(Service_HID, "called"); | ||
| 216 | |||
| 217 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 218 | R_SUCCEED(); | ||
| 219 | } | ||
| 220 | |||
| 221 | auto touch_screen = GetResourceManager()->GetTouchScreen(); | ||
| 222 | auto gesture = GetResourceManager()->GetGesture(); | ||
| 223 | |||
| 224 | if (firmware_settings->IsTouchI2cManaged()) { | ||
| 225 | bool is_touch_active{}; | ||
| 226 | bool is_gesture_active{}; | ||
| 227 | R_TRY(touch_screen->IsActive(is_touch_active)); | ||
| 228 | R_TRY(gesture->IsActive(is_gesture_active)); | ||
| 229 | |||
| 230 | if (is_touch_active) { | ||
| 231 | R_TRY(touch_screen->Deactivate()); | ||
| 232 | } | ||
| 233 | if (is_gesture_active) { | ||
| 234 | R_TRY(gesture->Deactivate()); | ||
| 235 | } | ||
| 236 | } | ||
| 237 | |||
| 238 | R_SUCCEED(); | ||
| 239 | } | ||
| 240 | |||
| 241 | Result IHidDebugServer::ForceRestartTouchScreenManagement(u32 basic_gesture_id, | ||
| 242 | ClientAppletResourceUserId aruid) { | ||
| 243 | LOG_INFO(Service_HID, "called, basic_gesture_id={}, applet_resource_user_id={}", | ||
| 244 | basic_gesture_id, aruid.pid); | ||
| 245 | |||
| 246 | auto touch_screen = GetResourceManager()->GetTouchScreen(); | ||
| 247 | auto gesture = GetResourceManager()->GetGesture(); | ||
| 248 | |||
| 249 | if (firmware_settings->IsDeviceManaged() && firmware_settings->IsTouchI2cManaged()) { | ||
| 250 | R_TRY(gesture->Activate()); | ||
| 251 | R_TRY(gesture->Activate(aruid.pid, basic_gesture_id)); | ||
| 252 | R_TRY(touch_screen->Activate()); | ||
| 253 | R_TRY(touch_screen->Activate(aruid.pid)); | ||
| 254 | } | ||
| 255 | |||
| 256 | R_SUCCEED(); | ||
| 257 | } | ||
| 258 | |||
| 259 | Result IHidDebugServer::IsTouchScreenManaged(Out<bool> out_is_managed) { | ||
| 260 | LOG_INFO(Service_HID, "called"); | ||
| 261 | |||
| 262 | bool is_touch_active{}; | ||
| 263 | bool is_gesture_active{}; | ||
| 264 | R_TRY(GetResourceManager()->GetTouchScreen()->IsActive(is_touch_active)); | ||
| 265 | R_TRY(GetResourceManager()->GetGesture()->IsActive(is_gesture_active)); | ||
| 266 | |||
| 267 | *out_is_managed = is_touch_active || is_gesture_active; | ||
| 268 | R_SUCCEED(); | ||
| 269 | } | ||
| 270 | |||
| 271 | Result IHidDebugServer::DeactivateGesture() { | ||
| 272 | LOG_INFO(Service_HID, "called"); | ||
| 273 | |||
| 274 | if (!firmware_settings->IsDeviceManaged()) { | ||
| 275 | R_RETURN(GetResourceManager()->GetGesture()->Deactivate()); | ||
| 276 | } | ||
| 277 | |||
| 278 | R_SUCCEED(); | ||
| 279 | } | ||
| 280 | |||
| 154 | std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { | 281 | std::shared_ptr<ResourceManager> IHidDebugServer::GetResourceManager() { |
| 155 | resource_manager->Initialize(); | 282 | resource_manager->Initialize(); |
| 156 | return resource_manager; | 283 | 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 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/hle/service/cmif_types.h" | ||
| 6 | #include "core/hle/service/service.h" | 7 | #include "core/hle/service/service.h" |
| 8 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 7 | 9 | ||
| 8 | namespace Core { | 10 | namespace Core { |
| 9 | class System; | 11 | class System; |
| @@ -11,16 +13,33 @@ class System; | |||
| 11 | 13 | ||
| 12 | namespace Service::HID { | 14 | namespace Service::HID { |
| 13 | class ResourceManager; | 15 | class ResourceManager; |
| 16 | class HidFirmwareSettings; | ||
| 14 | 17 | ||
| 15 | class IHidDebugServer final : public ServiceFramework<IHidDebugServer> { | 18 | class IHidDebugServer final : public ServiceFramework<IHidDebugServer> { |
| 16 | public: | 19 | public: |
| 17 | explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource); | 20 | explicit IHidDebugServer(Core::System& system_, std::shared_ptr<ResourceManager> resource, |
| 21 | std::shared_ptr<HidFirmwareSettings> settings); | ||
| 18 | ~IHidDebugServer() override; | 22 | ~IHidDebugServer() override; |
| 19 | 23 | ||
| 20 | private: | 24 | private: |
| 25 | Result DeactivateTouchScreen(); | ||
| 26 | Result SetTouchScreenAutoPilotState( | ||
| 27 | InArray<TouchState, BufferAttr_HipcMapAlias> auto_pilot_buffer); | ||
| 28 | Result UnsetTouchScreenAutoPilotState(); | ||
| 29 | Result GetTouchScreenConfiguration( | ||
| 30 | Out<Core::HID::TouchScreenConfigurationForNx> out_touchscreen_config, | ||
| 31 | ClientAppletResourceUserId aruid); | ||
| 32 | Result ProcessTouchScreenAutoTune(); | ||
| 33 | Result ForceStopTouchScreenManagement(); | ||
| 34 | Result ForceRestartTouchScreenManagement(u32 basic_gesture_id, | ||
| 35 | ClientAppletResourceUserId aruid); | ||
| 36 | Result IsTouchScreenManaged(Out<bool> out_is_managed); | ||
| 37 | Result DeactivateGesture(); | ||
| 38 | |||
| 21 | std::shared_ptr<ResourceManager> GetResourceManager(); | 39 | std::shared_ptr<ResourceManager> GetResourceManager(); |
| 22 | 40 | ||
| 23 | std::shared_ptr<ResourceManager> resource_manager; | 41 | std::shared_ptr<ResourceManager> resource_manager; |
| 42 | std::shared_ptr<HidFirmwareSettings> firmware_settings; | ||
| 24 | }; | 43 | }; |
| 25 | 44 | ||
| 26 | } // namespace Service::HID | 45 | } // 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) { | |||
| 990 | } | 990 | } |
| 991 | 991 | ||
| 992 | if (result.IsSuccess()) { | 992 | if (result.IsSuccess()) { |
| 993 | // TODO: Use gesture id here | 993 | result = gesture->Activate(parameters.applet_resource_user_id, parameters.basic_gesture_id); |
| 994 | result = gesture->Activate(parameters.applet_resource_user_id); | ||
| 995 | } | 994 | } |
| 996 | 995 | ||
| 997 | IPC::ResponseBuilder rb{ctx, 2}; | 996 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -2470,14 +2469,22 @@ void IHidServer::GetNpadCommunicationMode(HLERequestContext& ctx) { | |||
| 2470 | 2469 | ||
| 2471 | void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { | 2470 | void IHidServer::SetTouchScreenConfiguration(HLERequestContext& ctx) { |
| 2472 | IPC::RequestParser rp{ctx}; | 2471 | IPC::RequestParser rp{ctx}; |
| 2473 | const auto touchscreen_mode{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; | 2472 | auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; |
| 2474 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 2473 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 2475 | 2474 | ||
| 2476 | LOG_WARNING(Service_HID, "(STUBBED) called, touchscreen_mode={}, applet_resource_user_id={}", | 2475 | LOG_INFO(Service_HID, "called, touchscreen_config={}, applet_resource_user_id={}", |
| 2477 | touchscreen_mode.mode, applet_resource_user_id); | 2476 | touchscreen_config.mode, applet_resource_user_id); |
| 2477 | |||
| 2478 | if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && | ||
| 2479 | touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { | ||
| 2480 | touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; | ||
| 2481 | } | ||
| 2482 | |||
| 2483 | const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenConfiguration( | ||
| 2484 | touchscreen_config, applet_resource_user_id); | ||
| 2478 | 2485 | ||
| 2479 | IPC::ResponseBuilder rb{ctx, 2}; | 2486 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2480 | rb.Push(ResultSuccess); | 2487 | rb.Push(result); |
| 2481 | } | 2488 | } |
| 2482 | 2489 | ||
| 2483 | void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { | 2490 | void IHidServer::IsFirmwareUpdateNeededForNotification(HLERequestContext& ctx) { |
| @@ -2505,11 +2512,12 @@ void IHidServer::SetTouchScreenResolution(HLERequestContext& ctx) { | |||
| 2505 | const auto height{rp.Pop<u32>()}; | 2512 | const auto height{rp.Pop<u32>()}; |
| 2506 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 2513 | const auto applet_resource_user_id{rp.Pop<u64>()}; |
| 2507 | 2514 | ||
| 2508 | GetResourceManager()->GetTouchScreen()->SetTouchscreenDimensions(width, height); | ||
| 2509 | |||
| 2510 | LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height, | 2515 | LOG_INFO(Service_HID, "called, width={}, height={}, applet_resource_user_id={}", width, height, |
| 2511 | applet_resource_user_id); | 2516 | applet_resource_user_id); |
| 2512 | 2517 | ||
| 2518 | GetResourceManager()->GetTouchScreen()->SetTouchScreenResolution(width, height, | ||
| 2519 | applet_resource_user_id); | ||
| 2520 | |||
| 2513 | IPC::ResponseBuilder rb{ctx, 2}; | 2521 | IPC::ResponseBuilder rb{ctx, 2}; |
| 2514 | rb.Push(ResultSuccess); | 2522 | rb.Push(ResultSuccess); |
| 2515 | } | 2523 | } |
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 | |||
| 155 | {1133, nullptr, "StartUsbFirmwareUpdate"}, | 155 | {1133, nullptr, "StartUsbFirmwareUpdate"}, |
| 156 | {1134, nullptr, "GetUsbFirmwareUpdateState"}, | 156 | {1134, nullptr, "GetUsbFirmwareUpdateState"}, |
| 157 | {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, | 157 | {1135, &IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory, "InitializeUsbFirmwareUpdateWithoutMemory"}, |
| 158 | {1150, nullptr, "SetTouchScreenMagnification"}, | 158 | {1150, &IHidSystemServer::SetTouchScreenMagnification, "SetTouchScreenMagnification"}, |
| 159 | {1151, nullptr, "GetTouchScreenFirmwareVersion"}, | 159 | {1151, &IHidSystemServer::GetTouchScreenFirmwareVersion, "GetTouchScreenFirmwareVersion"}, |
| 160 | {1152, nullptr, "SetTouchScreenDefaultConfiguration"}, | 160 | {1152, &IHidSystemServer::SetTouchScreenDefaultConfiguration, "SetTouchScreenDefaultConfiguration"}, |
| 161 | {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, | 161 | {1153, &IHidSystemServer::GetTouchScreenDefaultConfiguration, "GetTouchScreenDefaultConfiguration"}, |
| 162 | {1154, nullptr, "IsFirmwareAvailableForNotification"}, | 162 | {1154, nullptr, "IsFirmwareAvailableForNotification"}, |
| 163 | {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"}, | 163 | {1155, &IHidSystemServer::SetForceHandheldStyleVibration, "SetForceHandheldStyleVibration"}, |
| @@ -845,12 +845,60 @@ void IHidSystemServer::InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContex | |||
| 845 | rb.Push(ResultSuccess); | 845 | rb.Push(ResultSuccess); |
| 846 | } | 846 | } |
| 847 | 847 | ||
| 848 | void IHidSystemServer::SetTouchScreenMagnification(HLERequestContext& ctx) { | ||
| 849 | IPC::RequestParser rp{ctx}; | ||
| 850 | const auto point1x{rp.Pop<f32>()}; | ||
| 851 | const auto point1y{rp.Pop<f32>()}; | ||
| 852 | const auto point2x{rp.Pop<f32>()}; | ||
| 853 | const auto point2y{rp.Pop<f32>()}; | ||
| 854 | |||
| 855 | LOG_INFO(Service_HID, "called, point1=-({},{}), point2=({},{})", point1x, point1y, point2x, | ||
| 856 | point2y); | ||
| 857 | |||
| 858 | const Result result = GetResourceManager()->GetTouchScreen()->SetTouchScreenMagnification( | ||
| 859 | point1x, point1y, point2x, point2y); | ||
| 860 | |||
| 861 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 862 | rb.Push(result); | ||
| 863 | } | ||
| 864 | |||
| 865 | void IHidSystemServer::GetTouchScreenFirmwareVersion(HLERequestContext& ctx) { | ||
| 866 | LOG_INFO(Service_HID, "called"); | ||
| 867 | |||
| 868 | Core::HID::FirmwareVersion firmware{}; | ||
| 869 | const auto result = GetResourceManager()->GetTouchScreenFirmwareVersion(firmware); | ||
| 870 | |||
| 871 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 872 | rb.Push(result); | ||
| 873 | rb.PushRaw(firmware); | ||
| 874 | } | ||
| 875 | |||
| 876 | void IHidSystemServer::SetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { | ||
| 877 | IPC::RequestParser rp{ctx}; | ||
| 878 | auto touchscreen_config{rp.PopRaw<Core::HID::TouchScreenConfigurationForNx>()}; | ||
| 879 | |||
| 880 | LOG_INFO(Service_HID, "called, touchscreen_config={}", touchscreen_config.mode); | ||
| 881 | |||
| 882 | if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && | ||
| 883 | touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { | ||
| 884 | touchscreen_config.mode = Core::HID::TouchScreenModeForNx::UseSystemSetting; | ||
| 885 | } | ||
| 886 | |||
| 887 | const Result result = | ||
| 888 | GetResourceManager()->GetTouchScreen()->SetTouchScreenDefaultConfiguration( | ||
| 889 | touchscreen_config); | ||
| 890 | |||
| 891 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 892 | rb.Push(result); | ||
| 893 | } | ||
| 894 | |||
| 848 | void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { | 895 | void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx) { |
| 849 | LOG_WARNING(Service_HID, "(STUBBED) called"); | 896 | LOG_INFO(Service_HID, "called"); |
| 850 | 897 | ||
| 851 | Core::HID::TouchScreenConfigurationForNx touchscreen_config{ | 898 | Core::HID::TouchScreenConfigurationForNx touchscreen_config{}; |
| 852 | .mode = Core::HID::TouchScreenModeForNx::Finger, | 899 | const Result result = |
| 853 | }; | 900 | GetResourceManager()->GetTouchScreen()->GetTouchScreenDefaultConfiguration( |
| 901 | touchscreen_config); | ||
| 854 | 902 | ||
| 855 | if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && | 903 | if (touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Heat2 && |
| 856 | touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { | 904 | touchscreen_config.mode != Core::HID::TouchScreenModeForNx::Finger) { |
| @@ -858,7 +906,7 @@ void IHidSystemServer::GetTouchScreenDefaultConfiguration(HLERequestContext& ctx | |||
| 858 | } | 906 | } |
| 859 | 907 | ||
| 860 | IPC::ResponseBuilder rb{ctx, 6}; | 908 | IPC::ResponseBuilder rb{ctx, 6}; |
| 861 | rb.Push(ResultSuccess); | 909 | rb.Push(result); |
| 862 | rb.PushRaw(touchscreen_config); | 910 | rb.PushRaw(touchscreen_config); |
| 863 | } | 911 | } |
| 864 | 912 | ||
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: | |||
| 71 | void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); | 71 | void FinalizeUsbFirmwareUpdate(HLERequestContext& ctx); |
| 72 | void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx); | 72 | void CheckUsbFirmwareUpdateRequired(HLERequestContext& ctx); |
| 73 | void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); | 73 | void InitializeUsbFirmwareUpdateWithoutMemory(HLERequestContext& ctx); |
| 74 | void SetTouchScreenMagnification(HLERequestContext& ctx); | ||
| 75 | void GetTouchScreenFirmwareVersion(HLERequestContext& ctx); | ||
| 76 | void SetTouchScreenDefaultConfiguration(HLERequestContext& ctx); | ||
| 74 | void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); | 77 | void GetTouchScreenDefaultConfiguration(HLERequestContext& ctx); |
| 75 | void SetForceHandheldStyleVibration(HLERequestContext& ctx); | 78 | void SetForceHandheldStyleVibration(HLERequestContext& ctx); |
| 76 | void IsUsingCustomButtonConfig(HLERequestContext& ctx); | 79 | 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 @@ | |||
| 9 | #include "core/hle/kernel/k_shared_memory.h" | 9 | #include "core/hle/kernel/k_shared_memory.h" |
| 10 | #include "core/hle/kernel/k_transfer_memory.h" | 10 | #include "core/hle/kernel/k_transfer_memory.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/hle/service/cmif_serialization.h" | ||
| 12 | #include "core/hle/service/hid/irs.h" | 13 | #include "core/hle/service/hid/irs.h" |
| 13 | #include "core/hle/service/ipc_helpers.h" | 14 | #include "core/hle/service/ipc_helpers.h" |
| 14 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| @@ -28,24 +29,24 @@ namespace Service::IRS { | |||
| 28 | IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { | 29 | IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { |
| 29 | // clang-format off | 30 | // clang-format off |
| 30 | static const FunctionInfo functions[] = { | 31 | static const FunctionInfo functions[] = { |
| 31 | {302, &IRS::ActivateIrsensor, "ActivateIrsensor"}, | 32 | {302, C<&IRS::ActivateIrsensor>, "ActivateIrsensor"}, |
| 32 | {303, &IRS::DeactivateIrsensor, "DeactivateIrsensor"}, | 33 | {303, C<&IRS::DeactivateIrsensor>, "DeactivateIrsensor"}, |
| 33 | {304, &IRS::GetIrsensorSharedMemoryHandle, "GetIrsensorSharedMemoryHandle"}, | 34 | {304, C<&IRS::GetIrsensorSharedMemoryHandle>, "GetIrsensorSharedMemoryHandle"}, |
| 34 | {305, &IRS::StopImageProcessor, "StopImageProcessor"}, | 35 | {305, C<&IRS::StopImageProcessor>, "StopImageProcessor"}, |
| 35 | {306, &IRS::RunMomentProcessor, "RunMomentProcessor"}, | 36 | {306, C<&IRS::RunMomentProcessor>, "RunMomentProcessor"}, |
| 36 | {307, &IRS::RunClusteringProcessor, "RunClusteringProcessor"}, | 37 | {307, C<&IRS::RunClusteringProcessor>, "RunClusteringProcessor"}, |
| 37 | {308, &IRS::RunImageTransferProcessor, "RunImageTransferProcessor"}, | 38 | {308, C<&IRS::RunImageTransferProcessor>, "RunImageTransferProcessor"}, |
| 38 | {309, &IRS::GetImageTransferProcessorState, "GetImageTransferProcessorState"}, | 39 | {309, C<&IRS::GetImageTransferProcessorState>, "GetImageTransferProcessorState"}, |
| 39 | {310, &IRS::RunTeraPluginProcessor, "RunTeraPluginProcessor"}, | 40 | {310, C<&IRS::RunTeraPluginProcessor>, "RunTeraPluginProcessor"}, |
| 40 | {311, &IRS::GetNpadIrCameraHandle, "GetNpadIrCameraHandle"}, | 41 | {311, C<&IRS::GetNpadIrCameraHandle>, "GetNpadIrCameraHandle"}, |
| 41 | {312, &IRS::RunPointingProcessor, "RunPointingProcessor"}, | 42 | {312, C<&IRS::RunPointingProcessor>, "RunPointingProcessor"}, |
| 42 | {313, &IRS::SuspendImageProcessor, "SuspendImageProcessor"}, | 43 | {313, C<&IRS::SuspendImageProcessor>, "SuspendImageProcessor"}, |
| 43 | {314, &IRS::CheckFirmwareVersion, "CheckFirmwareVersion"}, | 44 | {314, C<&IRS::CheckFirmwareVersion>, "CheckFirmwareVersion"}, |
| 44 | {315, &IRS::SetFunctionLevel, "SetFunctionLevel"}, | 45 | {315, C<&IRS::SetFunctionLevel>, "SetFunctionLevel"}, |
| 45 | {316, &IRS::RunImageTransferExProcessor, "RunImageTransferExProcessor"}, | 46 | {316, C<&IRS::RunImageTransferExProcessor>, "RunImageTransferExProcessor"}, |
| 46 | {317, &IRS::RunIrLedProcessor, "RunIrLedProcessor"}, | 47 | {317, C<&IRS::RunIrLedProcessor>, "RunIrLedProcessor"}, |
| 47 | {318, &IRS::StopImageProcessorAsync, "StopImageProcessorAsync"}, | 48 | {318, C<&IRS::StopImageProcessorAsync>, "StopImageProcessorAsync"}, |
| 48 | {319, &IRS::ActivateIrsensorWithFunctionLevel, "ActivateIrsensorWithFunctionLevel"}, | 49 | {319, C<&IRS::ActivateIrsensorWithFunctionLevel>, "ActivateIrsensorWithFunctionLevel"}, |
| 49 | }; | 50 | }; |
| 50 | // clang-format on | 51 | // clang-format on |
| 51 | 52 | ||
| @@ -57,489 +58,292 @@ IRS::IRS(Core::System& system_) : ServiceFramework{system_, "irs"} { | |||
| 57 | } | 58 | } |
| 58 | IRS::~IRS() = default; | 59 | IRS::~IRS() = default; |
| 59 | 60 | ||
| 60 | void IRS::ActivateIrsensor(HLERequestContext& ctx) { | 61 | Result IRS::ActivateIrsensor(ClientAppletResourceUserId aruid) { |
| 61 | IPC::RequestParser rp{ctx}; | 62 | LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); |
| 62 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 63 | R_SUCCEED(); |
| 63 | |||
| 64 | LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", | ||
| 65 | applet_resource_user_id); | ||
| 66 | |||
| 67 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 68 | rb.Push(ResultSuccess); | ||
| 69 | } | 64 | } |
| 70 | 65 | ||
| 71 | void IRS::DeactivateIrsensor(HLERequestContext& ctx) { | 66 | Result IRS::DeactivateIrsensor(ClientAppletResourceUserId aruid) { |
| 72 | IPC::RequestParser rp{ctx}; | 67 | LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", aruid.pid); |
| 73 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 68 | R_SUCCEED(); |
| 74 | |||
| 75 | LOG_WARNING(Service_IRS, "(STUBBED) called, applet_resource_user_id={}", | ||
| 76 | applet_resource_user_id); | ||
| 77 | |||
| 78 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 79 | rb.Push(ResultSuccess); | ||
| 80 | } | 69 | } |
| 81 | 70 | ||
| 82 | void IRS::GetIrsensorSharedMemoryHandle(HLERequestContext& ctx) { | 71 | Result IRS::GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory, |
| 83 | IPC::RequestParser rp{ctx}; | 72 | ClientAppletResourceUserId aruid) { |
| 84 | const auto applet_resource_user_id{rp.Pop<u64>()}; | 73 | LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", aruid.pid); |
| 85 | |||
| 86 | LOG_DEBUG(Service_IRS, "called, applet_resource_user_id={}", applet_resource_user_id); | ||
| 87 | 74 | ||
| 88 | IPC::ResponseBuilder rb{ctx, 2, 1}; | 75 | *out_shared_memory = &system.Kernel().GetIrsSharedMem(); |
| 89 | rb.Push(ResultSuccess); | 76 | R_SUCCEED(); |
| 90 | rb.PushCopyObjects(&system.Kernel().GetIrsSharedMem()); | ||
| 91 | } | 77 | } |
| 92 | 78 | ||
| 93 | void IRS::StopImageProcessor(HLERequestContext& ctx) { | 79 | Result IRS::StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 94 | IPC::RequestParser rp{ctx}; | 80 | ClientAppletResourceUserId aruid) { |
| 95 | struct Parameters { | ||
| 96 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 97 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 98 | u64 applet_resource_user_id; | ||
| 99 | }; | ||
| 100 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 101 | |||
| 102 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 103 | |||
| 104 | LOG_WARNING(Service_IRS, | 81 | LOG_WARNING(Service_IRS, |
| 105 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 82 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 106 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 83 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 107 | parameters.applet_resource_user_id); | ||
| 108 | |||
| 109 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 110 | if (result.IsSuccess()) { | ||
| 111 | // TODO: Stop Image processor | ||
| 112 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 113 | Common::Input::PollingMode::Active); | ||
| 114 | result = ResultSuccess; | ||
| 115 | } | ||
| 116 | 84 | ||
| 117 | IPC::ResponseBuilder rb{ctx, 2}; | 85 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 118 | rb.Push(result); | ||
| 119 | } | ||
| 120 | |||
| 121 | void IRS::RunMomentProcessor(HLERequestContext& ctx) { | ||
| 122 | IPC::RequestParser rp{ctx}; | ||
| 123 | struct Parameters { | ||
| 124 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 125 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 126 | u64 applet_resource_user_id; | ||
| 127 | Core::IrSensor::PackedMomentProcessorConfig processor_config; | ||
| 128 | }; | ||
| 129 | static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); | ||
| 130 | 86 | ||
| 131 | const auto parameters{rp.PopRaw<Parameters>()}; | 87 | // TODO: Stop Image processor |
| 88 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 89 | Common::Input::PollingMode::Active); | ||
| 90 | R_SUCCEED(); | ||
| 91 | } | ||
| 132 | 92 | ||
| 93 | Result IRS::RunMomentProcessor( | ||
| 94 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 95 | const Core::IrSensor::PackedMomentProcessorConfig& processor_config) { | ||
| 133 | LOG_WARNING(Service_IRS, | 96 | LOG_WARNING(Service_IRS, |
| 134 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 97 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 135 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 98 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 136 | parameters.applet_resource_user_id); | ||
| 137 | |||
| 138 | const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 139 | |||
| 140 | if (result.IsSuccess()) { | ||
| 141 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 142 | MakeProcessorWithCoreContext<MomentProcessor>(parameters.camera_handle, device); | ||
| 143 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(parameters.camera_handle); | ||
| 144 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 145 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 146 | Common::Input::PollingMode::IR); | ||
| 147 | } | ||
| 148 | 99 | ||
| 149 | IPC::ResponseBuilder rb{ctx, 2}; | 100 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 150 | rb.Push(result); | ||
| 151 | } | ||
| 152 | 101 | ||
| 153 | void IRS::RunClusteringProcessor(HLERequestContext& ctx) { | 102 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 154 | IPC::RequestParser rp{ctx}; | 103 | MakeProcessorWithCoreContext<MomentProcessor>(camera_handle, device); |
| 155 | struct Parameters { | 104 | auto& image_transfer_processor = GetProcessor<MomentProcessor>(camera_handle); |
| 156 | Core::IrSensor::IrCameraHandle camera_handle; | 105 | image_transfer_processor.SetConfig(processor_config); |
| 157 | INSERT_PADDING_WORDS_NOINIT(1); | 106 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 158 | u64 applet_resource_user_id; | 107 | Common::Input::PollingMode::IR); |
| 159 | Core::IrSensor::PackedClusteringProcessorConfig processor_config; | ||
| 160 | }; | ||
| 161 | static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); | ||
| 162 | 108 | ||
| 163 | const auto parameters{rp.PopRaw<Parameters>()}; | 109 | R_SUCCEED(); |
| 110 | } | ||
| 164 | 111 | ||
| 112 | Result IRS::RunClusteringProcessor( | ||
| 113 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 114 | const Core::IrSensor::PackedClusteringProcessorConfig& processor_config) { | ||
| 165 | LOG_WARNING(Service_IRS, | 115 | LOG_WARNING(Service_IRS, |
| 166 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 116 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 167 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 117 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 168 | parameters.applet_resource_user_id); | ||
| 169 | |||
| 170 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 171 | |||
| 172 | if (result.IsSuccess()) { | ||
| 173 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 174 | MakeProcessorWithCoreContext<ClusteringProcessor>(parameters.camera_handle, device); | ||
| 175 | auto& image_transfer_processor = | ||
| 176 | GetProcessor<ClusteringProcessor>(parameters.camera_handle); | ||
| 177 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 178 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 179 | Common::Input::PollingMode::IR); | ||
| 180 | } | ||
| 181 | 118 | ||
| 182 | IPC::ResponseBuilder rb{ctx, 2}; | 119 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 183 | rb.Push(result); | ||
| 184 | } | ||
| 185 | 120 | ||
| 186 | void IRS::RunImageTransferProcessor(HLERequestContext& ctx) { | 121 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 187 | IPC::RequestParser rp{ctx}; | 122 | MakeProcessorWithCoreContext<ClusteringProcessor>(camera_handle, device); |
| 188 | struct Parameters { | 123 | auto& image_transfer_processor = GetProcessor<ClusteringProcessor>(camera_handle); |
| 189 | Core::IrSensor::IrCameraHandle camera_handle; | 124 | image_transfer_processor.SetConfig(processor_config); |
| 190 | INSERT_PADDING_WORDS_NOINIT(1); | 125 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 191 | u64 applet_resource_user_id; | 126 | Common::Input::PollingMode::IR); |
| 192 | Core::IrSensor::PackedImageTransferProcessorConfig processor_config; | ||
| 193 | u32 transfer_memory_size; | ||
| 194 | }; | ||
| 195 | static_assert(sizeof(Parameters) == 0x30, "Parameters has incorrect size."); | ||
| 196 | 127 | ||
| 197 | const auto parameters{rp.PopRaw<Parameters>()}; | 128 | R_SUCCEED(); |
| 198 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | 129 | } |
| 199 | 130 | ||
| 200 | auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle); | 131 | Result IRS::RunImageTransferProcessor( |
| 132 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 133 | const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config, | ||
| 134 | u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) { | ||
| 201 | 135 | ||
| 202 | if (t_mem.IsNull()) { | 136 | ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size"); |
| 203 | LOG_ERROR(Service_IRS, "t_mem is a nullptr for handle=0x{:08X}", t_mem_handle); | ||
| 204 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 205 | rb.Push(ResultUnknown); | ||
| 206 | return; | ||
| 207 | } | ||
| 208 | |||
| 209 | ASSERT_MSG(t_mem->GetSize() == parameters.transfer_memory_size, "t_mem has incorrect size"); | ||
| 210 | 137 | ||
| 211 | LOG_INFO(Service_IRS, | 138 | LOG_INFO(Service_IRS, |
| 212 | "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " | 139 | "called, npad_type={}, npad_id={}, transfer_memory_size={}, transfer_memory_size={}, " |
| 213 | "applet_resource_user_id={}", | 140 | "applet_resource_user_id={}", |
| 214 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 141 | camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, t_mem->GetSize(), |
| 215 | parameters.transfer_memory_size, t_mem->GetSize(), parameters.applet_resource_user_id); | 142 | aruid.pid); |
| 216 | |||
| 217 | const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 218 | |||
| 219 | if (result.IsSuccess()) { | ||
| 220 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 221 | MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); | ||
| 222 | auto& image_transfer_processor = | ||
| 223 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||
| 224 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 225 | image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); | ||
| 226 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 227 | Common::Input::PollingMode::IR); | ||
| 228 | } | ||
| 229 | 143 | ||
| 230 | IPC::ResponseBuilder rb{ctx, 2}; | 144 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 231 | rb.Push(result); | ||
| 232 | } | ||
| 233 | 145 | ||
| 234 | void IRS::GetImageTransferProcessorState(HLERequestContext& ctx) { | 146 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 235 | IPC::RequestParser rp{ctx}; | 147 | MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device); |
| 236 | struct Parameters { | 148 | auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle); |
| 237 | Core::IrSensor::IrCameraHandle camera_handle; | 149 | image_transfer_processor.SetConfig(processor_config); |
| 238 | INSERT_PADDING_WORDS_NOINIT(1); | 150 | image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); |
| 239 | u64 applet_resource_user_id; | 151 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 240 | }; | 152 | Common::Input::PollingMode::IR); |
| 241 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 242 | 153 | ||
| 243 | const auto parameters{rp.PopRaw<Parameters>()}; | 154 | R_SUCCEED(); |
| 155 | } | ||
| 244 | 156 | ||
| 157 | Result IRS::GetImageTransferProcessorState( | ||
| 158 | Out<Core::IrSensor::ImageTransferProcessorState> out_state, | ||
| 159 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 160 | OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data) { | ||
| 245 | LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 161 | LOG_DEBUG(Service_IRS, "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 246 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 162 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 247 | parameters.applet_resource_user_id); | ||
| 248 | |||
| 249 | const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 250 | if (result.IsError()) { | ||
| 251 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 252 | rb.Push(result); | ||
| 253 | return; | ||
| 254 | } | ||
| 255 | 163 | ||
| 256 | const auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | 164 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 257 | 165 | ||
| 258 | if (device.mode != Core::IrSensor::IrSensorMode::ImageTransferProcessor) { | 166 | const auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 259 | IPC::ResponseBuilder rb{ctx, 2}; | 167 | |
| 260 | rb.Push(InvalidProcessorState); | 168 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 261 | return; | 169 | R_UNLESS(device.mode == Core::IrSensor::IrSensorMode::ImageTransferProcessor, |
| 262 | } | 170 | InvalidProcessorState); |
| 263 | 171 | ||
| 264 | std::vector<u8> data{}; | 172 | *out_state = GetProcessor<ImageTransferProcessor>(camera_handle).GetState(out_buffer_data); |
| 265 | const auto& image_transfer_processor = | ||
| 266 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||
| 267 | const auto& state = image_transfer_processor.GetState(data); | ||
| 268 | 173 | ||
| 269 | ctx.WriteBuffer(data); | 174 | R_SUCCEED(); |
| 270 | IPC::ResponseBuilder rb{ctx, 6}; | ||
| 271 | rb.Push(ResultSuccess); | ||
| 272 | rb.PushRaw(state); | ||
| 273 | } | 175 | } |
| 274 | 176 | ||
| 275 | void IRS::RunTeraPluginProcessor(HLERequestContext& ctx) { | 177 | Result IRS::RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 276 | IPC::RequestParser rp{ctx}; | 178 | Core::IrSensor::PackedTeraPluginProcessorConfig processor_config, |
| 277 | struct Parameters { | 179 | ClientAppletResourceUserId aruid) { |
| 278 | Core::IrSensor::IrCameraHandle camera_handle; | 180 | LOG_WARNING(Service_IRS, |
| 279 | Core::IrSensor::PackedTeraPluginProcessorConfig processor_config; | 181 | "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " |
| 280 | INSERT_PADDING_WORDS_NOINIT(1); | 182 | "applet_resource_user_id={}", |
| 281 | u64 applet_resource_user_id; | 183 | camera_handle.npad_type, camera_handle.npad_id, processor_config.mode, |
| 282 | }; | 184 | processor_config.required_mcu_version.major, |
| 283 | static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size."); | 185 | processor_config.required_mcu_version.minor, aruid.pid); |
| 284 | 186 | ||
| 285 | const auto parameters{rp.PopRaw<Parameters>()}; | 187 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 286 | 188 | ||
| 287 | LOG_WARNING( | 189 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 288 | Service_IRS, | 190 | MakeProcessor<TeraPluginProcessor>(camera_handle, device); |
| 289 | "(STUBBED) called, npad_type={}, npad_id={}, mode={}, mcu_version={}.{}, " | 191 | auto& image_transfer_processor = GetProcessor<TeraPluginProcessor>(camera_handle); |
| 290 | "applet_resource_user_id={}", | 192 | image_transfer_processor.SetConfig(processor_config); |
| 291 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 193 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 292 | parameters.processor_config.mode, parameters.processor_config.required_mcu_version.major, | 194 | Common::Input::PollingMode::IR); |
| 293 | parameters.processor_config.required_mcu_version.minor, parameters.applet_resource_user_id); | ||
| 294 | |||
| 295 | const auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 296 | |||
| 297 | if (result.IsSuccess()) { | ||
| 298 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 299 | MakeProcessor<TeraPluginProcessor>(parameters.camera_handle, device); | ||
| 300 | auto& image_transfer_processor = | ||
| 301 | GetProcessor<TeraPluginProcessor>(parameters.camera_handle); | ||
| 302 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 303 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 304 | Common::Input::PollingMode::IR); | ||
| 305 | } | ||
| 306 | 195 | ||
| 307 | IPC::ResponseBuilder rb{ctx, 2}; | 196 | R_SUCCEED(); |
| 308 | rb.Push(result); | ||
| 309 | } | 197 | } |
| 310 | 198 | ||
| 311 | void IRS::GetNpadIrCameraHandle(HLERequestContext& ctx) { | 199 | Result IRS::GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle, |
| 312 | IPC::RequestParser rp{ctx}; | 200 | Core::HID::NpadIdType npad_id) { |
| 313 | const auto npad_id{rp.PopEnum<Core::HID::NpadIdType>()}; | 201 | R_UNLESS(HID::IsNpadIdValid(npad_id), HID::ResultInvalidNpadId); |
| 314 | |||
| 315 | if (npad_id > Core::HID::NpadIdType::Player8 && npad_id != Core::HID::NpadIdType::Invalid && | ||
| 316 | npad_id != Core::HID::NpadIdType::Handheld) { | ||
| 317 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 318 | rb.Push(Service::HID::ResultInvalidNpadId); | ||
| 319 | return; | ||
| 320 | } | ||
| 321 | 202 | ||
| 322 | Core::IrSensor::IrCameraHandle camera_handle{ | 203 | *out_camera_handle = { |
| 323 | .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), | 204 | .npad_id = static_cast<u8>(HID::NpadIdTypeToIndex(npad_id)), |
| 324 | .npad_type = Core::HID::NpadStyleIndex::None, | 205 | .npad_type = Core::HID::NpadStyleIndex::None, |
| 325 | }; | 206 | }; |
| 326 | 207 | ||
| 327 | LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, | 208 | LOG_INFO(Service_IRS, "called, npad_id={}, camera_npad_id={}, camera_npad_type={}", npad_id, |
| 328 | camera_handle.npad_id, camera_handle.npad_type); | 209 | out_camera_handle->npad_id, out_camera_handle->npad_type); |
| 329 | 210 | ||
| 330 | IPC::ResponseBuilder rb{ctx, 3}; | 211 | R_SUCCEED(); |
| 331 | rb.Push(ResultSuccess); | ||
| 332 | rb.PushRaw(camera_handle); | ||
| 333 | } | 212 | } |
| 334 | 213 | ||
| 335 | void IRS::RunPointingProcessor(HLERequestContext& ctx) { | 214 | Result IRS::RunPointingProcessor( |
| 336 | IPC::RequestParser rp{ctx}; | 215 | Core::IrSensor::IrCameraHandle camera_handle, |
| 337 | const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | 216 | const Core::IrSensor::PackedPointingProcessorConfig& processor_config, |
| 338 | const auto processor_config{rp.PopRaw<Core::IrSensor::PackedPointingProcessorConfig>()}; | 217 | ClientAppletResourceUserId aruid) { |
| 339 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 340 | |||
| 341 | LOG_WARNING( | 218 | LOG_WARNING( |
| 342 | Service_IRS, | 219 | Service_IRS, |
| 343 | "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", | 220 | "(STUBBED) called, npad_type={}, npad_id={}, mcu_version={}.{}, applet_resource_user_id={}", |
| 344 | camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, | 221 | camera_handle.npad_type, camera_handle.npad_id, processor_config.required_mcu_version.major, |
| 345 | processor_config.required_mcu_version.minor, applet_resource_user_id); | 222 | processor_config.required_mcu_version.minor, aruid.pid); |
| 346 | 223 | ||
| 347 | auto result = IsIrCameraHandleValid(camera_handle); | 224 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 348 | 225 | ||
| 349 | if (result.IsSuccess()) { | 226 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 350 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); | 227 | MakeProcessor<PointingProcessor>(camera_handle, device); |
| 351 | MakeProcessor<PointingProcessor>(camera_handle, device); | 228 | auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); |
| 352 | auto& image_transfer_processor = GetProcessor<PointingProcessor>(camera_handle); | 229 | image_transfer_processor.SetConfig(processor_config); |
| 353 | image_transfer_processor.SetConfig(processor_config); | 230 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 354 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | 231 | Common::Input::PollingMode::IR); |
| 355 | Common::Input::PollingMode::IR); | ||
| 356 | } | ||
| 357 | 232 | ||
| 358 | IPC::ResponseBuilder rb{ctx, 2}; | 233 | R_SUCCEED(); |
| 359 | rb.Push(result); | ||
| 360 | } | 234 | } |
| 361 | 235 | ||
| 362 | void IRS::SuspendImageProcessor(HLERequestContext& ctx) { | 236 | Result IRS::SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 363 | IPC::RequestParser rp{ctx}; | 237 | ClientAppletResourceUserId aruid) { |
| 364 | struct Parameters { | ||
| 365 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 366 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 367 | u64 applet_resource_user_id; | ||
| 368 | }; | ||
| 369 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 370 | |||
| 371 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 372 | |||
| 373 | LOG_WARNING(Service_IRS, | 238 | LOG_WARNING(Service_IRS, |
| 374 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 239 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 375 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 240 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 376 | parameters.applet_resource_user_id); | ||
| 377 | 241 | ||
| 378 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | 242 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 379 | if (result.IsSuccess()) { | ||
| 380 | // TODO: Suspend image processor | ||
| 381 | result = ResultSuccess; | ||
| 382 | } | ||
| 383 | 243 | ||
| 384 | IPC::ResponseBuilder rb{ctx, 2}; | 244 | // TODO: Suspend image processor |
| 385 | rb.Push(result); | ||
| 386 | } | ||
| 387 | 245 | ||
| 388 | void IRS::CheckFirmwareVersion(HLERequestContext& ctx) { | 246 | R_SUCCEED(); |
| 389 | IPC::RequestParser rp{ctx}; | 247 | } |
| 390 | const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||
| 391 | const auto mcu_version{rp.PopRaw<Core::IrSensor::PackedMcuVersion>()}; | ||
| 392 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 393 | 248 | ||
| 249 | Result IRS::CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 250 | Core::IrSensor::PackedMcuVersion mcu_version, | ||
| 251 | ClientAppletResourceUserId aruid) { | ||
| 394 | LOG_WARNING( | 252 | LOG_WARNING( |
| 395 | Service_IRS, | 253 | Service_IRS, |
| 396 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", | 254 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}, mcu_version={}.{}", |
| 397 | camera_handle.npad_type, camera_handle.npad_id, applet_resource_user_id, mcu_version.major, | 255 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid, mcu_version.major, |
| 398 | mcu_version.minor); | 256 | mcu_version.minor); |
| 399 | 257 | ||
| 400 | auto result = IsIrCameraHandleValid(camera_handle); | 258 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 401 | if (result.IsSuccess()) { | ||
| 402 | // TODO: Check firmware version | ||
| 403 | result = ResultSuccess; | ||
| 404 | } | ||
| 405 | 259 | ||
| 406 | IPC::ResponseBuilder rb{ctx, 2}; | 260 | // TODO: Check firmware version |
| 407 | rb.Push(result); | ||
| 408 | } | ||
| 409 | 261 | ||
| 410 | void IRS::SetFunctionLevel(HLERequestContext& ctx) { | 262 | R_SUCCEED(); |
| 411 | IPC::RequestParser rp{ctx}; | 263 | } |
| 412 | const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||
| 413 | const auto function_level{rp.PopRaw<Core::IrSensor::PackedFunctionLevel>()}; | ||
| 414 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 415 | 264 | ||
| 265 | Result IRS::SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 266 | Core::IrSensor::PackedFunctionLevel function_level, | ||
| 267 | ClientAppletResourceUserId aruid) { | ||
| 416 | LOG_WARNING( | 268 | LOG_WARNING( |
| 417 | Service_IRS, | 269 | Service_IRS, |
| 418 | "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", | 270 | "(STUBBED) called, npad_type={}, npad_id={}, function_level={}, applet_resource_user_id={}", |
| 419 | camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, | 271 | camera_handle.npad_type, camera_handle.npad_id, function_level.function_level, aruid.pid); |
| 420 | applet_resource_user_id); | ||
| 421 | 272 | ||
| 422 | auto result = IsIrCameraHandleValid(camera_handle); | 273 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 423 | if (result.IsSuccess()) { | ||
| 424 | // TODO: Set Function level | ||
| 425 | result = ResultSuccess; | ||
| 426 | } | ||
| 427 | 274 | ||
| 428 | IPC::ResponseBuilder rb{ctx, 2}; | 275 | // TODO: Set Function level |
| 429 | rb.Push(result); | ||
| 430 | } | ||
| 431 | 276 | ||
| 432 | void IRS::RunImageTransferExProcessor(HLERequestContext& ctx) { | 277 | R_SUCCEED(); |
| 433 | IPC::RequestParser rp{ctx}; | 278 | } |
| 434 | struct Parameters { | ||
| 435 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 436 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 437 | u64 applet_resource_user_id; | ||
| 438 | Core::IrSensor::PackedImageTransferProcessorExConfig processor_config; | ||
| 439 | u64 transfer_memory_size; | ||
| 440 | }; | ||
| 441 | static_assert(sizeof(Parameters) == 0x38, "Parameters has incorrect size."); | ||
| 442 | 279 | ||
| 443 | const auto parameters{rp.PopRaw<Parameters>()}; | 280 | Result IRS::RunImageTransferExProcessor( |
| 444 | const auto t_mem_handle{ctx.GetCopyHandle(0)}; | 281 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, |
| 282 | const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config, | ||
| 283 | u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem) { | ||
| 445 | 284 | ||
| 446 | auto t_mem = ctx.GetObjectFromHandle<Kernel::KTransferMemory>(t_mem_handle); | 285 | ASSERT_MSG(t_mem->GetSize() == transfer_memory_size, "t_mem has incorrect size"); |
| 447 | 286 | ||
| 448 | LOG_INFO(Service_IRS, | 287 | LOG_INFO(Service_IRS, |
| 449 | "called, npad_type={}, npad_id={}, transfer_memory_size={}, " | 288 | "called, npad_type={}, npad_id={}, transfer_memory_size={}, " |
| 450 | "applet_resource_user_id={}", | 289 | "applet_resource_user_id={}", |
| 451 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 290 | camera_handle.npad_type, camera_handle.npad_id, transfer_memory_size, aruid.pid); |
| 452 | parameters.transfer_memory_size, parameters.applet_resource_user_id); | ||
| 453 | |||
| 454 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 455 | |||
| 456 | if (result.IsSuccess()) { | ||
| 457 | auto& device = GetIrCameraSharedMemoryDeviceEntry(parameters.camera_handle); | ||
| 458 | MakeProcessorWithCoreContext<ImageTransferProcessor>(parameters.camera_handle, device); | ||
| 459 | auto& image_transfer_processor = | ||
| 460 | GetProcessor<ImageTransferProcessor>(parameters.camera_handle); | ||
| 461 | image_transfer_processor.SetConfig(parameters.processor_config); | ||
| 462 | image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); | ||
| 463 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 464 | Common::Input::PollingMode::IR); | ||
| 465 | } | ||
| 466 | 291 | ||
| 467 | IPC::ResponseBuilder rb{ctx, 2}; | 292 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 468 | rb.Push(result); | 293 | |
| 469 | } | 294 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 295 | MakeProcessorWithCoreContext<ImageTransferProcessor>(camera_handle, device); | ||
| 296 | auto& image_transfer_processor = GetProcessor<ImageTransferProcessor>(camera_handle); | ||
| 297 | image_transfer_processor.SetConfig(processor_config); | ||
| 298 | image_transfer_processor.SetTransferMemoryAddress(t_mem->GetSourceAddress()); | ||
| 299 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 300 | Common::Input::PollingMode::IR); | ||
| 470 | 301 | ||
| 471 | void IRS::RunIrLedProcessor(HLERequestContext& ctx) { | 302 | R_SUCCEED(); |
| 472 | IPC::RequestParser rp{ctx}; | 303 | } |
| 473 | const auto camera_handle{rp.PopRaw<Core::IrSensor::IrCameraHandle>()}; | ||
| 474 | const auto processor_config{rp.PopRaw<Core::IrSensor::PackedIrLedProcessorConfig>()}; | ||
| 475 | const auto applet_resource_user_id{rp.Pop<u64>()}; | ||
| 476 | 304 | ||
| 305 | Result IRS::RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 306 | Core::IrSensor::PackedIrLedProcessorConfig processor_config, | ||
| 307 | ClientAppletResourceUserId aruid) { | ||
| 477 | LOG_WARNING(Service_IRS, | 308 | LOG_WARNING(Service_IRS, |
| 478 | "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " | 309 | "(STUBBED) called, npad_type={}, npad_id={}, light_target={}, mcu_version={}.{} " |
| 479 | "applet_resource_user_id={}", | 310 | "applet_resource_user_id={}", |
| 480 | camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, | 311 | camera_handle.npad_type, camera_handle.npad_id, processor_config.light_target, |
| 481 | processor_config.required_mcu_version.major, | 312 | processor_config.required_mcu_version.major, |
| 482 | processor_config.required_mcu_version.minor, applet_resource_user_id); | 313 | processor_config.required_mcu_version.minor, aruid.pid); |
| 483 | 314 | ||
| 484 | auto result = IsIrCameraHandleValid(camera_handle); | 315 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 485 | 316 | ||
| 486 | if (result.IsSuccess()) { | 317 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); |
| 487 | auto& device = GetIrCameraSharedMemoryDeviceEntry(camera_handle); | 318 | MakeProcessor<IrLedProcessor>(camera_handle, device); |
| 488 | MakeProcessor<IrLedProcessor>(camera_handle, device); | 319 | auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); |
| 489 | auto& image_transfer_processor = GetProcessor<IrLedProcessor>(camera_handle); | 320 | image_transfer_processor.SetConfig(processor_config); |
| 490 | image_transfer_processor.SetConfig(processor_config); | 321 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 491 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | 322 | Common::Input::PollingMode::IR); |
| 492 | Common::Input::PollingMode::IR); | ||
| 493 | } | ||
| 494 | 323 | ||
| 495 | IPC::ResponseBuilder rb{ctx, 2}; | 324 | R_SUCCEED(); |
| 496 | rb.Push(result); | ||
| 497 | } | 325 | } |
| 498 | 326 | ||
| 499 | void IRS::StopImageProcessorAsync(HLERequestContext& ctx) { | 327 | Result IRS::StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle, |
| 500 | IPC::RequestParser rp{ctx}; | 328 | ClientAppletResourceUserId aruid) { |
| 501 | struct Parameters { | ||
| 502 | Core::IrSensor::IrCameraHandle camera_handle; | ||
| 503 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 504 | u64 applet_resource_user_id; | ||
| 505 | }; | ||
| 506 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 507 | |||
| 508 | const auto parameters{rp.PopRaw<Parameters>()}; | ||
| 509 | |||
| 510 | LOG_WARNING(Service_IRS, | 329 | LOG_WARNING(Service_IRS, |
| 511 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", | 330 | "(STUBBED) called, npad_type={}, npad_id={}, applet_resource_user_id={}", |
| 512 | parameters.camera_handle.npad_type, parameters.camera_handle.npad_id, | 331 | camera_handle.npad_type, camera_handle.npad_id, aruid.pid); |
| 513 | parameters.applet_resource_user_id); | ||
| 514 | |||
| 515 | auto result = IsIrCameraHandleValid(parameters.camera_handle); | ||
| 516 | if (result.IsSuccess()) { | ||
| 517 | // TODO: Stop image processor async | ||
| 518 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, | ||
| 519 | Common::Input::PollingMode::Active); | ||
| 520 | result = ResultSuccess; | ||
| 521 | } | ||
| 522 | 332 | ||
| 523 | IPC::ResponseBuilder rb{ctx, 2}; | 333 | R_TRY(IsIrCameraHandleValid(camera_handle)); |
| 524 | rb.Push(result); | ||
| 525 | } | ||
| 526 | 334 | ||
| 527 | void IRS::ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx) { | 335 | // TODO: Stop image processor async |
| 528 | IPC::RequestParser rp{ctx}; | 336 | npad_device->SetPollingMode(Core::HID::EmulatedDeviceIndex::RightIndex, |
| 529 | struct Parameters { | 337 | Common::Input::PollingMode::Active); |
| 530 | Core::IrSensor::PackedFunctionLevel function_level; | ||
| 531 | INSERT_PADDING_WORDS_NOINIT(1); | ||
| 532 | u64 applet_resource_user_id; | ||
| 533 | }; | ||
| 534 | static_assert(sizeof(Parameters) == 0x10, "Parameters has incorrect size."); | ||
| 535 | 338 | ||
| 536 | const auto parameters{rp.PopRaw<Parameters>()}; | 339 | R_SUCCEED(); |
| 340 | } | ||
| 537 | 341 | ||
| 342 | Result IRS::ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level, | ||
| 343 | ClientAppletResourceUserId aruid) { | ||
| 538 | LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", | 344 | LOG_WARNING(Service_IRS, "(STUBBED) called, function_level={}, applet_resource_user_id={}", |
| 539 | parameters.function_level.function_level, parameters.applet_resource_user_id); | 345 | function_level.function_level, aruid.pid); |
| 540 | 346 | R_SUCCEED(); | |
| 541 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 542 | rb.Push(ResultSuccess); | ||
| 543 | } | 347 | } |
| 544 | 348 | ||
| 545 | Result IRS::IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const { | 349 | 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 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include "core/core.h" | 6 | #include "core/core.h" |
| 7 | #include "core/hle/service/cmif_types.h" | ||
| 7 | #include "core/hle/service/service.h" | 8 | #include "core/hle/service/service.h" |
| 8 | #include "hid_core/hid_types.h" | 9 | #include "hid_core/hid_types.h" |
| 9 | #include "hid_core/irsensor/irs_types.h" | 10 | #include "hid_core/irsensor/irs_types.h" |
| @@ -35,26 +36,73 @@ private: | |||
| 35 | }; | 36 | }; |
| 36 | static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); | 37 | static_assert(sizeof(StatusManager) == 0x8000, "StatusManager is an invalid size"); |
| 37 | 38 | ||
| 38 | void ActivateIrsensor(HLERequestContext& ctx); | 39 | Result ActivateIrsensor(ClientAppletResourceUserId aruid); |
| 39 | void DeactivateIrsensor(HLERequestContext& ctx); | 40 | |
| 40 | void GetIrsensorSharedMemoryHandle(HLERequestContext& ctx); | 41 | Result DeactivateIrsensor(ClientAppletResourceUserId aruid); |
| 41 | void StopImageProcessor(HLERequestContext& ctx); | 42 | |
| 42 | void RunMomentProcessor(HLERequestContext& ctx); | 43 | Result GetIrsensorSharedMemoryHandle(OutCopyHandle<Kernel::KSharedMemory> out_shared_memory, |
| 43 | void RunClusteringProcessor(HLERequestContext& ctx); | 44 | ClientAppletResourceUserId aruid); |
| 44 | void RunImageTransferProcessor(HLERequestContext& ctx); | 45 | Result StopImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 45 | void GetImageTransferProcessorState(HLERequestContext& ctx); | 46 | ClientAppletResourceUserId aruid); |
| 46 | void RunTeraPluginProcessor(HLERequestContext& ctx); | 47 | |
| 47 | void GetNpadIrCameraHandle(HLERequestContext& ctx); | 48 | Result RunMomentProcessor(Core::IrSensor::IrCameraHandle camera_handle, |
| 48 | void RunPointingProcessor(HLERequestContext& ctx); | 49 | ClientAppletResourceUserId aruid, |
| 49 | void SuspendImageProcessor(HLERequestContext& ctx); | 50 | const Core::IrSensor::PackedMomentProcessorConfig& processor_config); |
| 50 | void CheckFirmwareVersion(HLERequestContext& ctx); | 51 | |
| 51 | void SetFunctionLevel(HLERequestContext& ctx); | 52 | Result RunClusteringProcessor( |
| 52 | void RunImageTransferExProcessor(HLERequestContext& ctx); | 53 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, |
| 53 | void RunIrLedProcessor(HLERequestContext& ctx); | 54 | const Core::IrSensor::PackedClusteringProcessorConfig& processor_config); |
| 54 | void StopImageProcessorAsync(HLERequestContext& ctx); | 55 | |
| 55 | void ActivateIrsensorWithFunctionLevel(HLERequestContext& ctx); | 56 | Result RunImageTransferProcessor( |
| 57 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 58 | const Core::IrSensor::PackedImageTransferProcessorConfig& processor_config, | ||
| 59 | u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem); | ||
| 60 | |||
| 61 | Result GetImageTransferProcessorState( | ||
| 62 | Out<Core::IrSensor::ImageTransferProcessorState> out_state, | ||
| 63 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 64 | OutBuffer<BufferAttr_HipcMapAlias> out_buffer_data); | ||
| 65 | |||
| 66 | Result RunTeraPluginProcessor(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 67 | Core::IrSensor::PackedTeraPluginProcessorConfig processor_config, | ||
| 68 | ClientAppletResourceUserId aruid); | ||
| 69 | |||
| 70 | Result GetNpadIrCameraHandle(Out<Core::IrSensor::IrCameraHandle> out_camera_handle, | ||
| 71 | Core::HID::NpadIdType npad_id); | ||
| 72 | |||
| 73 | Result RunPointingProcessor( | ||
| 74 | Core::IrSensor::IrCameraHandle camera_handle, | ||
| 75 | const Core::IrSensor::PackedPointingProcessorConfig& processor_config, | ||
| 76 | ClientAppletResourceUserId aruid); | ||
| 77 | |||
| 78 | Result SuspendImageProcessor(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 79 | ClientAppletResourceUserId aruid); | ||
| 80 | |||
| 81 | Result CheckFirmwareVersion(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 82 | Core::IrSensor::PackedMcuVersion mcu_version, | ||
| 83 | ClientAppletResourceUserId aruid); | ||
| 84 | |||
| 85 | Result SetFunctionLevel(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 86 | Core::IrSensor::PackedFunctionLevel function_level, | ||
| 87 | ClientAppletResourceUserId aruid); | ||
| 88 | |||
| 89 | Result RunImageTransferExProcessor( | ||
| 90 | Core::IrSensor::IrCameraHandle camera_handle, ClientAppletResourceUserId aruid, | ||
| 91 | const Core::IrSensor::PackedImageTransferProcessorExConfig& processor_config, | ||
| 92 | u64 transfer_memory_size, InCopyHandle<Kernel::KTransferMemory> t_mem); | ||
| 93 | |||
| 94 | Result RunIrLedProcessor(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 95 | Core::IrSensor::PackedIrLedProcessorConfig processor_config, | ||
| 96 | ClientAppletResourceUserId aruid); | ||
| 97 | |||
| 98 | Result StopImageProcessorAsync(Core::IrSensor::IrCameraHandle camera_handle, | ||
| 99 | ClientAppletResourceUserId aruid); | ||
| 100 | |||
| 101 | Result ActivateIrsensorWithFunctionLevel(Core::IrSensor::PackedFunctionLevel function_level, | ||
| 102 | ClientAppletResourceUserId aruid); | ||
| 56 | 103 | ||
| 57 | Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; | 104 | Result IsIrCameraHandleValid(const Core::IrSensor::IrCameraHandle& camera_handle) const; |
| 105 | |||
| 58 | Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( | 106 | Core::IrSensor::DeviceFormat& GetIrCameraSharedMemoryDeviceEntry( |
| 59 | const Core::IrSensor::IrCameraHandle& camera_handle); | 107 | const Core::IrSensor::IrCameraHandle& camera_handle); |
| 60 | 108 | ||
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: | |||
| 126 | R_THROW(ResultUnknown); | 126 | R_THROW(ResultUnknown); |
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory>& tmem, | 129 | Result LoadPlugin(u64 tmem_size, InCopyHandle<Kernel::KTransferMemory> tmem, |
| 130 | InBuffer<BufferAttr_HipcMapAlias> nrr, | 130 | InBuffer<BufferAttr_HipcMapAlias> nrr, |
| 131 | InBuffer<BufferAttr_HipcMapAlias> nro) { | 131 | InBuffer<BufferAttr_HipcMapAlias> nro) { |
| 132 | if (!tmem) { | 132 | if (!tmem) { |
| @@ -268,9 +268,9 @@ public: | |||
| 268 | 268 | ||
| 269 | private: | 269 | private: |
| 270 | Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment, | 270 | Result CreateJitEnvironment(Out<SharedPointer<IJitEnvironment>> out_jit_environment, |
| 271 | u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess>& process, | 271 | u64 rx_size, u64 ro_size, InCopyHandle<Kernel::KProcess> process, |
| 272 | InCopyHandle<Kernel::KCodeMemory>& rx_mem, | 272 | InCopyHandle<Kernel::KCodeMemory> rx_mem, |
| 273 | InCopyHandle<Kernel::KCodeMemory>& ro_mem) { | 273 | InCopyHandle<Kernel::KCodeMemory> ro_mem) { |
| 274 | if (!process) { | 274 | if (!process) { |
| 275 | LOG_ERROR(Service_JIT, "process is null"); | 275 | LOG_ERROR(Service_JIT, "process is null"); |
| 276 | R_THROW(ResultUnknown); | 276 | 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: | |||
| 189 | R_RETURN(manager->Move(metadata, new_index, create_id)); | 189 | R_RETURN(manager->Move(metadata, new_index, create_id)); |
| 190 | } | 190 | } |
| 191 | 191 | ||
| 192 | Result AddOrReplace(StoreData& store_data) { | 192 | Result AddOrReplace(const StoreData& store_data) { |
| 193 | LOG_INFO(Service_Mii, "called"); | 193 | LOG_INFO(Service_Mii, "called"); |
| 194 | R_UNLESS(is_system, ResultPermissionDenied); | 194 | R_UNLESS(is_system, ResultPermissionDenied); |
| 195 | 195 | ||
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <boost/container/small_vector.hpp> | ||
| 5 | |||
| 4 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 5 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 6 | #include "core/core.h" | 8 | #include "core/core.h" |
| @@ -38,19 +40,30 @@ NvResult nvdisp_disp0::Ioctl3(DeviceFD fd, Ioctl command, std::span<const u8> in | |||
| 38 | void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} | 40 | void nvdisp_disp0::OnOpen(NvCore::SessionId session_id, DeviceFD fd) {} |
| 39 | void nvdisp_disp0::OnClose(DeviceFD fd) {} | 41 | void nvdisp_disp0::OnClose(DeviceFD fd) {} |
| 40 | 42 | ||
| 41 | void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, | 43 | void nvdisp_disp0::Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers) { |
| 42 | u32 height, u32 stride, android::BufferTransformFlags transform, | 44 | std::vector<Tegra::FramebufferConfig> output_layers; |
| 43 | const Common::Rectangle<int>& crop_rect, | 45 | std::vector<Service::Nvidia::NvFence> output_fences; |
| 44 | std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences) { | 46 | output_layers.reserve(sorted_layers.size()); |
| 45 | const DAddr addr = nvmap.GetHandleAddress(buffer_handle); | 47 | output_fences.reserve(sorted_layers.size()); |
| 46 | LOG_TRACE(Service, | 48 | |
| 47 | "Drawing from address {:X} offset {:08X} Width {} Height {} Stride {} Format {}", | 49 | for (auto& layer : sorted_layers) { |
| 48 | addr, offset, width, height, stride, format); | 50 | output_layers.emplace_back(Tegra::FramebufferConfig{ |
| 51 | .address = nvmap.GetHandleAddress(layer.buffer_handle), | ||
| 52 | .offset = layer.offset, | ||
| 53 | .width = layer.width, | ||
| 54 | .height = layer.height, | ||
| 55 | .stride = layer.stride, | ||
| 56 | .pixel_format = layer.format, | ||
| 57 | .transform_flags = layer.transform, | ||
| 58 | .crop_rect = layer.crop_rect, | ||
| 59 | }); | ||
| 49 | 60 | ||
| 50 | const Tegra::FramebufferConfig framebuffer{addr, offset, width, height, | 61 | for (size_t i = 0; i < layer.acquire_fence.num_fences; i++) { |
| 51 | stride, format, transform, crop_rect}; | 62 | output_fences.push_back(layer.acquire_fence.fences[i]); |
| 63 | } | ||
| 64 | } | ||
| 52 | 65 | ||
| 53 | system.GPU().RequestSwapBuffers(&framebuffer, fences, num_fences); | 66 | system.GPU().RequestComposite(std::move(output_layers), std::move(output_fences)); |
| 54 | system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); | 67 | system.SpeedLimiter().DoSpeedLimiting(system.CoreTiming().GetGlobalTimeUs()); |
| 55 | system.GetPerfStats().EndSystemFrame(); | 68 | system.GetPerfStats().EndSystemFrame(); |
| 56 | system.GetPerfStats().BeginSystemFrame(); | 69 | 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 @@ | |||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "common/math_util.h" | 9 | #include "common/math_util.h" |
| 10 | #include "core/hle/service/nvdrv/devices/nvdevice.h" | 10 | #include "core/hle/service/nvdrv/devices/nvdevice.h" |
| 11 | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | 11 | #include "core/hle/service/nvnflinger/hwc_layer.h" |
| 12 | #include "core/hle/service/nvnflinger/pixel_format.h" | ||
| 13 | 12 | ||
| 14 | namespace Service::Nvidia::NvCore { | 13 | namespace Service::Nvidia::NvCore { |
| 15 | class Container; | 14 | class Container; |
| @@ -35,11 +34,8 @@ public: | |||
| 35 | void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; | 34 | void OnOpen(NvCore::SessionId session_id, DeviceFD fd) override; |
| 36 | void OnClose(DeviceFD fd) override; | 35 | void OnClose(DeviceFD fd) override; |
| 37 | 36 | ||
| 38 | /// Performs a screen flip, drawing the buffer pointed to by the handle. | 37 | /// Performs a screen flip, compositing each buffer. |
| 39 | void flip(u32 buffer_handle, u32 offset, android::PixelFormat format, u32 width, u32 height, | 38 | void Composite(std::span<const Nvnflinger::HwcLayer> sorted_layers); |
| 40 | u32 stride, android::BufferTransformFlags transform, | ||
| 41 | const Common::Rectangle<int>& crop_rect, | ||
| 42 | std::array<Service::Nvidia::NvFence, 4>& fences, u32 num_fences); | ||
| 43 | 39 | ||
| 44 | Kernel::KEvent* QueryEvent(u32 event_id) override; | 40 | Kernel::KEvent* QueryEvent(u32 event_id) override; |
| 45 | 41 | ||
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include <boost/container/small_vector.hpp> | ||
| 5 | |||
| 6 | #include "common/microprofile.h" | ||
| 7 | #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" | ||
| 8 | #include "core/hle/service/nvnflinger/buffer_item.h" | ||
| 9 | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" | ||
| 10 | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||
| 11 | #include "core/hle/service/nvnflinger/hwc_layer.h" | ||
| 12 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | ||
| 13 | #include "core/hle/service/vi/display/vi_display.h" | ||
| 14 | #include "core/hle/service/vi/layer/vi_layer.h" | ||
| 15 | |||
| 16 | namespace Service::Nvnflinger { | ||
| 17 | |||
| 18 | namespace { | ||
| 19 | |||
| 20 | s32 NormalizeSwapInterval(f32* out_speed_scale, s32 swap_interval) { | ||
| 21 | if (swap_interval <= 0) { | ||
| 22 | // As an extension, treat nonpositive swap interval as speed multiplier. | ||
| 23 | if (out_speed_scale) { | ||
| 24 | *out_speed_scale = 2.f * static_cast<f32>(1 - swap_interval); | ||
| 25 | } | ||
| 26 | |||
| 27 | swap_interval = 1; | ||
| 28 | } | ||
| 29 | |||
| 30 | if (swap_interval >= 5) { | ||
| 31 | // As an extension, treat high swap interval as precise speed control. | ||
| 32 | if (out_speed_scale) { | ||
| 33 | *out_speed_scale = static_cast<f32>(swap_interval) / 100.f; | ||
| 34 | } | ||
| 35 | |||
| 36 | swap_interval = 1; | ||
| 37 | } | ||
| 38 | |||
| 39 | return swap_interval; | ||
| 40 | } | ||
| 41 | |||
| 42 | } // namespace | ||
| 43 | |||
| 44 | HardwareComposer::HardwareComposer() = default; | ||
| 45 | HardwareComposer::~HardwareComposer() = default; | ||
| 46 | |||
| 47 | u32 HardwareComposer::ComposeLocked(f32* out_speed_scale, VI::Display& display, | ||
| 48 | Nvidia::Devices::nvdisp_disp0& nvdisp) { | ||
| 49 | boost::container::small_vector<HwcLayer, 2> composition_stack; | ||
| 50 | |||
| 51 | // Set default speed limit to 100%. | ||
| 52 | *out_speed_scale = 1.0f; | ||
| 53 | |||
| 54 | // Determine the number of vsync periods to wait before composing again. | ||
| 55 | std::optional<s32> swap_interval{}; | ||
| 56 | bool has_acquired_buffer{}; | ||
| 57 | |||
| 58 | // Acquire all necessary framebuffers. | ||
| 59 | for (size_t i = 0; i < display.GetNumLayers(); i++) { | ||
| 60 | auto& layer = display.GetLayer(i); | ||
| 61 | auto layer_id = layer.GetLayerId(); | ||
| 62 | |||
| 63 | // Try to fetch the framebuffer (either new or stale). | ||
| 64 | const auto result = this->CacheFramebufferLocked(layer, layer_id); | ||
| 65 | |||
| 66 | // If we failed, skip this layer. | ||
| 67 | if (result == CacheStatus::NoBufferAvailable) { | ||
| 68 | continue; | ||
| 69 | } | ||
| 70 | |||
| 71 | // If we acquired a new buffer, we need to present. | ||
| 72 | if (result == CacheStatus::BufferAcquired) { | ||
| 73 | has_acquired_buffer = true; | ||
| 74 | } | ||
| 75 | |||
| 76 | const auto& buffer = m_framebuffers[layer_id]; | ||
| 77 | const auto& item = buffer.item; | ||
| 78 | const auto& igbp_buffer = *item.graphic_buffer; | ||
| 79 | |||
| 80 | // TODO: get proper Z-index from layer | ||
| 81 | composition_stack.emplace_back(HwcLayer{ | ||
| 82 | .buffer_handle = igbp_buffer.BufferId(), | ||
| 83 | .offset = igbp_buffer.Offset(), | ||
| 84 | .format = igbp_buffer.ExternalFormat(), | ||
| 85 | .width = igbp_buffer.Width(), | ||
| 86 | .height = igbp_buffer.Height(), | ||
| 87 | .stride = igbp_buffer.Stride(), | ||
| 88 | .z_index = 0, | ||
| 89 | .transform = static_cast<android::BufferTransformFlags>(item.transform), | ||
| 90 | .crop_rect = item.crop, | ||
| 91 | .acquire_fence = item.fence, | ||
| 92 | }); | ||
| 93 | |||
| 94 | // We need to compose again either before this frame is supposed to | ||
| 95 | // be released, or exactly on the vsync period it should be released. | ||
| 96 | const s32 item_swap_interval = NormalizeSwapInterval(out_speed_scale, item.swap_interval); | ||
| 97 | |||
| 98 | // TODO: handle cases where swap intervals are relatively prime. So far, | ||
| 99 | // only swap intervals of 0, 1 and 2 have been observed, but if 3 were | ||
| 100 | // to be introduced, this would cause an issue. | ||
| 101 | if (swap_interval) { | ||
| 102 | swap_interval = std::min(*swap_interval, item_swap_interval); | ||
| 103 | } else { | ||
| 104 | swap_interval = item_swap_interval; | ||
| 105 | } | ||
| 106 | } | ||
| 107 | |||
| 108 | // If any new buffers were acquired, we can present. | ||
| 109 | if (has_acquired_buffer) { | ||
| 110 | // Sort by Z-index. | ||
| 111 | std::stable_sort(composition_stack.begin(), composition_stack.end(), | ||
| 112 | [&](auto& l, auto& r) { return l.z_index < r.z_index; }); | ||
| 113 | |||
| 114 | // Composite. | ||
| 115 | nvdisp.Composite(composition_stack); | ||
| 116 | } | ||
| 117 | |||
| 118 | // Render MicroProfile. | ||
| 119 | MicroProfileFlip(); | ||
| 120 | |||
| 121 | // Advance by at least one frame. | ||
| 122 | const u32 frame_advance = swap_interval.value_or(1); | ||
| 123 | m_frame_number += frame_advance; | ||
| 124 | |||
| 125 | // Release any necessary framebuffers. | ||
| 126 | for (auto& [layer_id, framebuffer] : m_framebuffers) { | ||
| 127 | if (framebuffer.release_frame_number > m_frame_number) { | ||
| 128 | // Not yet ready to release this framebuffer. | ||
| 129 | continue; | ||
| 130 | } | ||
| 131 | |||
| 132 | if (!framebuffer.is_acquired) { | ||
| 133 | // Already released. | ||
| 134 | continue; | ||
| 135 | } | ||
| 136 | |||
| 137 | if (auto* layer = display.FindLayer(layer_id); layer != nullptr) { | ||
| 138 | // TODO: support release fence | ||
| 139 | // This is needed to prevent screen tearing | ||
| 140 | layer->GetConsumer().ReleaseBuffer(framebuffer.item, android::Fence::NoFence()); | ||
| 141 | framebuffer.is_acquired = false; | ||
| 142 | } | ||
| 143 | } | ||
| 144 | |||
| 145 | return frame_advance; | ||
| 146 | } | ||
| 147 | |||
| 148 | void HardwareComposer::RemoveLayerLocked(VI::Display& display, LayerId layer_id) { | ||
| 149 | // Check if we are tracking a slot with this layer_id. | ||
| 150 | const auto it = m_framebuffers.find(layer_id); | ||
| 151 | if (it == m_framebuffers.end()) { | ||
| 152 | return; | ||
| 153 | } | ||
| 154 | |||
| 155 | // Try to release the buffer item. | ||
| 156 | auto* const layer = display.FindLayer(layer_id); | ||
| 157 | if (layer && it->second.is_acquired) { | ||
| 158 | layer->GetConsumer().ReleaseBuffer(it->second.item, android::Fence::NoFence()); | ||
| 159 | } | ||
| 160 | |||
| 161 | // Erase the slot. | ||
| 162 | m_framebuffers.erase(it); | ||
| 163 | } | ||
| 164 | |||
| 165 | bool HardwareComposer::TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer) { | ||
| 166 | // Attempt the update. | ||
| 167 | const auto status = layer.GetConsumer().AcquireBuffer(&framebuffer.item, {}, false); | ||
| 168 | if (status != android::Status::NoError) { | ||
| 169 | return false; | ||
| 170 | } | ||
| 171 | |||
| 172 | // We succeeded, so set the new release frame info. | ||
| 173 | framebuffer.release_frame_number = | ||
| 174 | NormalizeSwapInterval(nullptr, framebuffer.item.swap_interval); | ||
| 175 | framebuffer.is_acquired = true; | ||
| 176 | |||
| 177 | return true; | ||
| 178 | } | ||
| 179 | |||
| 180 | HardwareComposer::CacheStatus HardwareComposer::CacheFramebufferLocked(VI::Layer& layer, | ||
| 181 | LayerId layer_id) { | ||
| 182 | // Check if this framebuffer is already present. | ||
| 183 | const auto it = m_framebuffers.find(layer_id); | ||
| 184 | if (it != m_framebuffers.end()) { | ||
| 185 | // If it's currently still acquired, we are done. | ||
| 186 | if (it->second.is_acquired) { | ||
| 187 | return CacheStatus::CachedBufferReused; | ||
| 188 | } | ||
| 189 | |||
| 190 | // Try to acquire a new item. | ||
| 191 | if (this->TryAcquireFramebufferLocked(layer, it->second)) { | ||
| 192 | // We got a new item. | ||
| 193 | return CacheStatus::BufferAcquired; | ||
| 194 | } else { | ||
| 195 | // We didn't acquire a new item, but we can reuse the slot. | ||
| 196 | return CacheStatus::CachedBufferReused; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | |||
| 200 | // Framebuffer is not present, so try to create it. | ||
| 201 | Framebuffer framebuffer{}; | ||
| 202 | |||
| 203 | if (this->TryAcquireFramebufferLocked(layer, framebuffer)) { | ||
| 204 | // Move the buffer item into a new slot. | ||
| 205 | m_framebuffers.emplace(layer_id, std::move(framebuffer)); | ||
| 206 | |||
| 207 | // We succeeded. | ||
| 208 | return CacheStatus::BufferAcquired; | ||
| 209 | } | ||
| 210 | |||
| 211 | // We couldn't acquire the buffer item, so don't create a slot. | ||
| 212 | return CacheStatus::NoBufferAvailable; | ||
| 213 | } | ||
| 214 | |||
| 215 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <boost/container/flat_map.hpp> | ||
| 8 | |||
| 9 | #include "core/hle/service/nvnflinger/buffer_item.h" | ||
| 10 | |||
| 11 | namespace Service::Nvidia::Devices { | ||
| 12 | class nvdisp_disp0; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Service::VI { | ||
| 16 | class Display; | ||
| 17 | class Layer; | ||
| 18 | } // namespace Service::VI | ||
| 19 | |||
| 20 | namespace Service::Nvnflinger { | ||
| 21 | |||
| 22 | using LayerId = u64; | ||
| 23 | |||
| 24 | class HardwareComposer { | ||
| 25 | public: | ||
| 26 | explicit HardwareComposer(); | ||
| 27 | ~HardwareComposer(); | ||
| 28 | |||
| 29 | u32 ComposeLocked(f32* out_speed_scale, VI::Display& display, | ||
| 30 | Nvidia::Devices::nvdisp_disp0& nvdisp); | ||
| 31 | void RemoveLayerLocked(VI::Display& display, LayerId layer_id); | ||
| 32 | |||
| 33 | private: | ||
| 34 | // TODO: do we want to track frame number in vi instead? | ||
| 35 | u64 m_frame_number{0}; | ||
| 36 | |||
| 37 | private: | ||
| 38 | using ReleaseFrameNumber = u64; | ||
| 39 | |||
| 40 | struct Framebuffer { | ||
| 41 | android::BufferItem item{}; | ||
| 42 | ReleaseFrameNumber release_frame_number{}; | ||
| 43 | bool is_acquired{false}; | ||
| 44 | }; | ||
| 45 | |||
| 46 | enum class CacheStatus : u32 { | ||
| 47 | NoBufferAvailable, | ||
| 48 | BufferAcquired, | ||
| 49 | CachedBufferReused, | ||
| 50 | }; | ||
| 51 | |||
| 52 | boost::container::flat_map<LayerId, Framebuffer> m_framebuffers{}; | ||
| 53 | |||
| 54 | private: | ||
| 55 | bool TryAcquireFramebufferLocked(VI::Layer& layer, Framebuffer& framebuffer); | ||
| 56 | CacheStatus CacheFramebufferLocked(VI::Layer& layer, LayerId layer_id); | ||
| 57 | }; | ||
| 58 | |||
| 59 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/math_util.h" | ||
| 7 | #include "core/hle/service/nvdrv/nvdata.h" | ||
| 8 | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | ||
| 9 | #include "core/hle/service/nvnflinger/pixel_format.h" | ||
| 10 | #include "core/hle/service/nvnflinger/ui/fence.h" | ||
| 11 | |||
| 12 | namespace Service::Nvnflinger { | ||
| 13 | |||
| 14 | struct HwcLayer { | ||
| 15 | u32 buffer_handle; | ||
| 16 | u32 offset; | ||
| 17 | android::PixelFormat format; | ||
| 18 | u32 width; | ||
| 19 | u32 height; | ||
| 20 | u32 stride; | ||
| 21 | s32 z_index; | ||
| 22 | android::BufferTransformFlags transform; | ||
| 23 | Common::Rectangle<int> crop_rect; | ||
| 24 | android::Fence acquire_fence; | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // 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 @@ | |||
| 18 | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" | 18 | #include "core/hle/service/nvnflinger/buffer_item_consumer.h" |
| 19 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | 19 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" |
| 20 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" | 20 | #include "core/hle/service/nvnflinger/fb_share_buffer_manager.h" |
| 21 | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||
| 21 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | 22 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" |
| 22 | #include "core/hle/service/nvnflinger/nvnflinger.h" | 23 | #include "core/hle/service/nvnflinger/nvnflinger.h" |
| 23 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" | 24 | #include "core/hle/service/nvnflinger/ui/graphic_buffer.h" |
| @@ -279,45 +280,18 @@ void Nvnflinger::Compose() { | |||
| 279 | SCOPE_EXIT({ display.SignalVSyncEvent(); }); | 280 | SCOPE_EXIT({ display.SignalVSyncEvent(); }); |
| 280 | 281 | ||
| 281 | // Don't do anything for displays without layers. | 282 | // Don't do anything for displays without layers. |
| 282 | if (!display.HasLayers()) | 283 | if (!display.HasLayers()) { |
| 283 | continue; | ||
| 284 | |||
| 285 | // TODO(Subv): Support more than 1 layer. | ||
| 286 | VI::Layer& layer = display.GetLayer(0); | ||
| 287 | |||
| 288 | android::BufferItem buffer{}; | ||
| 289 | const auto status = layer.GetConsumer().AcquireBuffer(&buffer, {}, false); | ||
| 290 | |||
| 291 | if (status != android::Status::NoError) { | ||
| 292 | continue; | 284 | continue; |
| 293 | } | 285 | } |
| 294 | 286 | ||
| 295 | const auto& igbp_buffer = *buffer.graphic_buffer; | ||
| 296 | |||
| 297 | if (!system.IsPoweredOn()) { | 287 | if (!system.IsPoweredOn()) { |
| 298 | return; // We are likely shutting down | 288 | return; // We are likely shutting down |
| 299 | } | 289 | } |
| 300 | 290 | ||
| 301 | // Now send the buffer to the GPU for drawing. | ||
| 302 | // TODO(Subv): Support more than just disp0. The display device selection is probably based | ||
| 303 | // on which display we're drawing (Default, Internal, External, etc) | ||
| 304 | auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); | 291 | auto nvdisp = nvdrv->GetDevice<Nvidia::Devices::nvdisp_disp0>(disp_fd); |
| 305 | ASSERT(nvdisp); | 292 | ASSERT(nvdisp); |
| 306 | 293 | ||
| 307 | Common::Rectangle<int> crop_rect{ | 294 | swap_interval = display.GetComposer().ComposeLocked(&compose_speed_scale, display, *nvdisp); |
| 308 | static_cast<int>(buffer.crop.Left()), static_cast<int>(buffer.crop.Top()), | ||
| 309 | static_cast<int>(buffer.crop.Right()), static_cast<int>(buffer.crop.Bottom())}; | ||
| 310 | |||
| 311 | nvdisp->flip(igbp_buffer.BufferId(), igbp_buffer.Offset(), igbp_buffer.ExternalFormat(), | ||
| 312 | igbp_buffer.Width(), igbp_buffer.Height(), igbp_buffer.Stride(), | ||
| 313 | static_cast<android::BufferTransformFlags>(buffer.transform), crop_rect, | ||
| 314 | buffer.fence.fences, buffer.fence.num_fences); | ||
| 315 | |||
| 316 | MicroProfileFlip(); | ||
| 317 | |||
| 318 | swap_interval = buffer.swap_interval; | ||
| 319 | |||
| 320 | layer.GetConsumer().ReleaseBuffer(buffer, android::Fence::NoFence()); | ||
| 321 | } | 295 | } |
| 322 | } | 296 | } |
| 323 | 297 | ||
| @@ -334,15 +308,16 @@ s64 Nvnflinger::GetNextTicks() const { | |||
| 334 | speed_scale = 0.01f; | 308 | speed_scale = 0.01f; |
| 335 | } | 309 | } |
| 336 | } | 310 | } |
| 311 | |||
| 312 | // Adjust by speed limit determined during composition. | ||
| 313 | speed_scale /= compose_speed_scale; | ||
| 314 | |||
| 337 | if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { | 315 | if (system.GetNVDECActive() && settings.use_video_framerate.GetValue()) { |
| 338 | // Run at intended presentation rate during video playback. | 316 | // Run at intended presentation rate during video playback. |
| 339 | speed_scale = 1.f; | 317 | speed_scale = 1.f; |
| 340 | } | 318 | } |
| 341 | 319 | ||
| 342 | // As an extension, treat nonpositive swap interval as framerate multiplier. | 320 | const f32 effective_fps = 60.f / static_cast<f32>(swap_interval); |
| 343 | const f32 effective_fps = swap_interval <= 0 ? 120.f * static_cast<f32>(1 - swap_interval) | ||
| 344 | : 60.f / static_cast<f32>(swap_interval); | ||
| 345 | |||
| 346 | return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); | 321 | return static_cast<s64>(speed_scale * (1000000000.f / effective_fps)); |
| 347 | } | 322 | } |
| 348 | 323 | ||
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; | |||
| 46 | namespace Service::Nvnflinger { | 46 | namespace Service::Nvnflinger { |
| 47 | 47 | ||
| 48 | class FbShareBufferManager; | 48 | class FbShareBufferManager; |
| 49 | class HardwareComposer; | ||
| 49 | class HosBinderDriverServer; | 50 | class HosBinderDriverServer; |
| 50 | 51 | ||
| 51 | class Nvnflinger final { | 52 | class Nvnflinger final { |
| @@ -143,6 +144,7 @@ private: | |||
| 143 | u32 next_buffer_queue_id = 1; | 144 | u32 next_buffer_queue_id = 1; |
| 144 | 145 | ||
| 145 | s32 swap_interval = 1; | 146 | s32 swap_interval = 1; |
| 147 | f32 compose_speed_scale = 1.0f; | ||
| 146 | 148 | ||
| 147 | bool is_abandoned = false; | 149 | bool is_abandoned = false; |
| 148 | 150 | ||
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 | |||
| 22 | SharedMemory& shared_memory) | 22 | SharedMemory& shared_memory) |
| 23 | : m_system{system}, m_shared_memory{shared_memory} {} | 23 | : m_system{system}, m_shared_memory{shared_memory} {} |
| 24 | 24 | ||
| 25 | Result LocalSystemClockContextWriter::Write(SystemClockContext& context) { | 25 | Result LocalSystemClockContextWriter::Write(const SystemClockContext& context) { |
| 26 | if (m_in_use) { | 26 | if (m_in_use) { |
| 27 | R_SUCCEED_IF(context == m_context); | 27 | R_SUCCEED_IF(context == m_context); |
| 28 | m_context = context; | 28 | m_context = context; |
| @@ -43,7 +43,7 @@ NetworkSystemClockContextWriter::NetworkSystemClockContextWriter(Core::System& s | |||
| 43 | SystemClockCore& system_clock) | 43 | SystemClockCore& system_clock) |
| 44 | : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {} | 44 | : m_system{system}, m_shared_memory{shared_memory}, m_system_clock{system_clock} {} |
| 45 | 45 | ||
| 46 | Result NetworkSystemClockContextWriter::Write(SystemClockContext& context) { | 46 | Result NetworkSystemClockContextWriter::Write(const SystemClockContext& context) { |
| 47 | s64 time{}; | 47 | s64 time{}; |
| 48 | [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time); | 48 | [[maybe_unused]] auto res = m_system_clock.GetCurrentTime(&time); |
| 49 | 49 | ||
| @@ -66,7 +66,7 @@ EphemeralNetworkSystemClockContextWriter::EphemeralNetworkSystemClockContextWrit | |||
| 66 | Core::System& system) | 66 | Core::System& system) |
| 67 | : m_system{system} {} | 67 | : m_system{system} {} |
| 68 | 68 | ||
| 69 | Result EphemeralNetworkSystemClockContextWriter::Write(SystemClockContext& context) { | 69 | Result EphemeralNetworkSystemClockContextWriter::Write(const SystemClockContext& context) { |
| 70 | if (m_in_use) { | 70 | if (m_in_use) { |
| 71 | R_SUCCEED_IF(context == m_context); | 71 | R_SUCCEED_IF(context == m_context); |
| 72 | m_context = context; | 72 | 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: | |||
| 24 | public: | 24 | public: |
| 25 | virtual ~ContextWriter() = default; | 25 | virtual ~ContextWriter() = default; |
| 26 | 26 | ||
| 27 | virtual Result Write(SystemClockContext& context) = 0; | 27 | virtual Result Write(const SystemClockContext& context) = 0; |
| 28 | void SignalAllNodes(); | 28 | void SignalAllNodes(); |
| 29 | void Link(OperationEvent& operation_event); | 29 | void Link(OperationEvent& operation_event); |
| 30 | 30 | ||
| @@ -37,7 +37,7 @@ class LocalSystemClockContextWriter : public ContextWriter { | |||
| 37 | public: | 37 | public: |
| 38 | explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory); | 38 | explicit LocalSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory); |
| 39 | 39 | ||
| 40 | Result Write(SystemClockContext& context) override; | 40 | Result Write(const SystemClockContext& context) override; |
| 41 | 41 | ||
| 42 | private: | 42 | private: |
| 43 | Core::System& m_system; | 43 | Core::System& m_system; |
| @@ -52,7 +52,7 @@ public: | |||
| 52 | explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory, | 52 | explicit NetworkSystemClockContextWriter(Core::System& system, SharedMemory& shared_memory, |
| 53 | SystemClockCore& system_clock); | 53 | SystemClockCore& system_clock); |
| 54 | 54 | ||
| 55 | Result Write(SystemClockContext& context) override; | 55 | Result Write(const SystemClockContext& context) override; |
| 56 | 56 | ||
| 57 | private: | 57 | private: |
| 58 | Core::System& m_system; | 58 | Core::System& m_system; |
| @@ -67,7 +67,7 @@ class EphemeralNetworkSystemClockContextWriter : public ContextWriter { | |||
| 67 | public: | 67 | public: |
| 68 | EphemeralNetworkSystemClockContextWriter(Core::System& system); | 68 | EphemeralNetworkSystemClockContextWriter(Core::System& system); |
| 69 | 69 | ||
| 70 | Result Write(SystemClockContext& context) override; | 70 | Result Write(const SystemClockContext& context) override; |
| 71 | 71 | ||
| 72 | private: | 72 | private: |
| 73 | Core::System& m_system; | 73 | 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 @@ | |||
| 5 | 5 | ||
| 6 | namespace Service::PSC::Time { | 6 | namespace Service::PSC::Time { |
| 7 | 7 | ||
| 8 | void StandardLocalSystemClockCore::Initialize(SystemClockContext& context, s64 time) { | 8 | void StandardLocalSystemClockCore::Initialize(const SystemClockContext& context, s64 time) { |
| 9 | SteadyClockTimePoint time_point{}; | 9 | SteadyClockTimePoint time_point{}; |
| 10 | if (GetCurrentTimePoint(time_point) == ResultSuccess && | 10 | if (GetCurrentTimePoint(time_point) == ResultSuccess && |
| 11 | context.steady_time_point.IdMatches(time_point)) { | 11 | 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: | |||
| 17 | : SystemClockCore{steady_clock} {} | 17 | : SystemClockCore{steady_clock} {} |
| 18 | ~StandardLocalSystemClockCore() override = default; | 18 | ~StandardLocalSystemClockCore() override = default; |
| 19 | 19 | ||
| 20 | void Initialize(SystemClockContext& context, s64 time); | 20 | void Initialize(const SystemClockContext& context, s64 time); |
| 21 | }; | 21 | }; |
| 22 | 22 | ||
| 23 | } // namespace Service::PSC::Time | 23 | } // 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 @@ | |||
| 5 | 5 | ||
| 6 | namespace Service::PSC::Time { | 6 | namespace Service::PSC::Time { |
| 7 | 7 | ||
| 8 | void StandardNetworkSystemClockCore::Initialize(SystemClockContext& context, s64 accuracy) { | 8 | void StandardNetworkSystemClockCore::Initialize(const SystemClockContext& context, s64 accuracy) { |
| 9 | if (SetContextAndWrite(context) != ResultSuccess) { | 9 | if (SetContextAndWrite(context) != ResultSuccess) { |
| 10 | LOG_ERROR(Service_Time, "Failed to SetContext"); | 10 | LOG_ERROR(Service_Time, "Failed to SetContext"); |
| 11 | } | 11 | } |
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: | |||
| 19 | : SystemClockCore{steady_clock} {} | 19 | : SystemClockCore{steady_clock} {} |
| 20 | ~StandardNetworkSystemClockCore() override = default; | 20 | ~StandardNetworkSystemClockCore() override = default; |
| 21 | 21 | ||
| 22 | void Initialize(SystemClockContext& context, s64 accuracy); | 22 | void Initialize(const SystemClockContext& context, s64 accuracy); |
| 23 | bool IsAccuracySufficient(); | 23 | bool IsAccuracySufficient(); |
| 24 | 24 | ||
| 25 | private: | 25 | 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) | |||
| 46 | R_RETURN(m_local_system_clock.GetContext(out_context)); | 46 | R_RETURN(m_local_system_clock.GetContext(out_context)); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | Result StandardUserSystemClockCore::SetContext(SystemClockContext& context) { | 49 | Result StandardUserSystemClockCore::SetContext(const SystemClockContext& context) { |
| 50 | R_RETURN(ResultNotImplemented); | 50 | R_RETURN(ResultNotImplemented); |
| 51 | } | 51 | } |
| 52 | 52 | ||
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: | |||
| 36 | Result SetAutomaticCorrection(bool automatic_correction); | 36 | Result SetAutomaticCorrection(bool automatic_correction); |
| 37 | 37 | ||
| 38 | Result GetContext(SystemClockContext& out_context) const override; | 38 | Result GetContext(SystemClockContext& out_context) const override; |
| 39 | Result SetContext(SystemClockContext& context) override; | 39 | Result SetContext(const SystemClockContext& context) override; |
| 40 | 40 | ||
| 41 | Result GetTimePoint(SteadyClockTimePoint& out_time_point); | 41 | Result GetTimePoint(SteadyClockTimePoint& out_time_point); |
| 42 | void SetTimePointAndSignal(SteadyClockTimePoint& time_point); | 42 | 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 { | |||
| 51 | R_SUCCEED(); | 51 | R_SUCCEED(); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | Result SystemClockCore::SetContext(SystemClockContext& context) { | 54 | Result SystemClockCore::SetContext(const SystemClockContext& context) { |
| 55 | m_context = context; | 55 | m_context = context; |
| 56 | R_SUCCEED(); | 56 | R_SUCCEED(); |
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | Result SystemClockCore::SetContextAndWrite(SystemClockContext& context) { | 59 | Result SystemClockCore::SetContextAndWrite(const SystemClockContext& context) { |
| 60 | R_TRY(SetContext(context)); | 60 | R_TRY(SetContext(context)); |
| 61 | 61 | ||
| 62 | if (m_context_writer) { | 62 | 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: | |||
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | virtual Result GetContext(SystemClockContext& out_context) const; | 43 | virtual Result GetContext(SystemClockContext& out_context) const; |
| 44 | virtual Result SetContext(SystemClockContext& context); | 44 | virtual Result SetContext(const SystemClockContext& context); |
| 45 | Result SetContextAndWrite(SystemClockContext& context); | 45 | Result SetContextAndWrite(const SystemClockContext& context); |
| 46 | 46 | ||
| 47 | void LinkOperationEvent(OperationEvent& operation_event); | 47 | void LinkOperationEvent(OperationEvent& operation_event); |
| 48 | 48 | ||
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 | |||
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected, | 80 | Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected, |
| 81 | Common::UUID& clock_source_id, s64 rtc_offset, | 81 | const Common::UUID& clock_source_id, |
| 82 | s64 internal_offset, s64 test_offset) { | 82 | s64 rtc_offset, s64 internal_offset, |
| 83 | s64 test_offset) { | ||
| 83 | LOG_DEBUG(Service_Time, | 84 | LOG_DEBUG(Service_Time, |
| 84 | "called. is_rtc_reset_detected={} clock_source_id={} rtc_offset={} " | 85 | "called. is_rtc_reset_detected={} clock_source_id={} rtc_offset={} " |
| 85 | "internal_offset={} test_offset={}", | 86 | "internal_offset={} test_offset={}", |
| @@ -102,7 +103,8 @@ Result ServiceManager::SetupStandardSteadyClockCore(bool is_rtc_reset_detected, | |||
| 102 | R_SUCCEED(); | 103 | R_SUCCEED(); |
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time) { | 106 | Result ServiceManager::SetupStandardLocalSystemClockCore(const SystemClockContext& context, |
| 107 | s64 time) { | ||
| 106 | LOG_DEBUG(Service_Time, | 108 | LOG_DEBUG(Service_Time, |
| 107 | "called. context={} context.steady_time_point.clock_source_id={} time={}", context, | 109 | "called. context={} context.steady_time_point.clock_source_id={} time={}", context, |
| 108 | context.steady_time_point.clock_source_id.RawString(), time); | 110 | context.steady_time_point.clock_source_id.RawString(), time); |
| @@ -114,7 +116,7 @@ Result ServiceManager::SetupStandardLocalSystemClockCore(SystemClockContext& con | |||
| 114 | R_SUCCEED(); | 116 | R_SUCCEED(); |
| 115 | } | 117 | } |
| 116 | 118 | ||
| 117 | Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& context, | 119 | Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext context, |
| 118 | s64 accuracy) { | 120 | s64 accuracy) { |
| 119 | LOG_DEBUG(Service_Time, "called. context={} steady_time_point.clock_source_id={} accuracy={}", | 121 | LOG_DEBUG(Service_Time, "called. context={} steady_time_point.clock_source_id={} accuracy={}", |
| 120 | context, context.steady_time_point.clock_source_id.RawString(), accuracy); | 122 | context, context.steady_time_point.clock_source_id.RawString(), accuracy); |
| @@ -131,7 +133,7 @@ Result ServiceManager::SetupStandardNetworkSystemClockCore(SystemClockContext& c | |||
| 131 | } | 133 | } |
| 132 | 134 | ||
| 133 | Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correction, | 135 | Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correction, |
| 134 | SteadyClockTimePoint& time_point) { | 136 | SteadyClockTimePoint time_point) { |
| 135 | LOG_DEBUG(Service_Time, "called. automatic_correction={} time_point={} clock_source_id={}", | 137 | LOG_DEBUG(Service_Time, "called. automatic_correction={} time_point={} clock_source_id={}", |
| 136 | automatic_correction, time_point, time_point.clock_source_id.RawString()); | 138 | automatic_correction, time_point, time_point.clock_source_id.RawString()); |
| 137 | 139 | ||
| @@ -144,9 +146,9 @@ Result ServiceManager::SetupStandardUserSystemClockCore(bool automatic_correctio | |||
| 144 | R_SUCCEED(); | 146 | R_SUCCEED(); |
| 145 | } | 147 | } |
| 146 | 148 | ||
| 147 | Result ServiceManager::SetupTimeZoneServiceCore(LocationName& name, RuleVersion& rule_version, | 149 | Result ServiceManager::SetupTimeZoneServiceCore(const LocationName& name, |
| 148 | u32 location_count, | 150 | const RuleVersion& rule_version, u32 location_count, |
| 149 | SteadyClockTimePoint& time_point, | 151 | const SteadyClockTimePoint& time_point, |
| 150 | InBuffer<BufferAttr_HipcAutoSelect> rule_buffer) { | 152 | InBuffer<BufferAttr_HipcAutoSelect> rule_buffer) { |
| 151 | LOG_DEBUG(Service_Time, | 153 | LOG_DEBUG(Service_Time, |
| 152 | "called. name={} rule_version={} location_count={} time_point={} " | 154 | "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: | |||
| 34 | Result GetStaticServiceAsAdmin(OutInterface<StaticService> out_service); | 34 | Result GetStaticServiceAsAdmin(OutInterface<StaticService> out_service); |
| 35 | Result GetStaticServiceAsRepair(OutInterface<StaticService> out_service); | 35 | Result GetStaticServiceAsRepair(OutInterface<StaticService> out_service); |
| 36 | Result GetStaticServiceAsServiceManager(OutInterface<StaticService> out_service); | 36 | Result GetStaticServiceAsServiceManager(OutInterface<StaticService> out_service); |
| 37 | Result SetupStandardSteadyClockCore(bool is_rtc_reset_detected, Common::UUID& clock_source_id, | 37 | Result SetupStandardSteadyClockCore(bool is_rtc_reset_detected, |
| 38 | s64 rtc_offset, s64 internal_offset, s64 test_offset); | 38 | const Common::UUID& clock_source_id, s64 rtc_offset, |
| 39 | Result SetupStandardLocalSystemClockCore(SystemClockContext& context, s64 time); | 39 | s64 internal_offset, s64 test_offset); |
| 40 | Result SetupStandardNetworkSystemClockCore(SystemClockContext& context, s64 accuracy); | 40 | Result SetupStandardLocalSystemClockCore(const SystemClockContext& context, s64 time); |
| 41 | Result SetupStandardNetworkSystemClockCore(SystemClockContext context, s64 accuracy); | ||
| 41 | Result SetupStandardUserSystemClockCore(bool automatic_correction, | 42 | Result SetupStandardUserSystemClockCore(bool automatic_correction, |
| 42 | SteadyClockTimePoint& time_point); | 43 | SteadyClockTimePoint time_point); |
| 43 | Result SetupTimeZoneServiceCore(LocationName& name, RuleVersion& rule_version, | 44 | Result SetupTimeZoneServiceCore(const LocationName& name, const RuleVersion& rule_version, |
| 44 | u32 location_count, SteadyClockTimePoint& time_point, | 45 | u32 location_count, const SteadyClockTimePoint& time_point, |
| 45 | InBuffer<BufferAttr_HipcAutoSelect> rule_buffer); | 46 | InBuffer<BufferAttr_HipcAutoSelect> rule_buffer); |
| 46 | Result SetupEphemeralNetworkSystemClockCore(); | 47 | Result SetupEphemeralNetworkSystemClockCore(); |
| 47 | Result GetStandardLocalClockOperationEvent(OutCopyHandle<Kernel::KReadableEvent> out_event); | 48 | 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) | |||
| 51 | std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr)); | 51 | std::memset(m_shared_memory_ptr, 0, sizeof(*m_shared_memory_ptr)); |
| 52 | } | 52 | } |
| 53 | 53 | ||
| 54 | void SharedMemory::SetLocalSystemContext(SystemClockContext& context) { | 54 | void SharedMemory::SetLocalSystemContext(const SystemClockContext& context) { |
| 55 | WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context); | 55 | WriteToLockFreeAtomicType(&m_shared_memory_ptr->local_system_clock_contexts, context); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | void SharedMemory::SetNetworkSystemContext(SystemClockContext& context) { | 58 | void SharedMemory::SetNetworkSystemContext(const SystemClockContext& context) { |
| 59 | WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context); | 59 | WriteToLockFreeAtomicType(&m_shared_memory_ptr->network_system_clock_contexts, context); |
| 60 | } | 60 | } |
| 61 | 61 | ||
| @@ -64,7 +64,7 @@ void SharedMemory::SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 ti | |||
| 64 | {time_point, clock_source_id}); | 64 | {time_point, clock_source_id}); |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | void SharedMemory::SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point) { | 67 | void SharedMemory::SetContinuousAdjustment(const ContinuousAdjustmentTimePoint& time_point) { |
| 68 | WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point); | 68 | WriteToLockFreeAtomicType(&m_shared_memory_ptr->continuous_adjustment_time_points, time_point); |
| 69 | } | 69 | } |
| 70 | 70 | ||
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: | |||
| 54 | return m_k_shared_memory; | 54 | return m_k_shared_memory; |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | void SetLocalSystemContext(SystemClockContext& context); | 57 | void SetLocalSystemContext(const SystemClockContext& context); |
| 58 | void SetNetworkSystemContext(SystemClockContext& context); | 58 | void SetNetworkSystemContext(const SystemClockContext& context); |
| 59 | void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff); | 59 | void SetSteadyClockTimePoint(ClockSourceId clock_source_id, s64 time_diff); |
| 60 | void SetContinuousAdjustment(ContinuousAdjustmentTimePoint& time_point); | 60 | void SetContinuousAdjustment(const ContinuousAdjustmentTimePoint& time_point); |
| 61 | void SetAutomaticCorrection(bool automatic_correction); | 61 | void SetAutomaticCorrection(bool automatic_correction); |
| 62 | void UpdateBaseTime(s64 time); | 62 | void UpdateBaseTime(s64 time); |
| 63 | 63 | ||
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( | |||
| 198 | R_SUCCEED(); | 198 | R_SUCCEED(); |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | Result StaticService::CalculateMonotonicSystemClockBaseTimePoint(Out<s64> out_time, | 201 | Result StaticService::CalculateMonotonicSystemClockBaseTimePoint( |
| 202 | SystemClockContext& context) { | 202 | Out<s64> out_time, const SystemClockContext& context) { |
| 203 | SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); | 203 | SCOPE_EXIT({ LOG_DEBUG(Service_Time, "called. context={} out_time={}", context, *out_time); }); |
| 204 | 204 | ||
| 205 | R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized); | 205 | R_UNLESS(m_time->m_standard_steady_clock.IsInitialized(), ResultClockUninitialized); |
| @@ -231,10 +231,9 @@ Result StaticService::GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType t | |||
| 231 | R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); | 231 | R_RETURN(GetClockSnapshotImpl(out_snapshot, user_context, network_context, type)); |
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | Result StaticService::GetClockSnapshotFromSystemClockContext(TimeType type, | 234 | Result StaticService::GetClockSnapshotFromSystemClockContext( |
| 235 | OutClockSnapshot out_snapshot, | 235 | TimeType type, OutClockSnapshot out_snapshot, const SystemClockContext& user_context, |
| 236 | SystemClockContext& user_context, | 236 | const SystemClockContext& network_context) { |
| 237 | SystemClockContext& network_context) { | ||
| 238 | SCOPE_EXIT({ | 237 | SCOPE_EXIT({ |
| 239 | LOG_DEBUG(Service_Time, | 238 | LOG_DEBUG(Service_Time, |
| 240 | "called. type={} user_context={} network_context={} out_snapshot={}", type, | 239 | "called. type={} user_context={} network_context={} out_snapshot={}", type, |
| @@ -294,8 +293,9 @@ Result StaticService::CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, | |||
| 294 | } | 293 | } |
| 295 | 294 | ||
| 296 | Result StaticService::GetClockSnapshotImpl(OutClockSnapshot out_snapshot, | 295 | Result StaticService::GetClockSnapshotImpl(OutClockSnapshot out_snapshot, |
| 297 | SystemClockContext& user_context, | 296 | const SystemClockContext& user_context, |
| 298 | SystemClockContext& network_context, TimeType type) { | 297 | const SystemClockContext& network_context, |
| 298 | TimeType type) { | ||
| 299 | out_snapshot->user_context = user_context; | 299 | out_snapshot->user_context = user_context; |
| 300 | out_snapshot->network_context = network_context; | 300 | out_snapshot->network_context = network_context; |
| 301 | 301 | ||
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: | |||
| 55 | Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( | 55 | Result GetStandardUserSystemClockAutomaticCorrectionUpdatedTime( |
| 56 | Out<SteadyClockTimePoint> out_time_point); | 56 | Out<SteadyClockTimePoint> out_time_point); |
| 57 | Result CalculateMonotonicSystemClockBaseTimePoint(Out<s64> out_time, | 57 | Result CalculateMonotonicSystemClockBaseTimePoint(Out<s64> out_time, |
| 58 | SystemClockContext& context); | 58 | const SystemClockContext& context); |
| 59 | Result GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type); | 59 | Result GetClockSnapshot(OutClockSnapshot out_snapshot, TimeType type); |
| 60 | Result GetClockSnapshotFromSystemClockContext(TimeType type, OutClockSnapshot out_snapshot, | 60 | Result GetClockSnapshotFromSystemClockContext(TimeType type, OutClockSnapshot out_snapshot, |
| 61 | SystemClockContext& user_context, | 61 | const SystemClockContext& user_context, |
| 62 | SystemClockContext& network_context); | 62 | const SystemClockContext& network_context); |
| 63 | Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference, | 63 | Result CalculateStandardUserSystemClockDifferenceByUser(Out<s64> out_difference, |
| 64 | InClockSnapshot a, InClockSnapshot b); | 64 | InClockSnapshot a, InClockSnapshot b); |
| 65 | Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b); | 65 | Result CalculateSpanBetween(Out<s64> out_time, InClockSnapshot a, InClockSnapshot b); |
| 66 | 66 | ||
| 67 | private: | 67 | private: |
| 68 | Result GetClockSnapshotImpl(OutClockSnapshot out_snapshot, SystemClockContext& user_context, | 68 | Result GetClockSnapshotImpl(OutClockSnapshot out_snapshot, |
| 69 | SystemClockContext& network_context, TimeType type); | 69 | const SystemClockContext& user_context, |
| 70 | const SystemClockContext& network_context, TimeType type); | ||
| 70 | 71 | ||
| 71 | Core::System& m_system; | 72 | Core::System& m_system; |
| 72 | StaticServiceSetupInfo m_setup_info; | 73 | 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) { | |||
| 53 | R_RETURN(m_clock_core.GetContext(*out_context)); | 53 | R_RETURN(m_clock_core.GetContext(*out_context)); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | Result SystemClock::SetSystemClockContext(SystemClockContext& context) { | 56 | Result SystemClock::SetSystemClockContext(const SystemClockContext& context) { |
| 57 | LOG_DEBUG(Service_Time, "called. context={}", context); | 57 | LOG_DEBUG(Service_Time, "called. context={}", context); |
| 58 | 58 | ||
| 59 | R_UNLESS(m_can_write_clock, ResultPermissionDenied); | 59 | 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: | |||
| 26 | Result GetCurrentTime(Out<s64> out_time); | 26 | Result GetCurrentTime(Out<s64> out_time); |
| 27 | Result SetCurrentTime(s64 time); | 27 | Result SetCurrentTime(s64 time); |
| 28 | Result GetSystemClockContext(Out<SystemClockContext> out_context); | 28 | Result GetSystemClockContext(Out<SystemClockContext> out_context); |
| 29 | Result SetSystemClockContext(SystemClockContext& context); | 29 | Result SetSystemClockContext(const SystemClockContext& context); |
| 30 | Result GetOperationEventReadableHandle(OutCopyHandle<Kernel::KReadableEvent> out_event); | 30 | Result GetOperationEventReadableHandle(OutCopyHandle<Kernel::KReadableEvent> out_event); |
| 31 | 31 | ||
| 32 | private: | 32 | 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 | |||
| 55 | } | 55 | } |
| 56 | } // namespace | 56 | } // namespace |
| 57 | 57 | ||
| 58 | void TimeZone::SetTimePoint(SteadyClockTimePoint& time_point) { | 58 | void TimeZone::SetTimePoint(const SteadyClockTimePoint& time_point) { |
| 59 | std::scoped_lock l{m_mutex}; | 59 | std::scoped_lock l{m_mutex}; |
| 60 | m_steady_clock_time_point = time_point; | 60 | m_steady_clock_time_point = time_point; |
| 61 | } | 61 | } |
| @@ -65,7 +65,7 @@ void TimeZone::SetTotalLocationNameCount(u32 count) { | |||
| 65 | m_total_location_name_count = count; | 65 | m_total_location_name_count = count; |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | void TimeZone::SetRuleVersion(RuleVersion& rule_version) { | 68 | void TimeZone::SetRuleVersion(const RuleVersion& rule_version) { |
| 69 | std::scoped_lock l{m_mutex}; | 69 | std::scoped_lock l{m_mutex}; |
| 70 | m_rule_version = rule_version; | 70 | m_rule_version = rule_version; |
| 71 | } | 71 | } |
| @@ -123,7 +123,7 @@ Result TimeZone::ToCalendarTimeWithMyRule(CalendarTime& calendar_time, | |||
| 123 | R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule)); | 123 | R_RETURN(ToCalendarTimeImpl(calendar_time, calendar_additional, time, m_my_rule)); |
| 124 | } | 124 | } |
| 125 | 125 | ||
| 126 | Result TimeZone::ParseBinary(LocationName& name, std::span<const u8> binary) { | 126 | Result TimeZone::ParseBinary(const LocationName& name, std::span<const u8> binary) { |
| 127 | std::scoped_lock l{m_mutex}; | 127 | std::scoped_lock l{m_mutex}; |
| 128 | 128 | ||
| 129 | Tz::Rule tmp_rule{}; | 129 | 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: | |||
| 23 | m_initialized = true; | 23 | m_initialized = true; |
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | void SetTimePoint(SteadyClockTimePoint& time_point); | 26 | void SetTimePoint(const SteadyClockTimePoint& time_point); |
| 27 | void SetTotalLocationNameCount(u32 count); | 27 | void SetTotalLocationNameCount(u32 count); |
| 28 | void SetRuleVersion(RuleVersion& rule_version); | 28 | void SetRuleVersion(const RuleVersion& rule_version); |
| 29 | Result GetLocationName(LocationName& out_name); | 29 | Result GetLocationName(LocationName& out_name); |
| 30 | Result GetTotalLocationCount(u32& out_count); | 30 | Result GetTotalLocationCount(u32& out_count); |
| 31 | Result GetRuleVersion(RuleVersion& out_rule_version); | 31 | Result GetRuleVersion(RuleVersion& out_rule_version); |
| @@ -36,7 +36,7 @@ public: | |||
| 36 | const Tz::Rule& rule); | 36 | const Tz::Rule& rule); |
| 37 | Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time, | 37 | Result ToCalendarTimeWithMyRule(CalendarTime& calendar_time, |
| 38 | CalendarAdditionalInfo& calendar_additional, s64 time); | 38 | CalendarAdditionalInfo& calendar_additional, s64 time); |
| 39 | Result ParseBinary(LocationName& name, std::span<const u8> binary); | 39 | Result ParseBinary(const LocationName& name, std::span<const u8> binary); |
| 40 | Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary); | 40 | Result ParseBinaryInto(Tz::Rule& out_rule, std::span<const u8> binary); |
| 41 | Result ToPosixTime(u32& out_count, std::span<s64> out_times, size_t out_times_max_count, | 41 | Result ToPosixTime(u32& out_count, std::span<s64> out_times, size_t out_times_max_count, |
| 42 | const CalendarTime& calendar, const Tz::Rule& rule); | 42 | 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 | |||
| 42 | R_RETURN(m_time_zone.GetLocationName(*out_location_name)); | 42 | R_RETURN(m_time_zone.GetLocationName(*out_location_name)); |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | Result TimeZoneService::SetDeviceLocationName(LocationName& location_name) { | 45 | Result TimeZoneService::SetDeviceLocationName(const LocationName& location_name) { |
| 46 | LOG_DEBUG(Service_Time, "called. This function is not implemented!"); | 46 | LOG_DEBUG(Service_Time, "called. This function is not implemented!"); |
| 47 | 47 | ||
| 48 | R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied); | 48 | R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied); |
| @@ -62,7 +62,7 @@ Result TimeZoneService::LoadLocationNameList( | |||
| 62 | R_RETURN(ResultNotImplemented); | 62 | R_RETURN(ResultNotImplemented); |
| 63 | } | 63 | } |
| 64 | 64 | ||
| 65 | Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, LocationName& location_name) { | 65 | Result TimeZoneService::LoadTimeZoneRule(OutRule out_rule, const LocationName& location_name) { |
| 66 | LOG_DEBUG(Service_Time, "called. This function is not implemented!"); | 66 | LOG_DEBUG(Service_Time, "called. This function is not implemented!"); |
| 67 | 67 | ||
| 68 | R_RETURN(ResultNotImplemented); | 68 | R_RETURN(ResultNotImplemented); |
| @@ -86,7 +86,7 @@ Result TimeZoneService::GetDeviceLocationNameAndUpdatedTime( | |||
| 86 | } | 86 | } |
| 87 | 87 | ||
| 88 | Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule( | 88 | Result TimeZoneService::SetDeviceLocationNameWithTimeZoneRule( |
| 89 | LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) { | 89 | const LocationName& location_name, InBuffer<BufferAttr_HipcAutoSelect> binary) { |
| 90 | LOG_DEBUG(Service_Time, "called. location_name={}", location_name); | 90 | LOG_DEBUG(Service_Time, "called. location_name={}", location_name); |
| 91 | 91 | ||
| 92 | R_UNLESS(m_can_write_timezone_device_location, ResultPermissionDenied); | 92 | 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: | |||
| 31 | ~TimeZoneService() override = default; | 31 | ~TimeZoneService() override = default; |
| 32 | 32 | ||
| 33 | Result GetDeviceLocationName(Out<LocationName> out_location_name); | 33 | Result GetDeviceLocationName(Out<LocationName> out_location_name); |
| 34 | Result SetDeviceLocationName(LocationName& location_name); | 34 | Result SetDeviceLocationName(const LocationName& location_name); |
| 35 | Result GetTotalLocationNameCount(Out<u32> out_count); | 35 | Result GetTotalLocationNameCount(Out<u32> out_count); |
| 36 | Result LoadLocationNameList(Out<u32> out_count, | 36 | Result LoadLocationNameList(Out<u32> out_count, |
| 37 | OutArray<LocationName, BufferAttr_HipcMapAlias> out_names, | 37 | OutArray<LocationName, BufferAttr_HipcMapAlias> out_names, |
| 38 | u32 index); | 38 | u32 index); |
| 39 | Result LoadTimeZoneRule(OutRule out_rule, LocationName& location_name); | 39 | Result LoadTimeZoneRule(OutRule out_rule, const LocationName& location_name); |
| 40 | Result GetTimeZoneRuleVersion(Out<RuleVersion> out_rule_version); | 40 | Result GetTimeZoneRuleVersion(Out<RuleVersion> out_rule_version); |
| 41 | Result GetDeviceLocationNameAndUpdatedTime(Out<LocationName> location_name, | 41 | Result GetDeviceLocationNameAndUpdatedTime(Out<LocationName> location_name, |
| 42 | Out<SteadyClockTimePoint> out_time_point); | 42 | Out<SteadyClockTimePoint> out_time_point); |
| 43 | Result SetDeviceLocationNameWithTimeZoneRule(LocationName& location_name, | 43 | Result SetDeviceLocationNameWithTimeZoneRule(const LocationName& location_name, |
| 44 | InBuffer<BufferAttr_HipcAutoSelect> binary); | 44 | InBuffer<BufferAttr_HipcAutoSelect> binary); |
| 45 | Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary); | 45 | Result ParseTimeZoneBinary(OutRule out_rule, InBuffer<BufferAttr_HipcAutoSelect> binary); |
| 46 | Result GetDeviceLocationNameOperationEventReadableHandle( | 46 | 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: | |||
| 549 | } | 549 | } |
| 550 | 550 | ||
| 551 | Result RegisterProcessHandle(ClientProcessId client_pid, | 551 | Result RegisterProcessHandle(ClientProcessId client_pid, |
| 552 | InCopyHandle<Kernel::KProcess>& process) { | 552 | InCopyHandle<Kernel::KProcess> process) { |
| 553 | // Register the process. | 553 | // Register the process. |
| 554 | R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.Get(), *client_pid)); | 554 | R_RETURN(m_ro->RegisterProcess(std::addressof(m_context_id), process.Get(), *client_pid)); |
| 555 | } | 555 | } |
| 556 | 556 | ||
| 557 | Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, | 557 | Result RegisterProcessModuleInfo(ClientProcessId client_pid, u64 nrr_address, u64 nrr_size, |
| 558 | InCopyHandle<Kernel::KProcess>& process) { | 558 | InCopyHandle<Kernel::KProcess> process) { |
| 559 | // Validate the process. | 559 | // Validate the process. |
| 560 | R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); | 560 | R_TRY(m_ro->ValidateProcess(m_context_id, *client_pid)); |
| 561 | 561 | ||
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 @@ | |||
| 12 | #include "common/vector_math.h" | 12 | #include "common/vector_math.h" |
| 13 | #include "core/hle/service/set/setting_formats/private_settings.h" | 13 | #include "core/hle/service/set/setting_formats/private_settings.h" |
| 14 | #include "core/hle/service/set/settings_types.h" | 14 | #include "core/hle/service/set/settings_types.h" |
| 15 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 15 | 16 | ||
| 16 | namespace Service::Set { | 17 | namespace Service::Set { |
| 17 | 18 | ||
| @@ -257,8 +258,7 @@ struct SystemSettings { | |||
| 257 | std::array<u8, 0x10> analog_stick_user_calibration_left; | 258 | std::array<u8, 0x10> analog_stick_user_calibration_left; |
| 258 | std::array<u8, 0x10> analog_stick_user_calibration_right; | 259 | std::array<u8, 0x10> analog_stick_user_calibration_right; |
| 259 | 260 | ||
| 260 | // nn::settings::system::TouchScreenMode | 261 | TouchScreenMode touch_screen_mode; |
| 261 | s32 touch_screen_mode; | ||
| 262 | INSERT_PADDING_BYTES(0x14); // Reserved | 262 | INSERT_PADDING_BYTES(0x14); // Reserved |
| 263 | 263 | ||
| 264 | TvSettings tv_settings; | 264 | 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_) | |||
| 275 | {184, nullptr, "SetPlatformRegion"}, | 275 | {184, nullptr, "SetPlatformRegion"}, |
| 276 | {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, | 276 | {185, &ISystemSettingsServer::GetHomeMenuSchemeModel, "GetHomeMenuSchemeModel"}, |
| 277 | {186, nullptr, "GetMemoryUsageRateFlag"}, | 277 | {186, nullptr, "GetMemoryUsageRateFlag"}, |
| 278 | {187, nullptr, "GetTouchScreenMode"}, | 278 | {187, &ISystemSettingsServer::GetTouchScreenMode, "GetTouchScreenMode"}, |
| 279 | {188, nullptr, "SetTouchScreenMode"}, | 279 | {188, &ISystemSettingsServer::SetTouchScreenMode, "SetTouchScreenMode"}, |
| 280 | {189, nullptr, "GetButtonConfigSettingsFull"}, | 280 | {189, nullptr, "GetButtonConfigSettingsFull"}, |
| 281 | {190, nullptr, "SetButtonConfigSettingsFull"}, | 281 | {190, nullptr, "SetButtonConfigSettingsFull"}, |
| 282 | {191, nullptr, "GetButtonConfigSettingsEmbedded"}, | 282 | {191, nullptr, "GetButtonConfigSettingsEmbedded"}, |
| @@ -1395,6 +1395,28 @@ void ISystemSettingsServer::GetHomeMenuSchemeModel(HLERequestContext& ctx) { | |||
| 1395 | rb.Push(0); | 1395 | rb.Push(0); |
| 1396 | } | 1396 | } |
| 1397 | 1397 | ||
| 1398 | void ISystemSettingsServer::GetTouchScreenMode(HLERequestContext& ctx) { | ||
| 1399 | TouchScreenMode touch_screen_mode{}; | ||
| 1400 | auto res = GetTouchScreenMode(touch_screen_mode); | ||
| 1401 | |||
| 1402 | LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode); | ||
| 1403 | |||
| 1404 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 1405 | rb.Push(res); | ||
| 1406 | rb.PushEnum(touch_screen_mode); | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | void ISystemSettingsServer::SetTouchScreenMode(HLERequestContext& ctx) { | ||
| 1410 | IPC::RequestParser rp{ctx}; | ||
| 1411 | const auto touch_screen_mode = rp.PopEnum<TouchScreenMode>(); | ||
| 1412 | auto res = SetTouchScreenMode(touch_screen_mode); | ||
| 1413 | |||
| 1414 | LOG_INFO(Service_SET, "called, touch_screen_mode={}", touch_screen_mode); | ||
| 1415 | |||
| 1416 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 1417 | rb.Push(res); | ||
| 1418 | } | ||
| 1419 | |||
| 1398 | void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) { | 1420 | void ISystemSettingsServer::GetFieldTestingFlag(HLERequestContext& ctx) { |
| 1399 | LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag); | 1421 | LOG_INFO(Service_SET, "called, field_testing_flag={}", m_system_settings.field_testing_flag); |
| 1400 | 1422 | ||
| @@ -1670,4 +1692,15 @@ Result ISystemSettingsServer::SetUserSystemClockAutomaticCorrectionUpdatedTime( | |||
| 1670 | R_SUCCEED(); | 1692 | R_SUCCEED(); |
| 1671 | } | 1693 | } |
| 1672 | 1694 | ||
| 1695 | Result ISystemSettingsServer::GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const { | ||
| 1696 | touch_screen_mode = m_system_settings.touch_screen_mode; | ||
| 1697 | R_SUCCEED(); | ||
| 1698 | } | ||
| 1699 | |||
| 1700 | Result ISystemSettingsServer::SetTouchScreenMode(TouchScreenMode touch_screen_mode) { | ||
| 1701 | m_system_settings.touch_screen_mode = touch_screen_mode; | ||
| 1702 | SetSaveNeeded(); | ||
| 1703 | R_SUCCEED(); | ||
| 1704 | } | ||
| 1705 | |||
| 1673 | } // namespace Service::Set | 1706 | } // 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: | |||
| 74 | Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; | 74 | Service::PSC::Time::SteadyClockTimePoint& out_time_point) const; |
| 75 | Result SetUserSystemClockAutomaticCorrectionUpdatedTime( | 75 | Result SetUserSystemClockAutomaticCorrectionUpdatedTime( |
| 76 | const Service::PSC::Time::SteadyClockTimePoint& time_point); | 76 | const Service::PSC::Time::SteadyClockTimePoint& time_point); |
| 77 | Result GetTouchScreenMode(TouchScreenMode& touch_screen_mode) const; | ||
| 78 | Result SetTouchScreenMode(TouchScreenMode touch_screen_mode); | ||
| 77 | 79 | ||
| 78 | private: | 80 | private: |
| 79 | void SetLanguageCode(HLERequestContext& ctx); | 81 | void SetLanguageCode(HLERequestContext& ctx); |
| @@ -154,6 +156,8 @@ private: | |||
| 154 | void GetChineseTraditionalInputMethod(HLERequestContext& ctx); | 156 | void GetChineseTraditionalInputMethod(HLERequestContext& ctx); |
| 155 | void GetHomeMenuScheme(HLERequestContext& ctx); | 157 | void GetHomeMenuScheme(HLERequestContext& ctx); |
| 156 | void GetHomeMenuSchemeModel(HLERequestContext& ctx); | 158 | void GetHomeMenuSchemeModel(HLERequestContext& ctx); |
| 159 | void GetTouchScreenMode(HLERequestContext& ctx); | ||
| 160 | void SetTouchScreenMode(HLERequestContext& ctx); | ||
| 157 | void GetFieldTestingFlag(HLERequestContext& ctx); | 161 | void GetFieldTestingFlag(HLERequestContext& ctx); |
| 158 | void GetPanelCrcMode(HLERequestContext& ctx); | 162 | void GetPanelCrcMode(HLERequestContext& ctx); |
| 159 | void SetPanelCrcMode(HLERequestContext& ctx); | 163 | 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 @@ | |||
| 16 | #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" | 16 | #include "core/hle/service/nvnflinger/buffer_queue_consumer.h" |
| 17 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" | 17 | #include "core/hle/service/nvnflinger/buffer_queue_core.h" |
| 18 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" | 18 | #include "core/hle/service/nvnflinger/buffer_queue_producer.h" |
| 19 | #include "core/hle/service/nvnflinger/hardware_composer.h" | ||
| 19 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" | 20 | #include "core/hle/service/nvnflinger/hos_binder_driver_server.h" |
| 20 | #include "core/hle/service/vi/display/vi_display.h" | 21 | #include "core/hle/service/vi/display/vi_display.h" |
| 21 | #include "core/hle/service/vi/layer/vi_layer.h" | 22 | #include "core/hle/service/vi/layer/vi_layer.h" |
| @@ -43,6 +44,7 @@ Display::Display(u64 id, std::string name_, | |||
| 43 | KernelHelpers::ServiceContext& service_context_, Core::System& system_) | 44 | KernelHelpers::ServiceContext& service_context_, Core::System& system_) |
| 44 | : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, | 45 | : display_id{id}, name{std::move(name_)}, hos_binder_driver_server{hos_binder_driver_server_}, |
| 45 | service_context{service_context_} { | 46 | service_context{service_context_} { |
| 47 | hardware_composer = std::make_unique<Nvnflinger::HardwareComposer>(); | ||
| 46 | vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); | 48 | vsync_event = service_context.CreateEvent(fmt::format("Display VSync Event {}", id)); |
| 47 | } | 49 | } |
| 48 | 50 | ||
| @@ -81,8 +83,6 @@ void Display::SignalVSyncEvent() { | |||
| 81 | 83 | ||
| 82 | void Display::CreateLayer(u64 layer_id, u32 binder_id, | 84 | void Display::CreateLayer(u64 layer_id, u32 binder_id, |
| 83 | Service::Nvidia::NvCore::Container& nv_core) { | 85 | Service::Nvidia::NvCore::Container& nv_core) { |
| 84 | ASSERT_MSG(layers.empty(), "Only one layer is supported per display at the moment"); | ||
| 85 | |||
| 86 | auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); | 86 | auto [core, producer, consumer] = CreateBufferQueue(service_context, nv_core.GetNvMapFile()); |
| 87 | 87 | ||
| 88 | auto buffer_item_consumer = std::make_shared<android::BufferItemConsumer>(std::move(consumer)); | 88 | 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 @@ | |||
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "core/hle/result.h" | 12 | #include "core/hle/result.h" |
| 13 | 13 | ||
| 14 | namespace Core { | ||
| 15 | class System; | ||
| 16 | } | ||
| 17 | |||
| 14 | namespace Kernel { | 18 | namespace Kernel { |
| 15 | class KEvent; | 19 | class KEvent; |
| 16 | } | 20 | class KReadableEvent; |
| 21 | } // namespace Kernel | ||
| 17 | 22 | ||
| 18 | namespace Service::android { | 23 | namespace Service::android { |
| 19 | class BufferQueueProducer; | 24 | class BufferQueueProducer; |
| @@ -24,8 +29,9 @@ class ServiceContext; | |||
| 24 | } | 29 | } |
| 25 | 30 | ||
| 26 | namespace Service::Nvnflinger { | 31 | namespace Service::Nvnflinger { |
| 32 | class HardwareComposer; | ||
| 27 | class HosBinderDriverServer; | 33 | class HosBinderDriverServer; |
| 28 | } | 34 | } // namespace Service::Nvnflinger |
| 29 | 35 | ||
| 30 | namespace Service::Nvidia::NvCore { | 36 | namespace Service::Nvidia::NvCore { |
| 31 | class Container; | 37 | class Container; |
| @@ -118,6 +124,10 @@ public: | |||
| 118 | /// | 124 | /// |
| 119 | const Layer* FindLayer(u64 layer_id) const; | 125 | const Layer* FindLayer(u64 layer_id) const; |
| 120 | 126 | ||
| 127 | Nvnflinger::HardwareComposer& GetComposer() const { | ||
| 128 | return *hardware_composer; | ||
| 129 | } | ||
| 130 | |||
| 121 | private: | 131 | private: |
| 122 | u64 display_id; | 132 | u64 display_id; |
| 123 | std::string name; | 133 | std::string name; |
| @@ -125,6 +135,7 @@ private: | |||
| 125 | KernelHelpers::ServiceContext& service_context; | 135 | KernelHelpers::ServiceContext& service_context; |
| 126 | 136 | ||
| 127 | std::vector<std::unique_ptr<Layer>> layers; | 137 | std::vector<std::unique_ptr<Layer>> layers; |
| 138 | std::unique_ptr<Nvnflinger::HardwareComposer> hardware_composer; | ||
| 128 | Kernel::KEvent* vsync_event{}; | 139 | Kernel::KEvent* vsync_event{}; |
| 129 | bool is_abandoned{}; | 140 | bool is_abandoned{}; |
| 130 | }; | 141 | }; |
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: | |||
| 195 | void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { | 195 | void GetSharedBufferMemoryHandleId(HLERequestContext& ctx) { |
| 196 | IPC::RequestParser rp{ctx}; | 196 | IPC::RequestParser rp{ctx}; |
| 197 | const u64 buffer_id = rp.PopRaw<u64>(); | 197 | const u64 buffer_id = rp.PopRaw<u64>(); |
| 198 | const u64 aruid = ctx.GetPID(); | ||
| 198 | 199 | ||
| 199 | LOG_INFO(Service_VI, "called. buffer_id={:#x}", buffer_id); | 200 | LOG_INFO(Service_VI, "called. buffer_id={:#x}, aruid={:#x}", buffer_id, aruid); |
| 200 | 201 | ||
| 201 | struct OutputParameters { | 202 | struct OutputParameters { |
| 202 | s32 nvmap_handle; | 203 | s32 nvmap_handle; |
| @@ -206,7 +207,7 @@ private: | |||
| 206 | OutputParameters out{}; | 207 | OutputParameters out{}; |
| 207 | Nvnflinger::SharedMemoryPoolLayout layout{}; | 208 | Nvnflinger::SharedMemoryPoolLayout layout{}; |
| 208 | const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( | 209 | const auto result = nvnflinger.GetSystemBufferManager().GetSharedBufferMemoryHandleId( |
| 209 | &out.size, &out.nvmap_handle, &layout, buffer_id, 0); | 210 | &out.size, &out.nvmap_handle, &layout, buffer_id, aruid); |
| 210 | 211 | ||
| 211 | ctx.WriteBuffer(&layout, sizeof(layout)); | 212 | ctx.WriteBuffer(&layout, sizeof(layout)); |
| 212 | 213 | ||
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 @@ | |||
| 9 | #include "core/core_timing.h" | 9 | #include "core/core_timing.h" |
| 10 | #include "core/hle/kernel/k_page_table.h" | 10 | #include "core/hle/kernel/k_page_table.h" |
| 11 | #include "core/hle/kernel/k_process.h" | 11 | #include "core/hle/kernel/k_process.h" |
| 12 | #include "core/hle/kernel/k_process_page_table.h" | ||
| 12 | #include "core/hle/service/hid/hid_server.h" | 13 | #include "core/hle/service/hid/hid_server.h" |
| 13 | #include "core/hle/service/sm/sm.h" | 14 | #include "core/hle/service/sm/sm.h" |
| 14 | #include "core/memory.h" | 15 | #include "core/memory.h" |
| @@ -46,12 +47,23 @@ StandardVmCallbacks::StandardVmCallbacks(System& system_, const CheatProcessMeta | |||
| 46 | 47 | ||
| 47 | StandardVmCallbacks::~StandardVmCallbacks() = default; | 48 | StandardVmCallbacks::~StandardVmCallbacks() = default; |
| 48 | 49 | ||
| 49 | void StandardVmCallbacks::MemoryRead(VAddr address, void* data, u64 size) { | 50 | void StandardVmCallbacks::MemoryReadUnsafe(VAddr address, void* data, u64 size) { |
| 50 | system.ApplicationMemory().ReadBlock(SanitizeAddress(address), data, size); | 51 | // Return zero on invalid address |
| 52 | if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) { | ||
| 53 | std::memset(data, 0, size); | ||
| 54 | return; | ||
| 55 | } | ||
| 56 | |||
| 57 | system.ApplicationMemory().ReadBlock(address, data, size); | ||
| 51 | } | 58 | } |
| 52 | 59 | ||
| 53 | void StandardVmCallbacks::MemoryWrite(VAddr address, const void* data, u64 size) { | 60 | void StandardVmCallbacks::MemoryWriteUnsafe(VAddr address, const void* data, u64 size) { |
| 54 | system.ApplicationMemory().WriteBlock(SanitizeAddress(address), data, size); | 61 | // Skip invalid memory write address |
| 62 | if (!IsAddressInRange(address) || !system.ApplicationMemory().IsValidVirtualAddress(address)) { | ||
| 63 | return; | ||
| 64 | } | ||
| 65 | |||
| 66 | system.ApplicationMemory().WriteBlock(address, data, size); | ||
| 55 | } | 67 | } |
| 56 | 68 | ||
| 57 | u64 StandardVmCallbacks::HidKeysDown() { | 69 | u64 StandardVmCallbacks::HidKeysDown() { |
| @@ -81,21 +93,25 @@ void StandardVmCallbacks::CommandLog(std::string_view data) { | |||
| 81 | data.back() == '\n' ? data.substr(0, data.size() - 1) : data); | 93 | data.back() == '\n' ? data.substr(0, data.size() - 1) : data); |
| 82 | } | 94 | } |
| 83 | 95 | ||
| 84 | VAddr StandardVmCallbacks::SanitizeAddress(VAddr in) const { | 96 | bool StandardVmCallbacks::IsAddressInRange(VAddr in) const { |
| 85 | if ((in < metadata.main_nso_extents.base || | 97 | if ((in < metadata.main_nso_extents.base || |
| 86 | in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && | 98 | in >= metadata.main_nso_extents.base + metadata.main_nso_extents.size) && |
| 87 | (in < metadata.heap_extents.base || | 99 | (in < metadata.heap_extents.base || |
| 88 | in >= metadata.heap_extents.base + metadata.heap_extents.size)) { | 100 | in >= metadata.heap_extents.base + metadata.heap_extents.size) && |
| 89 | LOG_ERROR(CheatEngine, | 101 | (in < metadata.alias_extents.base || |
| 102 | in >= metadata.heap_extents.base + metadata.alias_extents.size) && | ||
| 103 | (in < metadata.aslr_extents.base || | ||
| 104 | in >= metadata.heap_extents.base + metadata.aslr_extents.size)) { | ||
| 105 | LOG_DEBUG(CheatEngine, | ||
| 90 | "Cheat attempting to access memory at invalid address={:016X}, if this " | 106 | "Cheat attempting to access memory at invalid address={:016X}, if this " |
| 91 | "persists, " | 107 | "persists, " |
| 92 | "the cheat may be incorrect. However, this may be normal early in execution if " | 108 | "the cheat may be incorrect. However, this may be normal early in execution if " |
| 93 | "the game has not properly set up yet.", | 109 | "the game has not properly set up yet.", |
| 94 | in); | 110 | in); |
| 95 | return 0; ///< Invalid addresses will hard crash | 111 | return false; ///< Invalid addresses will hard crash |
| 96 | } | 112 | } |
| 97 | 113 | ||
| 98 | return in; | 114 | return true; |
| 99 | } | 115 | } |
| 100 | 116 | ||
| 101 | CheatParser::~CheatParser() = default; | 117 | CheatParser::~CheatParser() = default; |
| @@ -211,16 +227,14 @@ void CheatEngine::Initialize() { | |||
| 211 | .base = GetInteger(page_table.GetHeapRegionStart()), | 227 | .base = GetInteger(page_table.GetHeapRegionStart()), |
| 212 | .size = page_table.GetHeapRegionSize(), | 228 | .size = page_table.GetHeapRegionSize(), |
| 213 | }; | 229 | }; |
| 214 | 230 | metadata.aslr_extents = { | |
| 215 | metadata.address_space_extents = { | ||
| 216 | .base = GetInteger(page_table.GetAddressSpaceStart()), | ||
| 217 | .size = page_table.GetAddressSpaceSize(), | ||
| 218 | }; | ||
| 219 | |||
| 220 | metadata.alias_extents = { | ||
| 221 | .base = GetInteger(page_table.GetAliasCodeRegionStart()), | 231 | .base = GetInteger(page_table.GetAliasCodeRegionStart()), |
| 222 | .size = page_table.GetAliasCodeRegionSize(), | 232 | .size = page_table.GetAliasCodeRegionSize(), |
| 223 | }; | 233 | }; |
| 234 | metadata.alias_extents = { | ||
| 235 | .base = GetInteger(page_table.GetAliasRegionStart()), | ||
| 236 | .size = page_table.GetAliasRegionSize(), | ||
| 237 | }; | ||
| 224 | 238 | ||
| 225 | is_pending_reload.exchange(true); | 239 | is_pending_reload.exchange(true); |
| 226 | } | 240 | } |
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: | |||
| 27 | StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_); | 27 | StandardVmCallbacks(System& system_, const CheatProcessMetadata& metadata_); |
| 28 | ~StandardVmCallbacks() override; | 28 | ~StandardVmCallbacks() override; |
| 29 | 29 | ||
| 30 | void MemoryRead(VAddr address, void* data, u64 size) override; | 30 | void MemoryReadUnsafe(VAddr address, void* data, u64 size) override; |
| 31 | void MemoryWrite(VAddr address, const void* data, u64 size) override; | 31 | void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) override; |
| 32 | u64 HidKeysDown() override; | 32 | u64 HidKeysDown() override; |
| 33 | void DebugLog(u8 id, u64 value) override; | 33 | void DebugLog(u8 id, u64 value) override; |
| 34 | void CommandLog(std::string_view data) override; | 34 | void CommandLog(std::string_view data) override; |
| 35 | 35 | ||
| 36 | private: | 36 | private: |
| 37 | VAddr SanitizeAddress(VAddr address) const; | 37 | bool IsAddressInRange(VAddr address) const; |
| 38 | 38 | ||
| 39 | const CheatProcessMetadata& metadata; | 39 | const CheatProcessMetadata& metadata; |
| 40 | System& system; | 40 | Core::System& system; |
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | // Intermediary class that parses a text file or other disk format for storing cheats into a | 43 | // 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 { | |||
| 18 | MemoryRegionExtents main_nso_extents{}; | 18 | MemoryRegionExtents main_nso_extents{}; |
| 19 | MemoryRegionExtents heap_extents{}; | 19 | MemoryRegionExtents heap_extents{}; |
| 20 | MemoryRegionExtents alias_extents{}; | 20 | MemoryRegionExtents alias_extents{}; |
| 21 | MemoryRegionExtents address_space_extents{}; | 21 | MemoryRegionExtents aslr_extents{}; |
| 22 | std::array<u8, 0x20> main_nso_build_id{}; | 22 | std::array<u8, 0x20> main_nso_build_id{}; |
| 23 | }; | 23 | }; |
| 24 | 24 | ||
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) { | |||
| 322 | } break; | 322 | } break; |
| 323 | case CheatVmOpcodeType::EndConditionalBlock: { | 323 | case CheatVmOpcodeType::EndConditionalBlock: { |
| 324 | // 20000000 | 324 | // 20000000 |
| 325 | // There's actually nothing left to process here! | 325 | opcode.opcode = EndConditionalOpcode{ |
| 326 | opcode.opcode = EndConditionalOpcode{}; | 326 | .is_else = ((first_dword >> 24) & 0xf) == 1, |
| 327 | }; | ||
| 327 | } break; | 328 | } break; |
| 328 | case CheatVmOpcodeType::ControlLoop: { | 329 | case CheatVmOpcodeType::ControlLoop: { |
| 329 | // 300R0000 VVVVVVVV | 330 | // 300R0000 VVVVVVVV |
| @@ -555,6 +556,18 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 555 | .idx = first_dword & 0xF, | 556 | .idx = first_dword & 0xF, |
| 556 | }; | 557 | }; |
| 557 | } break; | 558 | } break; |
| 559 | case CheatVmOpcodeType::PauseProcess: { | ||
| 560 | /* FF0????? */ | ||
| 561 | /* FF0 = opcode 0xFF0 */ | ||
| 562 | /* Pauses the current process. */ | ||
| 563 | opcode.opcode = PauseProcessOpcode{}; | ||
| 564 | } break; | ||
| 565 | case CheatVmOpcodeType::ResumeProcess: { | ||
| 566 | /* FF0????? */ | ||
| 567 | /* FF0 = opcode 0xFF0 */ | ||
| 568 | /* Pauses the current process. */ | ||
| 569 | opcode.opcode = ResumeProcessOpcode{}; | ||
| 570 | } break; | ||
| 558 | case CheatVmOpcodeType::DebugLog: { | 571 | case CheatVmOpcodeType::DebugLog: { |
| 559 | // FFFTIX## | 572 | // FFFTIX## |
| 560 | // FFFTI0Ma aaaaaaaa | 573 | // FFFTI0Ma aaaaaaaa |
| @@ -621,7 +634,7 @@ bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { | |||
| 621 | return valid; | 634 | return valid; |
| 622 | } | 635 | } |
| 623 | 636 | ||
| 624 | void DmntCheatVm::SkipConditionalBlock() { | 637 | void DmntCheatVm::SkipConditionalBlock(bool is_if) { |
| 625 | if (condition_depth > 0) { | 638 | if (condition_depth > 0) { |
| 626 | // We want to continue until we're out of the current block. | 639 | // We want to continue until we're out of the current block. |
| 627 | const std::size_t desired_depth = condition_depth - 1; | 640 | const std::size_t desired_depth = condition_depth - 1; |
| @@ -637,8 +650,12 @@ void DmntCheatVm::SkipConditionalBlock() { | |||
| 637 | // We also support nesting of conditional blocks, and Gateway does not. | 650 | // We also support nesting of conditional blocks, and Gateway does not. |
| 638 | if (skip_opcode.begin_conditional_block) { | 651 | if (skip_opcode.begin_conditional_block) { |
| 639 | condition_depth++; | 652 | condition_depth++; |
| 640 | } else if (std::holds_alternative<EndConditionalOpcode>(skip_opcode.opcode)) { | 653 | } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&skip_opcode.opcode)) { |
| 641 | condition_depth--; | 654 | if (!end_cond->is_else) { |
| 655 | condition_depth--; | ||
| 656 | } else if (is_if && condition_depth - 1 == desired_depth) { | ||
| 657 | break; | ||
| 658 | } | ||
| 642 | } | 659 | } |
| 643 | } | 660 | } |
| 644 | } else { | 661 | } else { |
| @@ -675,6 +692,10 @@ u64 DmntCheatVm::GetCheatProcessAddress(const CheatProcessMetadata& metadata, | |||
| 675 | return metadata.main_nso_extents.base + rel_address; | 692 | return metadata.main_nso_extents.base + rel_address; |
| 676 | case MemoryAccessType::Heap: | 693 | case MemoryAccessType::Heap: |
| 677 | return metadata.heap_extents.base + rel_address; | 694 | return metadata.heap_extents.base + rel_address; |
| 695 | case MemoryAccessType::Alias: | ||
| 696 | return metadata.alias_extents.base + rel_address; | ||
| 697 | case MemoryAccessType::Aslr: | ||
| 698 | return metadata.aslr_extents.base + rel_address; | ||
| 678 | } | 699 | } |
| 679 | } | 700 | } |
| 680 | 701 | ||
| @@ -682,7 +703,6 @@ void DmntCheatVm::ResetState() { | |||
| 682 | registers.fill(0); | 703 | registers.fill(0); |
| 683 | saved_values.fill(0); | 704 | saved_values.fill(0); |
| 684 | loop_tops.fill(0); | 705 | loop_tops.fill(0); |
| 685 | static_registers.fill(0); | ||
| 686 | instruction_ptr = 0; | 706 | instruction_ptr = 0; |
| 687 | condition_depth = 0; | 707 | condition_depth = 0; |
| 688 | decode_success = true; | 708 | decode_success = true; |
| @@ -753,7 +773,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 753 | case 2: | 773 | case 2: |
| 754 | case 4: | 774 | case 4: |
| 755 | case 8: | 775 | case 8: |
| 756 | callbacks->MemoryWrite(dst_address, &dst_value, store_static->bit_width); | 776 | callbacks->MemoryWriteUnsafe(dst_address, &dst_value, store_static->bit_width); |
| 757 | break; | 777 | break; |
| 758 | } | 778 | } |
| 759 | } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { | 779 | } else if (auto begin_cond = std::get_if<BeginConditionalOpcode>(&cur_opcode.opcode)) { |
| @@ -766,7 +786,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 766 | case 2: | 786 | case 2: |
| 767 | case 4: | 787 | case 4: |
| 768 | case 8: | 788 | case 8: |
| 769 | callbacks->MemoryRead(src_address, &src_value, begin_cond->bit_width); | 789 | callbacks->MemoryReadUnsafe(src_address, &src_value, begin_cond->bit_width); |
| 770 | break; | 790 | break; |
| 771 | } | 791 | } |
| 772 | // Check against condition. | 792 | // Check against condition. |
| @@ -794,13 +814,18 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 794 | } | 814 | } |
| 795 | // Skip conditional block if condition not met. | 815 | // Skip conditional block if condition not met. |
| 796 | if (!cond_met) { | 816 | if (!cond_met) { |
| 797 | SkipConditionalBlock(); | 817 | SkipConditionalBlock(true); |
| 798 | } | 818 | } |
| 799 | } else if (std::holds_alternative<EndConditionalOpcode>(cur_opcode.opcode)) { | 819 | } else if (auto end_cond = std::get_if<EndConditionalOpcode>(&cur_opcode.opcode)) { |
| 800 | // Decrement the condition depth. | 820 | if (end_cond->is_else) { |
| 801 | // We will assume, graciously, that mismatched conditional block ends are a nop. | 821 | /* Skip to the end of the conditional block. */ |
| 802 | if (condition_depth > 0) { | 822 | this->SkipConditionalBlock(false); |
| 803 | condition_depth--; | 823 | } else { |
| 824 | /* Decrement the condition depth. */ | ||
| 825 | /* We will assume, graciously, that mismatched conditional block ends are a nop. */ | ||
| 826 | if (condition_depth > 0) { | ||
| 827 | condition_depth--; | ||
| 828 | } | ||
| 804 | } | 829 | } |
| 805 | } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { | 830 | } else if (auto ctrl_loop = std::get_if<ControlLoopOpcode>(&cur_opcode.opcode)) { |
| 806 | if (ctrl_loop->start_loop) { | 831 | if (ctrl_loop->start_loop) { |
| @@ -832,8 +857,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 832 | case 2: | 857 | case 2: |
| 833 | case 4: | 858 | case 4: |
| 834 | case 8: | 859 | case 8: |
| 835 | callbacks->MemoryRead(src_address, ®isters[ldr_memory->reg_index], | 860 | callbacks->MemoryReadUnsafe(src_address, ®isters[ldr_memory->reg_index], |
| 836 | ldr_memory->bit_width); | 861 | ldr_memory->bit_width); |
| 837 | break; | 862 | break; |
| 838 | } | 863 | } |
| 839 | } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { | 864 | } else if (auto str_static = std::get_if<StoreStaticToAddressOpcode>(&cur_opcode.opcode)) { |
| @@ -849,7 +874,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 849 | case 2: | 874 | case 2: |
| 850 | case 4: | 875 | case 4: |
| 851 | case 8: | 876 | case 8: |
| 852 | callbacks->MemoryWrite(dst_address, &dst_value, str_static->bit_width); | 877 | callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_static->bit_width); |
| 853 | break; | 878 | break; |
| 854 | } | 879 | } |
| 855 | // Increment register if relevant. | 880 | // Increment register if relevant. |
| @@ -908,7 +933,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 908 | // Check for keypress. | 933 | // Check for keypress. |
| 909 | if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { | 934 | if ((begin_keypress_cond->key_mask & kDown) != begin_keypress_cond->key_mask) { |
| 910 | // Keys not pressed. Skip conditional block. | 935 | // Keys not pressed. Skip conditional block. |
| 911 | SkipConditionalBlock(); | 936 | SkipConditionalBlock(true); |
| 912 | } | 937 | } |
| 913 | } else if (auto perform_math_reg = | 938 | } else if (auto perform_math_reg = |
| 914 | std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { | 939 | std::get_if<PerformArithmeticRegisterOpcode>(&cur_opcode.opcode)) { |
| @@ -1007,7 +1032,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1007 | case 2: | 1032 | case 2: |
| 1008 | case 4: | 1033 | case 4: |
| 1009 | case 8: | 1034 | case 8: |
| 1010 | callbacks->MemoryWrite(dst_address, &dst_value, str_register->bit_width); | 1035 | callbacks->MemoryWriteUnsafe(dst_address, &dst_value, str_register->bit_width); |
| 1011 | break; | 1036 | break; |
| 1012 | } | 1037 | } |
| 1013 | 1038 | ||
| @@ -1086,7 +1111,8 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1086 | case 2: | 1111 | case 2: |
| 1087 | case 4: | 1112 | case 4: |
| 1088 | case 8: | 1113 | case 8: |
| 1089 | callbacks->MemoryRead(cond_address, &cond_value, begin_reg_cond->bit_width); | 1114 | callbacks->MemoryReadUnsafe(cond_address, &cond_value, |
| 1115 | begin_reg_cond->bit_width); | ||
| 1090 | break; | 1116 | break; |
| 1091 | } | 1117 | } |
| 1092 | } | 1118 | } |
| @@ -1116,7 +1142,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1116 | 1142 | ||
| 1117 | // Skip conditional block if condition not met. | 1143 | // Skip conditional block if condition not met. |
| 1118 | if (!cond_met) { | 1144 | if (!cond_met) { |
| 1119 | SkipConditionalBlock(); | 1145 | SkipConditionalBlock(true); |
| 1120 | } | 1146 | } |
| 1121 | } else if (auto save_restore_reg = | 1147 | } else if (auto save_restore_reg = |
| 1122 | std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { | 1148 | std::get_if<SaveRestoreRegisterOpcode>(&cur_opcode.opcode)) { |
| @@ -1178,6 +1204,10 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1178 | // Store a register to a static register. | 1204 | // Store a register to a static register. |
| 1179 | static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; | 1205 | static_registers[rw_static_reg->static_idx] = registers[rw_static_reg->idx]; |
| 1180 | } | 1206 | } |
| 1207 | } else if (std::holds_alternative<PauseProcessOpcode>(cur_opcode.opcode)) { | ||
| 1208 | // TODO: Pause cheat process | ||
| 1209 | } else if (std::holds_alternative<ResumeProcessOpcode>(cur_opcode.opcode)) { | ||
| 1210 | // TODO: Resume cheat process | ||
| 1181 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { | 1211 | } else if (auto debug_log = std::get_if<DebugLogOpcode>(&cur_opcode.opcode)) { |
| 1182 | // Read value from memory. | 1212 | // Read value from memory. |
| 1183 | u64 log_value = 0; | 1213 | u64 log_value = 0; |
| @@ -1224,7 +1254,7 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) { | |||
| 1224 | case 2: | 1254 | case 2: |
| 1225 | case 4: | 1255 | case 4: |
| 1226 | case 8: | 1256 | case 8: |
| 1227 | callbacks->MemoryRead(val_address, &log_value, debug_log->bit_width); | 1257 | callbacks->MemoryReadUnsafe(val_address, &log_value, debug_log->bit_width); |
| 1228 | break; | 1258 | break; |
| 1229 | } | 1259 | } |
| 1230 | } | 1260 | } |
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 { | |||
| 42 | DoubleExtendedWidth = 0xF0, | 42 | DoubleExtendedWidth = 0xF0, |
| 43 | 43 | ||
| 44 | // Double-extended width opcodes. | 44 | // Double-extended width opcodes. |
| 45 | PauseProcess = 0xFF0, | ||
| 46 | ResumeProcess = 0xFF1, | ||
| 45 | DebugLog = 0xFFF, | 47 | DebugLog = 0xFFF, |
| 46 | }; | 48 | }; |
| 47 | 49 | ||
| 48 | enum class MemoryAccessType : u32 { | 50 | enum class MemoryAccessType : u32 { |
| 49 | MainNso = 0, | 51 | MainNso = 0, |
| 50 | Heap = 1, | 52 | Heap = 1, |
| 53 | Alias = 2, | ||
| 54 | Aslr = 3, | ||
| 51 | }; | 55 | }; |
| 52 | 56 | ||
| 53 | enum class ConditionalComparisonType : u32 { | 57 | enum class ConditionalComparisonType : u32 { |
| @@ -131,7 +135,9 @@ struct BeginConditionalOpcode { | |||
| 131 | VmInt value{}; | 135 | VmInt value{}; |
| 132 | }; | 136 | }; |
| 133 | 137 | ||
| 134 | struct EndConditionalOpcode {}; | 138 | struct EndConditionalOpcode { |
| 139 | bool is_else; | ||
| 140 | }; | ||
| 135 | 141 | ||
| 136 | struct ControlLoopOpcode { | 142 | struct ControlLoopOpcode { |
| 137 | bool start_loop{}; | 143 | bool start_loop{}; |
| @@ -222,6 +228,10 @@ struct ReadWriteStaticRegisterOpcode { | |||
| 222 | u32 idx{}; | 228 | u32 idx{}; |
| 223 | }; | 229 | }; |
| 224 | 230 | ||
| 231 | struct PauseProcessOpcode {}; | ||
| 232 | |||
| 233 | struct ResumeProcessOpcode {}; | ||
| 234 | |||
| 225 | struct DebugLogOpcode { | 235 | struct DebugLogOpcode { |
| 226 | u32 bit_width{}; | 236 | u32 bit_width{}; |
| 227 | u32 log_id{}; | 237 | u32 log_id{}; |
| @@ -244,8 +254,8 @@ struct CheatVmOpcode { | |||
| 244 | PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, | 254 | PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, |
| 245 | PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, | 255 | PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, |
| 246 | BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, | 256 | BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, |
| 247 | SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, DebugLogOpcode, | 257 | SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode, |
| 248 | UnrecognizedInstruction> | 258 | ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction> |
| 249 | opcode{}; | 259 | opcode{}; |
| 250 | }; | 260 | }; |
| 251 | 261 | ||
| @@ -256,8 +266,8 @@ public: | |||
| 256 | public: | 266 | public: |
| 257 | virtual ~Callbacks(); | 267 | virtual ~Callbacks(); |
| 258 | 268 | ||
| 259 | virtual void MemoryRead(VAddr address, void* data, u64 size) = 0; | 269 | virtual void MemoryReadUnsafe(VAddr address, void* data, u64 size) = 0; |
| 260 | virtual void MemoryWrite(VAddr address, const void* data, u64 size) = 0; | 270 | virtual void MemoryWriteUnsafe(VAddr address, const void* data, u64 size) = 0; |
| 261 | 271 | ||
| 262 | virtual u64 HidKeysDown() = 0; | 272 | virtual u64 HidKeysDown() = 0; |
| 263 | 273 | ||
| @@ -296,7 +306,7 @@ private: | |||
| 296 | std::array<std::size_t, NumRegisters> loop_tops{}; | 306 | std::array<std::size_t, NumRegisters> loop_tops{}; |
| 297 | 307 | ||
| 298 | bool DecodeNextOpcode(CheatVmOpcode& out); | 308 | bool DecodeNextOpcode(CheatVmOpcode& out); |
| 299 | void SkipConditionalBlock(); | 309 | void SkipConditionalBlock(bool is_if); |
| 300 | void ResetState(); | 310 | void ResetState(); |
| 301 | 311 | ||
| 302 | // For implementing the DebugLog opcode. | 312 | // 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() { | |||
| 190 | Settings::values.touchscreen.rotation_angle = | 190 | Settings::values.touchscreen.rotation_angle = |
| 191 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); | 191 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_angle"), 0)); |
| 192 | Settings::values.touchscreen.diameter_x = | 192 | Settings::values.touchscreen.diameter_x = |
| 193 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 15)); | 193 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_x"), 90)); |
| 194 | Settings::values.touchscreen.diameter_y = | 194 | Settings::values.touchscreen.diameter_y = |
| 195 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 15)); | 195 | static_cast<u32>(ReadIntegerSetting(std::string("touchscreen_diameter_y"), 90)); |
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | void Config::ReadAudioValues() { | 198 | void Config::ReadAudioValues() { |
| @@ -478,9 +478,9 @@ void Config::SaveTouchscreenValues() { | |||
| 478 | WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, | 478 | WriteIntegerSetting(std::string("touchscreen_angle"), touchscreen.rotation_angle, |
| 479 | std::make_optional(static_cast<u32>(0))); | 479 | std::make_optional(static_cast<u32>(0))); |
| 480 | WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, | 480 | WriteIntegerSetting(std::string("touchscreen_diameter_x"), touchscreen.diameter_x, |
| 481 | std::make_optional(static_cast<u32>(15))); | 481 | std::make_optional(static_cast<u32>(90))); |
| 482 | WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, | 482 | WriteIntegerSetting(std::string("touchscreen_diameter_y"), touchscreen.diameter_y, |
| 483 | std::make_optional(static_cast<u32>(15))); | 483 | std::make_optional(static_cast<u32>(90))); |
| 484 | } | 484 | } |
| 485 | 485 | ||
| 486 | void Config::SaveMotionTouchValues() { | 486 | 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 | |||
| 99 | resources/system_buttons/system_button_types.h | 99 | resources/system_buttons/system_button_types.h |
| 100 | resources/touch_screen/gesture.cpp | 100 | resources/touch_screen/gesture.cpp |
| 101 | resources/touch_screen/gesture.h | 101 | resources/touch_screen/gesture.h |
| 102 | resources/touch_screen/gesture_types.h | 102 | resources/touch_screen/gesture_handler.cpp |
| 103 | resources/touch_screen/gesture_handler.h | ||
| 103 | resources/touch_screen/touch_screen.cpp | 104 | resources/touch_screen/touch_screen.cpp |
| 104 | resources/touch_screen/touch_screen.h | 105 | resources/touch_screen/touch_screen.h |
| 106 | resources/touch_screen/touch_screen_driver.cpp | ||
| 107 | resources/touch_screen/touch_screen_driver.h | ||
| 108 | resources/touch_screen/touch_screen_resource.cpp | ||
| 109 | resources/touch_screen/touch_screen_resource.h | ||
| 105 | resources/touch_screen/touch_types.h | 110 | resources/touch_screen/touch_types.h |
| 106 | resources/unique_pad/unique_pad.cpp | 111 | resources/unique_pad/unique_pad.cpp |
| 107 | resources/unique_pad/unique_pad.h | 112 | 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 @@ | |||
| 8 | namespace Service::HID { | 8 | namespace Service::HID { |
| 9 | 9 | ||
| 10 | constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; | 10 | constexpr Result PalmaResultSuccess{ErrorModule::HID, 0}; |
| 11 | |||
| 12 | constexpr Result ResultTouchNotInitialized{ErrorModule::HID, 41}; | ||
| 13 | constexpr Result ResultTouchOverflow{ErrorModule::HID, 42}; | ||
| 14 | |||
| 11 | constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; | 15 | constexpr Result NpadInvalidHandle{ErrorModule::HID, 100}; |
| 12 | constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; | 16 | constexpr Result NpadDeviceIndexOutOfRange{ErrorModule::HID, 107}; |
| 13 | 17 | ||
| @@ -23,6 +27,10 @@ constexpr Result InvalidSixAxisFusionRange{ErrorModule::HID, 423}; | |||
| 23 | constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461}; | 27 | constexpr Result ResultNfcIsNotReady{ErrorModule::HID, 461}; |
| 24 | constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464}; | 28 | constexpr Result ResultNfcXcdHandleIsNotInitialized{ErrorModule::HID, 464}; |
| 25 | constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501}; | 29 | constexpr Result ResultIrSensorIsNotReady{ErrorModule::HID, 501}; |
| 30 | |||
| 31 | constexpr Result ResultGestureOverflow{ErrorModule::HID, 522}; | ||
| 32 | constexpr Result ResultGestureNotInitialized{ErrorModule::HID, 523}; | ||
| 33 | |||
| 26 | constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541}; | 34 | constexpr Result ResultMcuIsNotReady{ErrorModule::HID, 541}; |
| 27 | 35 | ||
| 28 | constexpr Result NpadIsDualJoycon{ErrorModule::HID, 601}; | 36 | 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 { | |||
| 299 | Tight = 2, | 299 | Tight = 2, |
| 300 | }; | 300 | }; |
| 301 | 301 | ||
| 302 | // This is nn::settings::system::TouchScreenMode | ||
| 303 | enum class TouchScreenMode : u32 { | ||
| 304 | Stylus = 0, | ||
| 305 | Standard = 1, | ||
| 306 | }; | ||
| 307 | |||
| 308 | // This is nn::hid::TouchScreenModeForNx | 302 | // This is nn::hid::TouchScreenModeForNx |
| 309 | enum class TouchScreenModeForNx : u8 { | 303 | enum class TouchScreenModeForNx : u8 { |
| 310 | UseSystemSetting, | 304 | UseSystemSetting, |
| @@ -354,18 +348,6 @@ struct TouchAttribute { | |||
| 354 | }; | 348 | }; |
| 355 | static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); | 349 | static_assert(sizeof(TouchAttribute) == 0x4, "TouchAttribute is an invalid size"); |
| 356 | 350 | ||
| 357 | // This is nn::hid::TouchState | ||
| 358 | struct TouchState { | ||
| 359 | u64 delta_time{}; | ||
| 360 | TouchAttribute attribute{}; | ||
| 361 | u32 finger{}; | ||
| 362 | Common::Point<u32> position{}; | ||
| 363 | u32 diameter_x{}; | ||
| 364 | u32 diameter_y{}; | ||
| 365 | u32 rotation_angle{}; | ||
| 366 | }; | ||
| 367 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | ||
| 368 | |||
| 369 | struct TouchFinger { | 351 | struct TouchFinger { |
| 370 | u64 last_touch{}; | 352 | u64 last_touch{}; |
| 371 | Common::Point<float> position{}; | 353 | Common::Point<float> position{}; |
| @@ -756,4 +738,14 @@ struct UniquePadId { | |||
| 756 | }; | 738 | }; |
| 757 | static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); | 739 | static_assert(sizeof(UniquePadId) == 0x8, "UniquePadId is an invalid size"); |
| 758 | 740 | ||
| 741 | // This is nn::hid::system::FirmwareVersion | ||
| 742 | struct FirmwareVersion { | ||
| 743 | u8 major; | ||
| 744 | u8 minor; | ||
| 745 | u8 micro; | ||
| 746 | u8 revision; | ||
| 747 | std::array<char, 0xc> device_identifier; | ||
| 748 | }; | ||
| 749 | static_assert(sizeof(FirmwareVersion) == 0x10, "FirmwareVersion is an invalid size"); | ||
| 750 | |||
| 759 | } // namespace Core::HID | 751 | } // 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 | |||
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( | 147 | Core::IrSensor::ImageTransferProcessorState ImageTransferProcessor::GetState( |
| 148 | std::vector<u8>& data) const { | 148 | std::span<u8> data) const { |
| 149 | const auto size = GetDataSize(current_config.trimming_format); | 149 | const auto size = std::min(GetDataSize(current_config.trimming_format), data.size()); |
| 150 | data.resize(size); | ||
| 151 | system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); | 150 | system.ApplicationMemory().ReadBlock(transfer_memory, data.data(), size); |
| 152 | return processor_state; | 151 | return processor_state; |
| 153 | } | 152 | } |
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 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <span> | ||
| 7 | |||
| 6 | #include "common/typed_address.h" | 8 | #include "common/typed_address.h" |
| 7 | #include "hid_core/irsensor/irs_types.h" | 9 | #include "hid_core/irsensor/irs_types.h" |
| 8 | #include "hid_core/irsensor/processor_base.h" | 10 | #include "hid_core/irsensor/processor_base.h" |
| @@ -39,7 +41,7 @@ public: | |||
| 39 | // Transfer memory where the image data will be stored | 41 | // Transfer memory where the image data will be stored |
| 40 | void SetTransferMemoryAddress(Common::ProcessAddress t_mem); | 42 | void SetTransferMemoryAddress(Common::ProcessAddress t_mem); |
| 41 | 43 | ||
| 42 | Core::IrSensor::ImageTransferProcessorState GetState(std::vector<u8>& data) const; | 44 | Core::IrSensor::ImageTransferProcessorState GetState(std::span<u8> data) const; |
| 43 | 45 | ||
| 44 | private: | 46 | private: |
| 45 | // This is nn::irsensor::ImageTransferProcessorConfig | 47 | // 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 @@ | |||
| 15 | #include "hid_core/resources/applet_resource.h" | 15 | #include "hid_core/resources/applet_resource.h" |
| 16 | #include "hid_core/resources/debug_pad/debug_pad.h" | 16 | #include "hid_core/resources/debug_pad/debug_pad.h" |
| 17 | #include "hid_core/resources/digitizer/digitizer.h" | 17 | #include "hid_core/resources/digitizer/digitizer.h" |
| 18 | #include "hid_core/resources/hid_firmware_settings.h" | ||
| 18 | #include "hid_core/resources/keyboard/keyboard.h" | 19 | #include "hid_core/resources/keyboard/keyboard.h" |
| 19 | #include "hid_core/resources/mouse/debug_mouse.h" | 20 | #include "hid_core/resources/mouse/debug_mouse.h" |
| 20 | #include "hid_core/resources/mouse/mouse.h" | 21 | #include "hid_core/resources/mouse/mouse.h" |
| @@ -29,6 +30,8 @@ | |||
| 29 | #include "hid_core/resources/system_buttons/sleep_button.h" | 30 | #include "hid_core/resources/system_buttons/sleep_button.h" |
| 30 | #include "hid_core/resources/touch_screen/gesture.h" | 31 | #include "hid_core/resources/touch_screen/gesture.h" |
| 31 | #include "hid_core/resources/touch_screen/touch_screen.h" | 32 | #include "hid_core/resources/touch_screen/touch_screen.h" |
| 33 | #include "hid_core/resources/touch_screen/touch_screen_driver.h" | ||
| 34 | #include "hid_core/resources/touch_screen/touch_screen_resource.h" | ||
| 32 | #include "hid_core/resources/unique_pad/unique_pad.h" | 35 | #include "hid_core/resources/unique_pad/unique_pad.h" |
| 33 | #include "hid_core/resources/vibration/gc_vibration_device.h" | 36 | #include "hid_core/resources/vibration/gc_vibration_device.h" |
| 34 | #include "hid_core/resources/vibration/n64_vibration_device.h" | 37 | #include "hid_core/resources/vibration/n64_vibration_device.h" |
| @@ -45,12 +48,49 @@ constexpr auto default_update_ns = std::chrono::nanoseconds{4 * 1000 * 1000}; // | |||
| 45 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) | 48 | constexpr auto mouse_keyboard_update_ns = std::chrono::nanoseconds{8 * 1000 * 1000}; // (8ms, 125Hz) |
| 46 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) | 49 | constexpr auto motion_update_ns = std::chrono::nanoseconds{5 * 1000 * 1000}; // (5ms, 200Hz) |
| 47 | 50 | ||
| 48 | ResourceManager::ResourceManager(Core::System& system_) | 51 | ResourceManager::ResourceManager(Core::System& system_, |
| 49 | : system{system_}, service_context{system_, "hid"} { | 52 | std::shared_ptr<HidFirmwareSettings> settings) |
| 53 | : firmware_settings{settings}, system{system_}, service_context{system_, "hid"} { | ||
| 50 | applet_resource = std::make_shared<AppletResource>(system); | 54 | applet_resource = std::make_shared<AppletResource>(system); |
| 55 | |||
| 56 | // Register update callbacks | ||
| 57 | npad_update_event = Core::Timing::CreateEvent("HID::UpdatePadCallback", | ||
| 58 | [this](s64 time, std::chrono::nanoseconds ns_late) | ||
| 59 | -> std::optional<std::chrono::nanoseconds> { | ||
| 60 | UpdateNpad(ns_late); | ||
| 61 | return std::nullopt; | ||
| 62 | }); | ||
| 63 | default_update_event = Core::Timing::CreateEvent( | ||
| 64 | "HID::UpdateDefaultCallback", | ||
| 65 | [this](s64 time, | ||
| 66 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 67 | UpdateControllers(ns_late); | ||
| 68 | return std::nullopt; | ||
| 69 | }); | ||
| 70 | mouse_keyboard_update_event = Core::Timing::CreateEvent( | ||
| 71 | "HID::UpdateMouseKeyboardCallback", | ||
| 72 | [this](s64 time, | ||
| 73 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 74 | UpdateMouseKeyboard(ns_late); | ||
| 75 | return std::nullopt; | ||
| 76 | }); | ||
| 77 | motion_update_event = Core::Timing::CreateEvent( | ||
| 78 | "HID::UpdateMotionCallback", | ||
| 79 | [this](s64 time, | ||
| 80 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 81 | UpdateMotion(ns_late); | ||
| 82 | return std::nullopt; | ||
| 83 | }); | ||
| 51 | } | 84 | } |
| 52 | 85 | ||
| 53 | ResourceManager::~ResourceManager() = default; | 86 | ResourceManager::~ResourceManager() { |
| 87 | system.CoreTiming().UnscheduleEvent(npad_update_event); | ||
| 88 | system.CoreTiming().UnscheduleEvent(default_update_event); | ||
| 89 | system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event); | ||
| 90 | system.CoreTiming().UnscheduleEvent(motion_update_event); | ||
| 91 | system.CoreTiming().UnscheduleEvent(touch_update_event); | ||
| 92 | input_event->Finalize(); | ||
| 93 | }; | ||
| 54 | 94 | ||
| 55 | void ResourceManager::Initialize() { | 95 | void ResourceManager::Initialize() { |
| 56 | if (is_initialized) { | 96 | if (is_initialized) { |
| @@ -59,7 +99,9 @@ void ResourceManager::Initialize() { | |||
| 59 | 99 | ||
| 60 | system.HIDCore().ReloadInputDevices(); | 100 | system.HIDCore().ReloadInputDevices(); |
| 61 | 101 | ||
| 62 | handheld_config = std::make_shared<HandheldConfig>(); | 102 | input_event = service_context.CreateEvent("ResourceManager:InputEvent"); |
| 103 | |||
| 104 | InitializeHandheldConfig(); | ||
| 63 | InitializeHidCommonSampler(); | 105 | InitializeHidCommonSampler(); |
| 64 | InitializeTouchScreenSampler(); | 106 | InitializeTouchScreenSampler(); |
| 65 | InitializeConsoleSixAxisSampler(); | 107 | InitializeConsoleSixAxisSampler(); |
| @@ -154,6 +196,7 @@ Result ResourceManager::CreateAppletResource(u64 aruid) { | |||
| 154 | npad->Activate(); | 196 | npad->Activate(); |
| 155 | six_axis->Activate(); | 197 | six_axis->Activate(); |
| 156 | touch_screen->Activate(); | 198 | touch_screen->Activate(); |
| 199 | gesture->Activate(); | ||
| 157 | 200 | ||
| 158 | return GetNpad()->ActivateNpadResource(aruid); | 201 | return GetNpad()->ActivateNpadResource(aruid); |
| 159 | } | 202 | } |
| @@ -163,6 +206,17 @@ Result ResourceManager::CreateAppletResourceImpl(u64 aruid) { | |||
| 163 | return applet_resource->CreateAppletResource(aruid); | 206 | return applet_resource->CreateAppletResource(aruid); |
| 164 | } | 207 | } |
| 165 | 208 | ||
| 209 | void ResourceManager::InitializeHandheldConfig() { | ||
| 210 | handheld_config = std::make_shared<HandheldConfig>(); | ||
| 211 | handheld_config->is_handheld_hid_enabled = true; | ||
| 212 | handheld_config->is_joycon_rail_enabled = true; | ||
| 213 | handheld_config->is_force_handheld_style_vibration = false; | ||
| 214 | handheld_config->is_force_handheld = false; | ||
| 215 | if (firmware_settings->IsHandheldForced()) { | ||
| 216 | handheld_config->is_joycon_rail_enabled = false; | ||
| 217 | } | ||
| 218 | } | ||
| 219 | |||
| 166 | void ResourceManager::InitializeHidCommonSampler() { | 220 | void ResourceManager::InitializeHidCommonSampler() { |
| 167 | debug_pad = std::make_shared<DebugPad>(system.HIDCore()); | 221 | debug_pad = std::make_shared<DebugPad>(system.HIDCore()); |
| 168 | mouse = std::make_shared<Mouse>(system.HIDCore()); | 222 | mouse = std::make_shared<Mouse>(system.HIDCore()); |
| @@ -170,7 +224,6 @@ void ResourceManager::InitializeHidCommonSampler() { | |||
| 170 | keyboard = std::make_shared<Keyboard>(system.HIDCore()); | 224 | keyboard = std::make_shared<Keyboard>(system.HIDCore()); |
| 171 | unique_pad = std::make_shared<UniquePad>(system.HIDCore()); | 225 | unique_pad = std::make_shared<UniquePad>(system.HIDCore()); |
| 172 | npad = std::make_shared<NPad>(system.HIDCore(), service_context); | 226 | npad = std::make_shared<NPad>(system.HIDCore(), service_context); |
| 173 | gesture = std::make_shared<Gesture>(system.HIDCore()); | ||
| 174 | home_button = std::make_shared<HomeButton>(system.HIDCore()); | 227 | home_button = std::make_shared<HomeButton>(system.HIDCore()); |
| 175 | sleep_button = std::make_shared<SleepButton>(system.HIDCore()); | 228 | sleep_button = std::make_shared<SleepButton>(system.HIDCore()); |
| 176 | capture_button = std::make_shared<CaptureButton>(system.HIDCore()); | 229 | capture_button = std::make_shared<CaptureButton>(system.HIDCore()); |
| @@ -181,11 +234,13 @@ void ResourceManager::InitializeHidCommonSampler() { | |||
| 181 | 234 | ||
| 182 | debug_pad->SetAppletResource(applet_resource, &shared_mutex); | 235 | debug_pad->SetAppletResource(applet_resource, &shared_mutex); |
| 183 | digitizer->SetAppletResource(applet_resource, &shared_mutex); | 236 | digitizer->SetAppletResource(applet_resource, &shared_mutex); |
| 237 | unique_pad->SetAppletResource(applet_resource, &shared_mutex); | ||
| 184 | keyboard->SetAppletResource(applet_resource, &shared_mutex); | 238 | keyboard->SetAppletResource(applet_resource, &shared_mutex); |
| 185 | 239 | ||
| 186 | const auto settings = | 240 | const auto settings = |
| 187 | system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); | 241 | system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys", true); |
| 188 | npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, settings); | 242 | npad->SetNpadExternals(applet_resource, &shared_mutex, handheld_config, input_event, |
| 243 | &input_mutex, settings); | ||
| 189 | 244 | ||
| 190 | six_axis->SetAppletResource(applet_resource, &shared_mutex); | 245 | six_axis->SetAppletResource(applet_resource, &shared_mutex); |
| 191 | mouse->SetAppletResource(applet_resource, &shared_mutex); | 246 | mouse->SetAppletResource(applet_resource, &shared_mutex); |
| @@ -193,14 +248,36 @@ void ResourceManager::InitializeHidCommonSampler() { | |||
| 193 | home_button->SetAppletResource(applet_resource, &shared_mutex); | 248 | home_button->SetAppletResource(applet_resource, &shared_mutex); |
| 194 | sleep_button->SetAppletResource(applet_resource, &shared_mutex); | 249 | sleep_button->SetAppletResource(applet_resource, &shared_mutex); |
| 195 | capture_button->SetAppletResource(applet_resource, &shared_mutex); | 250 | capture_button->SetAppletResource(applet_resource, &shared_mutex); |
| 251 | |||
| 252 | system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); | ||
| 253 | system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, | ||
| 254 | default_update_event); | ||
| 255 | system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, | ||
| 256 | mouse_keyboard_update_event); | ||
| 257 | system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, | ||
| 258 | motion_update_event); | ||
| 196 | } | 259 | } |
| 197 | 260 | ||
| 198 | void ResourceManager::InitializeTouchScreenSampler() { | 261 | void ResourceManager::InitializeTouchScreenSampler() { |
| 199 | gesture = std::make_shared<Gesture>(system.HIDCore()); | 262 | // This is nn.hid.TouchScreenSampler |
| 200 | touch_screen = std::make_shared<TouchScreen>(system.HIDCore()); | 263 | touch_resource = std::make_shared<TouchResource>(system); |
| 264 | touch_driver = std::make_shared<TouchDriver>(system.HIDCore()); | ||
| 265 | touch_screen = std::make_shared<TouchScreen>(touch_resource); | ||
| 266 | gesture = std::make_shared<Gesture>(touch_resource); | ||
| 267 | |||
| 268 | touch_update_event = Core::Timing::CreateEvent( | ||
| 269 | "HID::TouchUpdateCallback", | ||
| 270 | [this](s64 time, | ||
| 271 | std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 272 | touch_resource->OnTouchUpdate(time); | ||
| 273 | return std::nullopt; | ||
| 274 | }); | ||
| 201 | 275 | ||
| 202 | touch_screen->SetAppletResource(applet_resource, &shared_mutex); | 276 | touch_resource->SetTouchDriver(touch_driver); |
| 203 | gesture->SetAppletResource(applet_resource, &shared_mutex); | 277 | touch_resource->SetAppletResource(applet_resource, &shared_mutex); |
| 278 | touch_resource->SetInputEvent(input_event, &input_mutex); | ||
| 279 | touch_resource->SetHandheldConfig(handheld_config); | ||
| 280 | touch_resource->SetTimerEvent(touch_update_event); | ||
| 204 | } | 281 | } |
| 205 | 282 | ||
| 206 | void ResourceManager::InitializeConsoleSixAxisSampler() { | 283 | void ResourceManager::InitializeConsoleSixAxisSampler() { |
| @@ -388,13 +465,15 @@ Result ResourceManager::SendVibrationValue(u64 aruid, | |||
| 388 | return result; | 465 | return result; |
| 389 | } | 466 | } |
| 390 | 467 | ||
| 468 | Result ResourceManager::GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const { | ||
| 469 | return ResultSuccess; | ||
| 470 | } | ||
| 471 | |||
| 391 | void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) { | 472 | void ResourceManager::UpdateControllers(std::chrono::nanoseconds ns_late) { |
| 392 | auto& core_timing = system.CoreTiming(); | 473 | auto& core_timing = system.CoreTiming(); |
| 393 | debug_pad->OnUpdate(core_timing); | 474 | debug_pad->OnUpdate(core_timing); |
| 394 | digitizer->OnUpdate(core_timing); | 475 | digitizer->OnUpdate(core_timing); |
| 395 | unique_pad->OnUpdate(core_timing); | 476 | unique_pad->OnUpdate(core_timing); |
| 396 | gesture->OnUpdate(core_timing); | ||
| 397 | touch_screen->OnUpdate(core_timing); | ||
| 398 | palma->OnUpdate(core_timing); | 477 | palma->OnUpdate(core_timing); |
| 399 | home_button->OnUpdate(core_timing); | 478 | home_button->OnUpdate(core_timing); |
| 400 | sleep_button->OnUpdate(core_timing); | 479 | sleep_button->OnUpdate(core_timing); |
| @@ -428,55 +507,9 @@ IAppletResource::IAppletResource(Core::System& system_, std::shared_ptr<Resource | |||
| 428 | {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, | 507 | {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"}, |
| 429 | }; | 508 | }; |
| 430 | RegisterHandlers(functions); | 509 | RegisterHandlers(functions); |
| 431 | |||
| 432 | // Register update callbacks | ||
| 433 | npad_update_event = Core::Timing::CreateEvent( | ||
| 434 | "HID::UpdatePadCallback", | ||
| 435 | [this, resource]( | ||
| 436 | s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 437 | const auto guard = LockService(); | ||
| 438 | resource->UpdateNpad(ns_late); | ||
| 439 | return std::nullopt; | ||
| 440 | }); | ||
| 441 | default_update_event = Core::Timing::CreateEvent( | ||
| 442 | "HID::UpdateDefaultCallback", | ||
| 443 | [this, resource]( | ||
| 444 | s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 445 | const auto guard = LockService(); | ||
| 446 | resource->UpdateControllers(ns_late); | ||
| 447 | return std::nullopt; | ||
| 448 | }); | ||
| 449 | mouse_keyboard_update_event = Core::Timing::CreateEvent( | ||
| 450 | "HID::UpdateMouseKeyboardCallback", | ||
| 451 | [this, resource]( | ||
| 452 | s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 453 | const auto guard = LockService(); | ||
| 454 | resource->UpdateMouseKeyboard(ns_late); | ||
| 455 | return std::nullopt; | ||
| 456 | }); | ||
| 457 | motion_update_event = Core::Timing::CreateEvent( | ||
| 458 | "HID::UpdateMotionCallback", | ||
| 459 | [this, resource]( | ||
| 460 | s64 time, std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> { | ||
| 461 | const auto guard = LockService(); | ||
| 462 | resource->UpdateMotion(ns_late); | ||
| 463 | return std::nullopt; | ||
| 464 | }); | ||
| 465 | |||
| 466 | system.CoreTiming().ScheduleLoopingEvent(npad_update_ns, npad_update_ns, npad_update_event); | ||
| 467 | system.CoreTiming().ScheduleLoopingEvent(default_update_ns, default_update_ns, | ||
| 468 | default_update_event); | ||
| 469 | system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns, | ||
| 470 | mouse_keyboard_update_event); | ||
| 471 | system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns, | ||
| 472 | motion_update_event); | ||
| 473 | } | 510 | } |
| 474 | 511 | ||
| 475 | IAppletResource::~IAppletResource() { | 512 | IAppletResource::~IAppletResource() { |
| 476 | system.CoreTiming().UnscheduleEvent(npad_update_event); | ||
| 477 | system.CoreTiming().UnscheduleEvent(default_update_event); | ||
| 478 | system.CoreTiming().UnscheduleEvent(mouse_keyboard_update_event); | ||
| 479 | system.CoreTiming().UnscheduleEvent(motion_update_event); | ||
| 480 | resource_manager->FreeAppletResourceId(aruid); | 513 | resource_manager->FreeAppletResourceId(aruid); |
| 481 | } | 514 | } |
| 482 | 515 | ||
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; | |||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | namespace Core::HID { | 13 | namespace Core::HID { |
| 14 | struct FirmwareVersion; | ||
| 14 | struct VibrationDeviceHandle; | 15 | struct VibrationDeviceHandle; |
| 15 | struct VibrationValue; | 16 | struct VibrationValue; |
| 16 | struct VibrationDeviceInfo; | 17 | struct VibrationDeviceInfo; |
| @@ -21,8 +22,9 @@ struct EventType; | |||
| 21 | } | 22 | } |
| 22 | 23 | ||
| 23 | namespace Kernel { | 24 | namespace Kernel { |
| 25 | class KEvent; | ||
| 24 | class KSharedMemory; | 26 | class KSharedMemory; |
| 25 | } | 27 | } // namespace Kernel |
| 26 | 28 | ||
| 27 | namespace Service::HID { | 29 | namespace Service::HID { |
| 28 | class AppletResource; | 30 | class AppletResource; |
| @@ -33,6 +35,7 @@ class DebugMouse; | |||
| 33 | class DebugPad; | 35 | class DebugPad; |
| 34 | class Digitizer; | 36 | class Digitizer; |
| 35 | class Gesture; | 37 | class Gesture; |
| 38 | class HidFirmwareSettings; | ||
| 36 | class HomeButton; | 39 | class HomeButton; |
| 37 | class Keyboard; | 40 | class Keyboard; |
| 38 | class Mouse; | 41 | class Mouse; |
| @@ -42,6 +45,8 @@ class SevenSixAxis; | |||
| 42 | class SixAxis; | 45 | class SixAxis; |
| 43 | class SleepButton; | 46 | class SleepButton; |
| 44 | class TouchScreen; | 47 | class TouchScreen; |
| 48 | class TouchDriver; | ||
| 49 | class TouchResource; | ||
| 45 | class UniquePad; | 50 | class UniquePad; |
| 46 | class NpadVibrationBase; | 51 | class NpadVibrationBase; |
| 47 | class NpadN64VibrationDevice; | 52 | class NpadN64VibrationDevice; |
| @@ -52,7 +57,7 @@ struct HandheldConfig; | |||
| 52 | class ResourceManager { | 57 | class ResourceManager { |
| 53 | 58 | ||
| 54 | public: | 59 | public: |
| 55 | explicit ResourceManager(Core::System& system_); | 60 | explicit ResourceManager(Core::System& system_, std::shared_ptr<HidFirmwareSettings> settings); |
| 56 | ~ResourceManager(); | 61 | ~ResourceManager(); |
| 57 | 62 | ||
| 58 | void Initialize(); | 63 | void Initialize(); |
| @@ -102,6 +107,8 @@ public: | |||
| 102 | Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle, | 107 | Result SendVibrationValue(u64 aruid, const Core::HID::VibrationDeviceHandle& handle, |
| 103 | const Core::HID::VibrationValue& value); | 108 | const Core::HID::VibrationValue& value); |
| 104 | 109 | ||
| 110 | Result GetTouchScreenFirmwareVersion(Core::HID::FirmwareVersion& firmware) const; | ||
| 111 | |||
| 105 | void UpdateControllers(std::chrono::nanoseconds ns_late); | 112 | void UpdateControllers(std::chrono::nanoseconds ns_late); |
| 106 | void UpdateNpad(std::chrono::nanoseconds ns_late); | 113 | void UpdateNpad(std::chrono::nanoseconds ns_late); |
| 107 | void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late); | 114 | void UpdateMouseKeyboard(std::chrono::nanoseconds ns_late); |
| @@ -109,6 +116,7 @@ public: | |||
| 109 | 116 | ||
| 110 | private: | 117 | private: |
| 111 | Result CreateAppletResourceImpl(u64 aruid); | 118 | Result CreateAppletResourceImpl(u64 aruid); |
| 119 | void InitializeHandheldConfig(); | ||
| 112 | void InitializeHidCommonSampler(); | 120 | void InitializeHidCommonSampler(); |
| 113 | void InitializeTouchScreenSampler(); | 121 | void InitializeTouchScreenSampler(); |
| 114 | void InitializeConsoleSixAxisSampler(); | 122 | void InitializeConsoleSixAxisSampler(); |
| @@ -117,37 +125,50 @@ private: | |||
| 117 | bool is_initialized{false}; | 125 | bool is_initialized{false}; |
| 118 | 126 | ||
| 119 | mutable std::recursive_mutex shared_mutex; | 127 | mutable std::recursive_mutex shared_mutex; |
| 120 | std::shared_ptr<AppletResource> applet_resource = nullptr; | 128 | std::shared_ptr<AppletResource> applet_resource{nullptr}; |
| 121 | 129 | ||
| 122 | std::shared_ptr<CaptureButton> capture_button = nullptr; | 130 | mutable std::mutex input_mutex; |
| 123 | std::shared_ptr<ConsoleSixAxis> console_six_axis = nullptr; | 131 | Kernel::KEvent* input_event{nullptr}; |
| 124 | std::shared_ptr<DebugMouse> debug_mouse = nullptr; | 132 | |
| 125 | std::shared_ptr<DebugPad> debug_pad = nullptr; | 133 | std::shared_ptr<HandheldConfig> handheld_config{nullptr}; |
| 126 | std::shared_ptr<Digitizer> digitizer = nullptr; | 134 | std::shared_ptr<HidFirmwareSettings> firmware_settings{nullptr}; |
| 127 | std::shared_ptr<Gesture> gesture = nullptr; | 135 | |
| 128 | std::shared_ptr<HomeButton> home_button = nullptr; | 136 | std::shared_ptr<CaptureButton> capture_button{nullptr}; |
| 129 | std::shared_ptr<Keyboard> keyboard = nullptr; | 137 | std::shared_ptr<ConsoleSixAxis> console_six_axis{nullptr}; |
| 130 | std::shared_ptr<Mouse> mouse = nullptr; | 138 | std::shared_ptr<DebugMouse> debug_mouse{nullptr}; |
| 131 | std::shared_ptr<NPad> npad = nullptr; | 139 | std::shared_ptr<DebugPad> debug_pad{nullptr}; |
| 132 | std::shared_ptr<Palma> palma = nullptr; | 140 | std::shared_ptr<Digitizer> digitizer{nullptr}; |
| 133 | std::shared_ptr<SevenSixAxis> seven_six_axis = nullptr; | 141 | std::shared_ptr<HomeButton> home_button{nullptr}; |
| 134 | std::shared_ptr<SixAxis> six_axis = nullptr; | 142 | std::shared_ptr<Keyboard> keyboard{nullptr}; |
| 135 | std::shared_ptr<SleepButton> sleep_button = nullptr; | 143 | std::shared_ptr<Mouse> mouse{nullptr}; |
| 136 | std::shared_ptr<TouchScreen> touch_screen = nullptr; | 144 | std::shared_ptr<NPad> npad{nullptr}; |
| 137 | std::shared_ptr<UniquePad> unique_pad = nullptr; | 145 | std::shared_ptr<Palma> palma{nullptr}; |
| 138 | 146 | std::shared_ptr<SevenSixAxis> seven_six_axis{nullptr}; | |
| 139 | std::shared_ptr<HandheldConfig> handheld_config = nullptr; | 147 | std::shared_ptr<SixAxis> six_axis{nullptr}; |
| 148 | std::shared_ptr<SleepButton> sleep_button{nullptr}; | ||
| 149 | std::shared_ptr<UniquePad> unique_pad{nullptr}; | ||
| 150 | std::shared_ptr<Core::Timing::EventType> npad_update_event; | ||
| 151 | std::shared_ptr<Core::Timing::EventType> default_update_event; | ||
| 152 | std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; | ||
| 153 | std::shared_ptr<Core::Timing::EventType> motion_update_event; | ||
| 140 | 154 | ||
| 141 | // TODO: Create these resources | 155 | // TODO: Create these resources |
| 142 | // std::shared_ptr<AudioControl> audio_control = nullptr; | 156 | // std::shared_ptr<AudioControl> audio_control{nullptr}; |
| 143 | // std::shared_ptr<ButtonConfig> button_config = nullptr; | 157 | // std::shared_ptr<ButtonConfig> button_config{nullptr}; |
| 144 | // std::shared_ptr<Config> config = nullptr; | 158 | // std::shared_ptr<Config> config{nullptr}; |
| 145 | // std::shared_ptr<Connection> connection = nullptr; | 159 | // std::shared_ptr<Connection> connection{nullptr}; |
| 146 | // std::shared_ptr<CustomConfig> custom_config = nullptr; | 160 | // std::shared_ptr<CustomConfig> custom_config{nullptr}; |
| 147 | // std::shared_ptr<Digitizer> digitizer = nullptr; | 161 | // std::shared_ptr<Digitizer> digitizer{nullptr}; |
| 148 | // std::shared_ptr<Hdls> hdls = nullptr; | 162 | // std::shared_ptr<Hdls> hdls{nullptr}; |
| 149 | // std::shared_ptr<PlayReport> play_report = nullptr; | 163 | // std::shared_ptr<PlayReport> play_report{nullptr}; |
| 150 | // std::shared_ptr<Rail> rail = nullptr; | 164 | // std::shared_ptr<Rail> rail{nullptr}; |
| 165 | |||
| 166 | // Touch Resources | ||
| 167 | std::shared_ptr<Gesture> gesture{nullptr}; | ||
| 168 | std::shared_ptr<TouchScreen> touch_screen{nullptr}; | ||
| 169 | std::shared_ptr<TouchResource> touch_resource{nullptr}; | ||
| 170 | std::shared_ptr<TouchDriver> touch_driver{nullptr}; | ||
| 171 | std::shared_ptr<Core::Timing::EventType> touch_update_event{nullptr}; | ||
| 151 | 172 | ||
| 152 | Core::System& system; | 173 | Core::System& system; |
| 153 | KernelHelpers::ServiceContext service_context; | 174 | KernelHelpers::ServiceContext service_context; |
| @@ -162,12 +183,7 @@ public: | |||
| 162 | private: | 183 | private: |
| 163 | void GetSharedMemoryHandle(HLERequestContext& ctx); | 184 | void GetSharedMemoryHandle(HLERequestContext& ctx); |
| 164 | 185 | ||
| 165 | std::shared_ptr<Core::Timing::EventType> npad_update_event; | 186 | u64 aruid{}; |
| 166 | std::shared_ptr<Core::Timing::EventType> default_update_event; | ||
| 167 | std::shared_ptr<Core::Timing::EventType> mouse_keyboard_update_event; | ||
| 168 | std::shared_ptr<Core::Timing::EventType> motion_update_event; | ||
| 169 | |||
| 170 | u64 aruid; | ||
| 171 | std::shared_ptr<ResourceManager> resource_manager; | 187 | std::shared_ptr<ResourceManager> resource_manager; |
| 172 | }; | 188 | }; |
| 173 | 189 | ||
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() { | |||
| 57 | Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); | 57 | Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); |
| 58 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | 58 | for (std::size_t i = 0; i < AruidIndexMax; i++) { |
| 59 | auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); | 59 | auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); |
| 60 | if (data->flag.is_assigned) { | 60 | if (data == nullptr || !data->flag.is_assigned) { |
| 61 | continue; | 61 | continue; |
| 62 | } | 62 | } |
| 63 | auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; | 63 | 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) { | |||
| 118 | data[index].aruid = 0; | 118 | data[index].aruid = 0; |
| 119 | 119 | ||
| 120 | registration_list.flag[index] = RegistrationStatus::PendingDelete; | 120 | registration_list.flag[index] = RegistrationStatus::PendingDelete; |
| 121 | |||
| 122 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 123 | if (registration_list.flag[i] == RegistrationStatus::Initialized) { | ||
| 124 | active_aruid = registration_list.aruid[i]; | ||
| 125 | } | ||
| 126 | } | ||
| 121 | } | 127 | } |
| 122 | 128 | ||
| 123 | void AppletResource::FreeAppletResourceId(u64 aruid) { | 129 | 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 @@ | |||
| 13 | 13 | ||
| 14 | namespace Core { | 14 | namespace Core { |
| 15 | class System; | 15 | class System; |
| 16 | } | 16 | } // namespace Core |
| 17 | 17 | ||
| 18 | namespace Kernel { | 18 | namespace Kernel { |
| 19 | class KEvent; | ||
| 19 | class KSharedMemory; | 20 | class KSharedMemory; |
| 20 | } | 21 | } // namespace Kernel |
| 21 | 22 | ||
| 22 | namespace Service::HID { | 23 | namespace Service::HID { |
| 23 | struct SharedMemoryFormat; | 24 | struct SharedMemoryFormat; |
| @@ -73,7 +74,8 @@ struct AppletResourceHolder { | |||
| 73 | std::recursive_mutex* shared_mutex{nullptr}; | 74 | std::recursive_mutex* shared_mutex{nullptr}; |
| 74 | NPadResource* shared_npad_resource{nullptr}; | 75 | NPadResource* shared_npad_resource{nullptr}; |
| 75 | std::shared_ptr<HandheldConfig> handheld_config{nullptr}; | 76 | std::shared_ptr<HandheldConfig> handheld_config{nullptr}; |
| 76 | long* handle_1; | 77 | Kernel::KEvent* input_event{nullptr}; |
| 78 | std::mutex* input_mutex{nullptr}; | ||
| 77 | }; | 79 | }; |
| 78 | 80 | ||
| 79 | class AppletResource { | 81 | 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() {} | |||
| 17 | void Digitizer::OnRelease() {} | 17 | void Digitizer::OnRelease() {} |
| 18 | 18 | ||
| 19 | void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 19 | void Digitizer::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 20 | if (!smart_update) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | |||
| 24 | std::scoped_lock shared_lock{*shared_mutex}; | 20 | std::scoped_lock shared_lock{*shared_mutex}; |
| 25 | const u64 aruid = applet_resource->GetActiveAruid(); | 21 | const u64 aruid = applet_resource->GetActiveAruid(); |
| 26 | auto* data = applet_resource->GetAruidData(aruid); | 22 | 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: | |||
| 20 | 20 | ||
| 21 | // When the controller is requesting an update for the shared memory | 21 | // When the controller is requesting an update for the shared memory |
| 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; |
| 23 | |||
| 24 | private: | ||
| 25 | bool smart_update{}; | ||
| 26 | }; | 23 | }; |
| 27 | } // namespace Service::HID | 24 | } // 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) { | |||
| 102 | for (std::size_t i = 0; i < 19; ++i) { | 102 | for (std::size_t i = 0; i < 19; ++i) { |
| 103 | WriteEmptyEntry(npad); | 103 | WriteEmptyEntry(npad); |
| 104 | } | 104 | } |
| 105 | |||
| 106 | controller.is_active = true; | ||
| 105 | } | 107 | } |
| 106 | 108 | ||
| 107 | return ResultSuccess; | 109 | return ResultSuccess; |
| @@ -129,7 +131,7 @@ void NPad::ControllerUpdate(Core::HID::ControllerTriggerType type, std::size_t c | |||
| 129 | 131 | ||
| 130 | auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); | 132 | auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); |
| 131 | 133 | ||
| 132 | if (!data->flag.is_assigned) { | 134 | if (data == nullptr || !data->flag.is_assigned) { |
| 133 | continue; | 135 | continue; |
| 134 | } | 136 | } |
| 135 | 137 | ||
| @@ -461,9 +463,16 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | |||
| 461 | std::scoped_lock lock{*applet_resource_holder.shared_mutex}; | 463 | std::scoped_lock lock{*applet_resource_holder.shared_mutex}; |
| 462 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { | 464 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { |
| 463 | const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); | 465 | const auto* data = applet_resource_holder.applet_resource->GetAruidDataByIndex(aruid_index); |
| 464 | const auto aruid = data->aruid; | ||
| 465 | 466 | ||
| 466 | if (!data->flag.is_assigned) { | 467 | if (data == nullptr || !data->flag.is_assigned) { |
| 468 | continue; | ||
| 469 | } | ||
| 470 | |||
| 471 | bool is_set{}; | ||
| 472 | const auto aruid = data->aruid; | ||
| 473 | npad_resource.IsSupportedNpadStyleSet(is_set, aruid); | ||
| 474 | // Wait until style is defined | ||
| 475 | if (!is_set) { | ||
| 467 | continue; | 476 | continue; |
| 468 | } | 477 | } |
| 469 | 478 | ||
| @@ -484,6 +493,10 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | |||
| 484 | continue; | 493 | continue; |
| 485 | } | 494 | } |
| 486 | 495 | ||
| 496 | if (!controller.is_active) { | ||
| 497 | continue; | ||
| 498 | } | ||
| 499 | |||
| 487 | RequestPadStateUpdate(aruid, controller.device->GetNpadIdType()); | 500 | RequestPadStateUpdate(aruid, controller.device->GetNpadIdType()); |
| 488 | auto& pad_state = controller.npad_pad_state; | 501 | auto& pad_state = controller.npad_pad_state; |
| 489 | auto& libnx_state = controller.npad_libnx_state; | 502 | auto& libnx_state = controller.npad_libnx_state; |
| @@ -592,7 +605,9 @@ void NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | |||
| 592 | libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; | 605 | libnx_state.npad_buttons.raw = pad_state.npad_buttons.raw; |
| 593 | libnx_state.l_stick = pad_state.l_stick; | 606 | libnx_state.l_stick = pad_state.l_stick; |
| 594 | libnx_state.r_stick = pad_state.r_stick; | 607 | libnx_state.r_stick = pad_state.r_stick; |
| 595 | npad->system_ext_lifo.WriteNextEntry(pad_state); | 608 | libnx_state.sampling_number = |
| 609 | npad->system_ext_lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 610 | npad->system_ext_lifo.WriteNextEntry(libnx_state); | ||
| 596 | 611 | ||
| 597 | press_state |= static_cast<u64>(pad_state.npad_buttons.raw); | 612 | press_state |= static_cast<u64>(pad_state.npad_buttons.raw); |
| 598 | } | 613 | } |
| @@ -1060,6 +1075,7 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) { | |||
| 1060 | // TODO: Remove this once abstract pad is emulated properly | 1075 | // TODO: Remove this once abstract pad is emulated properly |
| 1061 | const auto aruid_index = npad_resource.GetIndexFromAruid(aruid); | 1076 | const auto aruid_index = npad_resource.GetIndexFromAruid(aruid); |
| 1062 | for (auto& controller : controller_data[aruid_index]) { | 1077 | for (auto& controller : controller_data[aruid_index]) { |
| 1078 | controller.is_active = false; | ||
| 1063 | controller.is_connected = false; | 1079 | controller.is_connected = false; |
| 1064 | controller.shared_memory = nullptr; | 1080 | controller.shared_memory = nullptr; |
| 1065 | } | 1081 | } |
| @@ -1070,11 +1086,14 @@ void NPad::UnregisterAppletResourceUserId(u64 aruid) { | |||
| 1070 | void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, | 1086 | void NPad::SetNpadExternals(std::shared_ptr<AppletResource> resource, |
| 1071 | std::recursive_mutex* shared_mutex, | 1087 | std::recursive_mutex* shared_mutex, |
| 1072 | std::shared_ptr<HandheldConfig> handheld_config, | 1088 | std::shared_ptr<HandheldConfig> handheld_config, |
| 1089 | Kernel::KEvent* input_event, std::mutex* input_mutex, | ||
| 1073 | std::shared_ptr<Service::Set::ISystemSettingsServer> settings) { | 1090 | std::shared_ptr<Service::Set::ISystemSettingsServer> settings) { |
| 1074 | applet_resource_holder.applet_resource = resource; | 1091 | applet_resource_holder.applet_resource = resource; |
| 1075 | applet_resource_holder.shared_mutex = shared_mutex; | 1092 | applet_resource_holder.shared_mutex = shared_mutex; |
| 1076 | applet_resource_holder.shared_npad_resource = &npad_resource; | 1093 | applet_resource_holder.shared_npad_resource = &npad_resource; |
| 1077 | applet_resource_holder.handheld_config = handheld_config; | 1094 | applet_resource_holder.handheld_config = handheld_config; |
| 1095 | applet_resource_holder.input_event = input_event; | ||
| 1096 | applet_resource_holder.input_mutex = input_mutex; | ||
| 1078 | 1097 | ||
| 1079 | vibration_handler.SetSettingsService(settings); | 1098 | vibration_handler.SetSettingsService(settings); |
| 1080 | 1099 | ||
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: | |||
| 131 | void SetNpadExternals(std::shared_ptr<AppletResource> resource, | 131 | void SetNpadExternals(std::shared_ptr<AppletResource> resource, |
| 132 | std::recursive_mutex* shared_mutex, | 132 | std::recursive_mutex* shared_mutex, |
| 133 | std::shared_ptr<HandheldConfig> handheld_config, | 133 | std::shared_ptr<HandheldConfig> handheld_config, |
| 134 | Kernel::KEvent* input_event, std::mutex* input_mutex, | ||
| 134 | std::shared_ptr<Service::Set::ISystemSettingsServer> settings); | 135 | std::shared_ptr<Service::Set::ISystemSettingsServer> settings); |
| 135 | 136 | ||
| 136 | AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); | 137 | AppletDetailedUiType GetAppletDetailedUiType(Core::HID::NpadIdType npad_id); |
| @@ -163,6 +164,7 @@ private: | |||
| 163 | NpadInternalState* shared_memory = nullptr; | 164 | NpadInternalState* shared_memory = nullptr; |
| 164 | Core::HID::EmulatedController* device = nullptr; | 165 | Core::HID::EmulatedController* device = nullptr; |
| 165 | 166 | ||
| 167 | bool is_active{}; | ||
| 166 | bool is_connected{}; | 168 | bool is_connected{}; |
| 167 | 169 | ||
| 168 | // Dual joycons can have only one side connected | 170 | // Dual joycons can have only one side connected |
| @@ -204,9 +206,6 @@ private: | |||
| 204 | std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads; | 206 | std::array<AbstractPad, MaxSupportedNpadIdTypes> abstracted_pads; |
| 205 | NpadVibration vibration_handler{}; | 207 | NpadVibration vibration_handler{}; |
| 206 | 208 | ||
| 207 | Kernel::KEvent* input_event{nullptr}; | ||
| 208 | std::mutex* input_mutex{nullptr}; | ||
| 209 | |||
| 210 | std::atomic<u64> press_state{}; | 209 | std::atomic<u64> press_state{}; |
| 211 | std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax> | 210 | std::array<std::array<NpadControllerData, MaxSupportedNpadIdTypes>, AruidIndexMax> |
| 212 | controller_data{}; | 211 | 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) { | |||
| 72 | state[aruid_index] = {}; | 72 | state[aruid_index] = {}; |
| 73 | registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete; | 73 | registration_list.flag[aruid_index] = RegistrationStatus::PendingDelete; |
| 74 | } | 74 | } |
| 75 | |||
| 76 | for (std::size_t i = 0; i < AruidIndexMax; i++) { | ||
| 77 | if (registration_list.flag[i] == RegistrationStatus::Initialized) { | ||
| 78 | active_data_aruid = registration_list.aruid[i]; | ||
| 79 | } | ||
| 80 | } | ||
| 75 | } | 81 | } |
| 76 | 82 | ||
| 77 | void NPadResource::DestroyStyleSetUpdateEvents(u64 aruid) { | 83 | 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() {} | |||
| 28 | 28 | ||
| 29 | void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 29 | void SixAxis::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 30 | std::scoped_lock shared_lock{*shared_mutex}; | 30 | std::scoped_lock shared_lock{*shared_mutex}; |
| 31 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 32 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 33 | 31 | ||
| 34 | if (data == nullptr || !data->flag.is_assigned) { | 32 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; ++aruid_index) { |
| 35 | return; | 33 | const auto* data = applet_resource->GetAruidDataByIndex(aruid_index); |
| 36 | } | ||
| 37 | |||
| 38 | if (!IsControllerActivated()) { | ||
| 39 | return; | ||
| 40 | } | ||
| 41 | 34 | ||
| 42 | for (std::size_t i = 0; i < controller_data.size(); ++i) { | 35 | if (data == nullptr || !data->flag.is_assigned) { |
| 43 | NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i]; | ||
| 44 | auto& controller = controller_data[i]; | ||
| 45 | const auto& controller_type = controller.device->GetNpadStyleIndex(); | ||
| 46 | |||
| 47 | if (controller_type == Core::HID::NpadStyleIndex::None || | ||
| 48 | !controller.device->IsConnected()) { | ||
| 49 | continue; | 36 | continue; |
| 50 | } | 37 | } |
| 51 | 38 | ||
| 52 | const auto& motion_state = controller.device->GetMotions(); | 39 | if (!IsControllerActivated()) { |
| 53 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | 40 | return; |
| 54 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; | ||
| 55 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; | ||
| 56 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; | ||
| 57 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; | ||
| 58 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; | ||
| 59 | |||
| 60 | auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo; | ||
| 61 | auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo; | ||
| 62 | auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo; | ||
| 63 | auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo; | ||
| 64 | auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo; | ||
| 65 | auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo; | ||
| 66 | |||
| 67 | // Clear previous state | ||
| 68 | sixaxis_fullkey_state = {}; | ||
| 69 | sixaxis_handheld_state = {}; | ||
| 70 | sixaxis_dual_left_state = {}; | ||
| 71 | sixaxis_dual_right_state = {}; | ||
| 72 | sixaxis_left_lifo_state = {}; | ||
| 73 | sixaxis_right_lifo_state = {}; | ||
| 74 | |||
| 75 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { | ||
| 76 | controller.sixaxis_at_rest = true; | ||
| 77 | for (std::size_t e = 0; e < motion_state.size(); ++e) { | ||
| 78 | controller.sixaxis_at_rest = | ||
| 79 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 80 | } | ||
| 81 | } | 41 | } |
| 82 | 42 | ||
| 83 | const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, | 43 | for (std::size_t i = 0; i < controller_data.size(); ++i) { |
| 84 | const Core::HID::ControllerMotion& hid_state) { | 44 | NpadSharedMemoryEntry& shared_memory = data->shared_memory_format->npad.npad_entry[i]; |
| 85 | using namespace std::literals::chrono_literals; | 45 | auto& controller = controller_data[i]; |
| 86 | static constexpr Core::HID::SixAxisSensorState default_motion_state = { | 46 | const auto& controller_type = controller.device->GetNpadStyleIndex(); |
| 87 | .delta_time = std::chrono::nanoseconds(5ms).count(), | 47 | |
| 88 | .accel = {0, 0, -1.0f}, | 48 | if (!data->flag.enable_six_axis_sensor) { |
| 89 | .orientation = | 49 | continue; |
| 90 | { | 50 | } |
| 91 | Common::Vec3f{1.0f, 0, 0}, | 51 | |
| 92 | Common::Vec3f{0, 1.0f, 0}, | 52 | if (controller_type == Core::HID::NpadStyleIndex::None || |
| 93 | Common::Vec3f{0, 0, 1.0f}, | 53 | !controller.device->IsConnected()) { |
| 94 | }, | 54 | continue; |
| 95 | .attribute = {1}, | 55 | } |
| 56 | |||
| 57 | const auto& motion_state = controller.device->GetMotions(); | ||
| 58 | auto& sixaxis_fullkey_state = controller.sixaxis_fullkey_state; | ||
| 59 | auto& sixaxis_handheld_state = controller.sixaxis_handheld_state; | ||
| 60 | auto& sixaxis_dual_left_state = controller.sixaxis_dual_left_state; | ||
| 61 | auto& sixaxis_dual_right_state = controller.sixaxis_dual_right_state; | ||
| 62 | auto& sixaxis_left_lifo_state = controller.sixaxis_left_lifo_state; | ||
| 63 | auto& sixaxis_right_lifo_state = controller.sixaxis_right_lifo_state; | ||
| 64 | |||
| 65 | auto& sixaxis_fullkey_lifo = shared_memory.internal_state.sixaxis_fullkey_lifo; | ||
| 66 | auto& sixaxis_handheld_lifo = shared_memory.internal_state.sixaxis_handheld_lifo; | ||
| 67 | auto& sixaxis_dual_left_lifo = shared_memory.internal_state.sixaxis_dual_left_lifo; | ||
| 68 | auto& sixaxis_dual_right_lifo = shared_memory.internal_state.sixaxis_dual_right_lifo; | ||
| 69 | auto& sixaxis_left_lifo = shared_memory.internal_state.sixaxis_left_lifo; | ||
| 70 | auto& sixaxis_right_lifo = shared_memory.internal_state.sixaxis_right_lifo; | ||
| 71 | |||
| 72 | // Clear previous state | ||
| 73 | sixaxis_fullkey_state = {}; | ||
| 74 | sixaxis_handheld_state = {}; | ||
| 75 | sixaxis_dual_left_state = {}; | ||
| 76 | sixaxis_dual_right_state = {}; | ||
| 77 | sixaxis_left_lifo_state = {}; | ||
| 78 | sixaxis_right_lifo_state = {}; | ||
| 79 | |||
| 80 | if (controller.sixaxis_sensor_enabled && Settings::values.motion_enabled.GetValue()) { | ||
| 81 | controller.sixaxis_at_rest = true; | ||
| 82 | for (std::size_t e = 0; e < motion_state.size(); ++e) { | ||
| 83 | controller.sixaxis_at_rest = | ||
| 84 | controller.sixaxis_at_rest && motion_state[e].is_at_rest; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | const auto set_motion_state = [&](Core::HID::SixAxisSensorState& state, | ||
| 89 | const Core::HID::ControllerMotion& hid_state) { | ||
| 90 | using namespace std::literals::chrono_literals; | ||
| 91 | static constexpr Core::HID::SixAxisSensorState default_motion_state = { | ||
| 92 | .delta_time = std::chrono::nanoseconds(5ms).count(), | ||
| 93 | .accel = {0, 0, -1.0f}, | ||
| 94 | .orientation = | ||
| 95 | { | ||
| 96 | Common::Vec3f{1.0f, 0, 0}, | ||
| 97 | Common::Vec3f{0, 1.0f, 0}, | ||
| 98 | Common::Vec3f{0, 0, 1.0f}, | ||
| 99 | }, | ||
| 100 | .attribute = {1}, | ||
| 101 | }; | ||
| 102 | if (!controller.sixaxis_sensor_enabled) { | ||
| 103 | state = default_motion_state; | ||
| 104 | return; | ||
| 105 | } | ||
| 106 | if (!Settings::values.motion_enabled.GetValue()) { | ||
| 107 | state = default_motion_state; | ||
| 108 | return; | ||
| 109 | } | ||
| 110 | state.attribute.is_connected.Assign(1); | ||
| 111 | state.delta_time = std::chrono::nanoseconds(5ms).count(); | ||
| 112 | state.accel = hid_state.accel; | ||
| 113 | state.gyro = hid_state.gyro; | ||
| 114 | state.rotation = hid_state.rotation; | ||
| 115 | state.orientation = hid_state.orientation; | ||
| 96 | }; | 116 | }; |
| 97 | if (!controller.sixaxis_sensor_enabled) { | 117 | |
| 98 | state = default_motion_state; | 118 | switch (controller_type) { |
| 99 | return; | 119 | case Core::HID::NpadStyleIndex::None: |
| 120 | ASSERT(false); | ||
| 121 | break; | ||
| 122 | case Core::HID::NpadStyleIndex::Fullkey: | ||
| 123 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 124 | break; | ||
| 125 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 126 | set_motion_state(sixaxis_handheld_state, motion_state[0]); | ||
| 127 | break; | ||
| 128 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 129 | set_motion_state(sixaxis_dual_left_state, motion_state[0]); | ||
| 130 | set_motion_state(sixaxis_dual_right_state, motion_state[1]); | ||
| 131 | break; | ||
| 132 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 133 | set_motion_state(sixaxis_left_lifo_state, motion_state[0]); | ||
| 134 | break; | ||
| 135 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 136 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | ||
| 137 | break; | ||
| 138 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 139 | using namespace std::literals::chrono_literals; | ||
| 140 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 141 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 142 | break; | ||
| 143 | default: | ||
| 144 | break; | ||
| 100 | } | 145 | } |
| 101 | if (!Settings::values.motion_enabled.GetValue()) { | 146 | |
| 102 | state = default_motion_state; | 147 | sixaxis_fullkey_state.sampling_number = |
| 103 | return; | 148 | sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; |
| 149 | sixaxis_handheld_state.sampling_number = | ||
| 150 | sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 151 | sixaxis_dual_left_state.sampling_number = | ||
| 152 | sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 153 | sixaxis_dual_right_state.sampling_number = | ||
| 154 | sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 155 | sixaxis_left_lifo_state.sampling_number = | ||
| 156 | sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 157 | sixaxis_right_lifo_state.sampling_number = | ||
| 158 | sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 159 | |||
| 160 | if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 161 | // This buffer only is updated on handheld on HW | ||
| 162 | sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 163 | } else { | ||
| 164 | // Handheld doesn't update this buffer on HW | ||
| 165 | sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 104 | } | 166 | } |
| 105 | state.attribute.is_connected.Assign(1); | ||
| 106 | state.delta_time = std::chrono::nanoseconds(5ms).count(); | ||
| 107 | state.accel = hid_state.accel; | ||
| 108 | state.gyro = hid_state.gyro; | ||
| 109 | state.rotation = hid_state.rotation; | ||
| 110 | state.orientation = hid_state.orientation; | ||
| 111 | }; | ||
| 112 | |||
| 113 | switch (controller_type) { | ||
| 114 | case Core::HID::NpadStyleIndex::None: | ||
| 115 | ASSERT(false); | ||
| 116 | break; | ||
| 117 | case Core::HID::NpadStyleIndex::Fullkey: | ||
| 118 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 119 | break; | ||
| 120 | case Core::HID::NpadStyleIndex::Handheld: | ||
| 121 | set_motion_state(sixaxis_handheld_state, motion_state[0]); | ||
| 122 | break; | ||
| 123 | case Core::HID::NpadStyleIndex::JoyconDual: | ||
| 124 | set_motion_state(sixaxis_dual_left_state, motion_state[0]); | ||
| 125 | set_motion_state(sixaxis_dual_right_state, motion_state[1]); | ||
| 126 | break; | ||
| 127 | case Core::HID::NpadStyleIndex::JoyconLeft: | ||
| 128 | set_motion_state(sixaxis_left_lifo_state, motion_state[0]); | ||
| 129 | break; | ||
| 130 | case Core::HID::NpadStyleIndex::JoyconRight: | ||
| 131 | set_motion_state(sixaxis_right_lifo_state, motion_state[1]); | ||
| 132 | break; | ||
| 133 | case Core::HID::NpadStyleIndex::Pokeball: | ||
| 134 | using namespace std::literals::chrono_literals; | ||
| 135 | set_motion_state(sixaxis_fullkey_state, motion_state[0]); | ||
| 136 | sixaxis_fullkey_state.delta_time = std::chrono::nanoseconds(15ms).count(); | ||
| 137 | break; | ||
| 138 | default: | ||
| 139 | break; | ||
| 140 | } | ||
| 141 | 167 | ||
| 142 | sixaxis_fullkey_state.sampling_number = | 168 | sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state); |
| 143 | sixaxis_fullkey_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | 169 | sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state); |
| 144 | sixaxis_handheld_state.sampling_number = | 170 | sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state); |
| 145 | sixaxis_handheld_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | 171 | sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state); |
| 146 | sixaxis_dual_left_state.sampling_number = | ||
| 147 | sixaxis_dual_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 148 | sixaxis_dual_right_state.sampling_number = | ||
| 149 | sixaxis_dual_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 150 | sixaxis_left_lifo_state.sampling_number = | ||
| 151 | sixaxis_left_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 152 | sixaxis_right_lifo_state.sampling_number = | ||
| 153 | sixaxis_right_lifo.lifo.ReadCurrentEntry().state.sampling_number + 1; | ||
| 154 | |||
| 155 | if (IndexToNpadIdType(i) == Core::HID::NpadIdType::Handheld) { | ||
| 156 | // This buffer only is updated on handheld on HW | ||
| 157 | sixaxis_handheld_lifo.lifo.WriteNextEntry(sixaxis_handheld_state); | ||
| 158 | } else { | ||
| 159 | // Handheld doesn't update this buffer on HW | ||
| 160 | sixaxis_fullkey_lifo.lifo.WriteNextEntry(sixaxis_fullkey_state); | ||
| 161 | } | 172 | } |
| 162 | |||
| 163 | sixaxis_dual_left_lifo.lifo.WriteNextEntry(sixaxis_dual_left_state); | ||
| 164 | sixaxis_dual_right_lifo.lifo.WriteNextEntry(sixaxis_dual_right_state); | ||
| 165 | sixaxis_left_lifo.lifo.WriteNextEntry(sixaxis_left_lifo_state); | ||
| 166 | sixaxis_right_lifo.lifo.WriteNextEntry(sixaxis_right_lifo_state); | ||
| 167 | } | 173 | } |
| 168 | } | 174 | } |
| 169 | 175 | ||
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/math_util.h" | ||
| 5 | #include "common/settings.h" | ||
| 6 | #include "core/frontend/emu_window.h" | ||
| 7 | #include "hid_core/frontend/emulated_console.h" | ||
| 8 | #include "hid_core/hid_core.h" | ||
| 9 | #include "hid_core/resources/applet_resource.h" | ||
| 10 | #include "hid_core/resources/shared_memory_format.h" | ||
| 11 | #include "hid_core/resources/touch_screen/gesture.h" | 4 | #include "hid_core/resources/touch_screen/gesture.h" |
| 5 | #include "hid_core/resources/touch_screen/touch_screen_resource.h" | ||
| 12 | 6 | ||
| 13 | namespace Service::HID { | 7 | namespace Service::HID { |
| 14 | // HW is around 700, value is set to 400 to make it easier to trigger with mouse | ||
| 15 | constexpr f32 swipe_threshold = 400.0f; // Threshold in pixels/s | ||
| 16 | constexpr f32 angle_threshold = 0.015f; // Threshold in radians | ||
| 17 | constexpr f32 pinch_threshold = 0.5f; // Threshold in pixels | ||
| 18 | constexpr f32 press_delay = 0.5f; // Time in seconds | ||
| 19 | constexpr f32 double_tap_delay = 0.35f; // Time in seconds | ||
| 20 | 8 | ||
| 21 | constexpr f32 Square(s32 num) { | 9 | Gesture::Gesture(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {} |
| 22 | return static_cast<f32>(num * num); | ||
| 23 | } | ||
| 24 | 10 | ||
| 25 | Gesture::Gesture(Core::HID::HIDCore& hid_core_) : ControllerBase(hid_core_) { | ||
| 26 | console = hid_core.GetEmulatedConsole(); | ||
| 27 | } | ||
| 28 | Gesture::~Gesture() = default; | 11 | Gesture::~Gesture() = default; |
| 29 | 12 | ||
| 30 | void Gesture::OnInit() { | 13 | Result Gesture::Activate() { |
| 31 | std::scoped_lock shared_lock{*shared_mutex}; | 14 | std::scoped_lock lock{mutex}; |
| 32 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 33 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 34 | |||
| 35 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 36 | return; | ||
| 37 | } | ||
| 38 | |||
| 39 | shared_memory = &data->shared_memory_format->gesture; | ||
| 40 | shared_memory->gesture_lifo.buffer_count = 0; | ||
| 41 | shared_memory->gesture_lifo.buffer_tail = 0; | ||
| 42 | force_update = true; | ||
| 43 | } | ||
| 44 | |||
| 45 | void Gesture::OnRelease() {} | ||
| 46 | |||
| 47 | void Gesture::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | ||
| 48 | std::scoped_lock shared_lock{*shared_mutex}; | ||
| 49 | const u64 aruid = applet_resource->GetActiveAruid(); | ||
| 50 | auto* data = applet_resource->GetAruidData(aruid); | ||
| 51 | |||
| 52 | if (data == nullptr || !data->flag.is_assigned) { | ||
| 53 | return; | ||
| 54 | } | ||
| 55 | |||
| 56 | shared_memory = &data->shared_memory_format->gesture; | ||
| 57 | |||
| 58 | if (!IsControllerActivated()) { | ||
| 59 | shared_memory->gesture_lifo.buffer_count = 0; | ||
| 60 | shared_memory->gesture_lifo.buffer_tail = 0; | ||
| 61 | return; | ||
| 62 | } | ||
| 63 | |||
| 64 | ReadTouchInput(); | ||
| 65 | |||
| 66 | GestureProperties gesture = GetGestureProperties(); | ||
| 67 | f32 time_difference = | ||
| 68 | static_cast<f32>(shared_memory->gesture_lifo.timestamp - last_update_timestamp) / | ||
| 69 | (1000 * 1000 * 1000); | ||
| 70 | |||
| 71 | // Only update if necessary | ||
| 72 | if (!ShouldUpdateGesture(gesture, time_difference)) { | ||
| 73 | return; | ||
| 74 | } | ||
| 75 | |||
| 76 | last_update_timestamp = shared_memory->gesture_lifo.timestamp; | ||
| 77 | UpdateGestureSharedMemory(gesture, time_difference); | ||
| 78 | } | ||
| 79 | |||
| 80 | void Gesture::ReadTouchInput() { | ||
| 81 | if (!Settings::values.touchscreen.enabled) { | ||
| 82 | fingers = {}; | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | const auto touch_status = console->GetTouch(); | ||
| 87 | for (std::size_t id = 0; id < fingers.size(); ++id) { | ||
| 88 | fingers[id] = touch_status[id]; | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | bool Gesture::ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference) { | ||
| 93 | const auto& last_entry = GetLastGestureEntry(); | ||
| 94 | if (force_update) { | ||
| 95 | force_update = false; | ||
| 96 | return true; | ||
| 97 | } | ||
| 98 | |||
| 99 | // Update if coordinates change | ||
| 100 | for (size_t id = 0; id < MAX_POINTS; id++) { | ||
| 101 | if (gesture.points[id] != last_gesture.points[id]) { | ||
| 102 | return true; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | // Update on press and hold event after 0.5 seconds | ||
| 107 | if (last_entry.type == GestureType::Touch && last_entry.point_count == 1 && | ||
| 108 | time_difference > press_delay) { | ||
| 109 | return enable_press_and_tap; | ||
| 110 | } | ||
| 111 | |||
| 112 | return false; | ||
| 113 | } | ||
| 114 | |||
| 115 | void Gesture::UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference) { | ||
| 116 | GestureType type = GestureType::Idle; | ||
| 117 | GestureAttribute attributes{}; | ||
| 118 | |||
| 119 | const auto& last_entry = shared_memory->gesture_lifo.ReadCurrentEntry().state; | ||
| 120 | |||
| 121 | // Reset next state to default | ||
| 122 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 123 | next_state.delta = {}; | ||
| 124 | next_state.vel_x = 0; | ||
| 125 | next_state.vel_y = 0; | ||
| 126 | next_state.direction = GestureDirection::None; | ||
| 127 | next_state.rotation_angle = 0; | ||
| 128 | next_state.scale = 0; | ||
| 129 | |||
| 130 | if (gesture.active_points > 0) { | ||
| 131 | if (last_gesture.active_points == 0) { | ||
| 132 | NewGesture(gesture, type, attributes); | ||
| 133 | } else { | ||
| 134 | UpdateExistingGesture(gesture, type, time_difference); | ||
| 135 | } | ||
| 136 | } else { | ||
| 137 | EndGesture(gesture, last_gesture, type, attributes, time_difference); | ||
| 138 | } | ||
| 139 | |||
| 140 | // Apply attributes | ||
| 141 | next_state.detection_count = gesture.detection_count; | ||
| 142 | next_state.type = type; | ||
| 143 | next_state.attributes = attributes; | ||
| 144 | next_state.pos = gesture.mid_point; | ||
| 145 | next_state.point_count = static_cast<s32>(gesture.active_points); | ||
| 146 | next_state.points = gesture.points; | ||
| 147 | last_gesture = gesture; | ||
| 148 | |||
| 149 | shared_memory->gesture_lifo.WriteNextEntry(next_state); | ||
| 150 | } | ||
| 151 | |||
| 152 | void Gesture::NewGesture(GestureProperties& gesture, GestureType& type, | ||
| 153 | GestureAttribute& attributes) { | ||
| 154 | const auto& last_entry = GetLastGestureEntry(); | ||
| 155 | |||
| 156 | gesture.detection_count++; | ||
| 157 | type = GestureType::Touch; | ||
| 158 | |||
| 159 | // New touch after cancel is not considered new | ||
| 160 | if (last_entry.type != GestureType::Cancel) { | ||
| 161 | attributes.is_new_touch.Assign(1); | ||
| 162 | enable_press_and_tap = true; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | void Gesture::UpdateExistingGesture(GestureProperties& gesture, GestureType& type, | ||
| 167 | f32 time_difference) { | ||
| 168 | const auto& last_entry = GetLastGestureEntry(); | ||
| 169 | 15 | ||
| 170 | // Promote to pan type if touch moved | 16 | // TODO: Result result = CreateThread(); |
| 171 | for (size_t id = 0; id < MAX_POINTS; id++) { | 17 | Result result = ResultSuccess; |
| 172 | if (gesture.points[id] != last_gesture.points[id]) { | 18 | if (result.IsError()) { |
| 173 | type = GestureType::Pan; | 19 | return result; |
| 174 | break; | ||
| 175 | } | ||
| 176 | } | 20 | } |
| 177 | 21 | ||
| 178 | // Number of fingers changed cancel the last event and clear data | 22 | result = touch_resource->ActivateGesture(); |
| 179 | if (gesture.active_points != last_gesture.active_points) { | ||
| 180 | type = GestureType::Cancel; | ||
| 181 | enable_press_and_tap = false; | ||
| 182 | gesture.active_points = 0; | ||
| 183 | gesture.mid_point = {}; | ||
| 184 | gesture.points.fill({}); | ||
| 185 | return; | ||
| 186 | } | ||
| 187 | |||
| 188 | // Calculate extra parameters of panning | ||
| 189 | if (type == GestureType::Pan) { | ||
| 190 | UpdatePanEvent(gesture, last_gesture, type, time_difference); | ||
| 191 | return; | ||
| 192 | } | ||
| 193 | 23 | ||
| 194 | // Promote to press type | 24 | if (result.IsError()) { |
| 195 | if (last_entry.type == GestureType::Touch) { | 25 | // TODO: StopThread(); |
| 196 | type = GestureType::Press; | ||
| 197 | } | 26 | } |
| 198 | } | ||
| 199 | |||
| 200 | void Gesture::EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 201 | GestureType& type, GestureAttribute& attributes, f32 time_difference) { | ||
| 202 | const auto& last_entry = GetLastGestureEntry(); | ||
| 203 | 27 | ||
| 204 | if (last_gesture_props.active_points != 0) { | 28 | return result; |
| 205 | switch (last_entry.type) { | ||
| 206 | case GestureType::Touch: | ||
| 207 | if (enable_press_and_tap) { | ||
| 208 | SetTapEvent(gesture, last_gesture_props, type, attributes); | ||
| 209 | return; | ||
| 210 | } | ||
| 211 | type = GestureType::Cancel; | ||
| 212 | force_update = true; | ||
| 213 | break; | ||
| 214 | case GestureType::Press: | ||
| 215 | case GestureType::Tap: | ||
| 216 | case GestureType::Swipe: | ||
| 217 | case GestureType::Pinch: | ||
| 218 | case GestureType::Rotate: | ||
| 219 | type = GestureType::Complete; | ||
| 220 | force_update = true; | ||
| 221 | break; | ||
| 222 | case GestureType::Pan: | ||
| 223 | EndPanEvent(gesture, last_gesture_props, type, time_difference); | ||
| 224 | break; | ||
| 225 | default: | ||
| 226 | break; | ||
| 227 | } | ||
| 228 | return; | ||
| 229 | } | ||
| 230 | if (last_entry.type == GestureType::Complete || last_entry.type == GestureType::Cancel) { | ||
| 231 | gesture.detection_count++; | ||
| 232 | } | ||
| 233 | } | 29 | } |
| 234 | 30 | ||
| 235 | void Gesture::SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 31 | Result Gesture::Activate(u64 aruid, u32 basic_gesture_id) { |
| 236 | GestureType& type, GestureAttribute& attributes) { | 32 | std::scoped_lock lock{mutex}; |
| 237 | type = GestureType::Tap; | 33 | return touch_resource->ActivateGesture(aruid, basic_gesture_id); |
| 238 | gesture = last_gesture_props; | ||
| 239 | force_update = true; | ||
| 240 | f32 tap_time_difference = | ||
| 241 | static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); | ||
| 242 | last_tap_timestamp = last_update_timestamp; | ||
| 243 | if (tap_time_difference < double_tap_delay) { | ||
| 244 | attributes.is_double_tap.Assign(1); | ||
| 245 | } | ||
| 246 | } | 34 | } |
| 247 | 35 | ||
| 248 | void Gesture::UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 36 | Result Gesture::Deactivate() { |
| 249 | GestureType& type, f32 time_difference) { | 37 | std::scoped_lock lock{mutex}; |
| 250 | const auto& last_entry = GetLastGestureEntry(); | 38 | const auto result = touch_resource->DeactivateGesture(); |
| 251 | 39 | ||
| 252 | next_state.delta = gesture.mid_point - last_entry.pos; | 40 | if (result.IsError()) { |
| 253 | next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; | 41 | return result; |
| 254 | next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; | ||
| 255 | last_pan_time_difference = time_difference; | ||
| 256 | |||
| 257 | // Promote to pinch type | ||
| 258 | if (std::abs(gesture.average_distance - last_gesture_props.average_distance) > | ||
| 259 | pinch_threshold) { | ||
| 260 | type = GestureType::Pinch; | ||
| 261 | next_state.scale = gesture.average_distance / last_gesture_props.average_distance; | ||
| 262 | } | 42 | } |
| 263 | 43 | ||
| 264 | const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture_props.angle) / | 44 | // TODO: return StopThread(); |
| 265 | (1 + (gesture.angle * last_gesture_props.angle))); | 45 | return ResultSuccess; |
| 266 | // Promote to rotate type | ||
| 267 | if (std::abs(angle_between_two_lines) > angle_threshold) { | ||
| 268 | type = GestureType::Rotate; | ||
| 269 | next_state.scale = 0; | ||
| 270 | next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; | ||
| 271 | } | ||
| 272 | } | 46 | } |
| 273 | 47 | ||
| 274 | void Gesture::EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | 48 | Result Gesture::IsActive(bool& out_is_active) const { |
| 275 | GestureType& type, f32 time_difference) { | 49 | out_is_active = touch_resource->IsGestureActive(); |
| 276 | const auto& last_entry = GetLastGestureEntry(); | 50 | return ResultSuccess; |
| 277 | next_state.vel_x = | ||
| 278 | static_cast<f32>(last_entry.delta.x) / (last_pan_time_difference + time_difference); | ||
| 279 | next_state.vel_y = | ||
| 280 | static_cast<f32>(last_entry.delta.y) / (last_pan_time_difference + time_difference); | ||
| 281 | const f32 curr_vel = | ||
| 282 | std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); | ||
| 283 | |||
| 284 | // Set swipe event with parameters | ||
| 285 | if (curr_vel > swipe_threshold) { | ||
| 286 | SetSwipeEvent(gesture, last_gesture_props, type); | ||
| 287 | return; | ||
| 288 | } | ||
| 289 | |||
| 290 | // End panning without swipe | ||
| 291 | type = GestureType::Complete; | ||
| 292 | next_state.vel_x = 0; | ||
| 293 | next_state.vel_y = 0; | ||
| 294 | force_update = true; | ||
| 295 | } | ||
| 296 | |||
| 297 | void Gesture::SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 298 | GestureType& type) { | ||
| 299 | const auto& last_entry = GetLastGestureEntry(); | ||
| 300 | |||
| 301 | type = GestureType::Swipe; | ||
| 302 | gesture = last_gesture_props; | ||
| 303 | force_update = true; | ||
| 304 | next_state.delta = last_entry.delta; | ||
| 305 | |||
| 306 | if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { | ||
| 307 | if (next_state.delta.x > 0) { | ||
| 308 | next_state.direction = GestureDirection::Right; | ||
| 309 | return; | ||
| 310 | } | ||
| 311 | next_state.direction = GestureDirection::Left; | ||
| 312 | return; | ||
| 313 | } | ||
| 314 | if (next_state.delta.y > 0) { | ||
| 315 | next_state.direction = GestureDirection::Down; | ||
| 316 | return; | ||
| 317 | } | ||
| 318 | next_state.direction = GestureDirection::Up; | ||
| 319 | } | ||
| 320 | |||
| 321 | const GestureState& Gesture::GetLastGestureEntry() const { | ||
| 322 | return shared_memory->gesture_lifo.ReadCurrentEntry().state; | ||
| 323 | } | ||
| 324 | |||
| 325 | GestureProperties Gesture::GetGestureProperties() { | ||
| 326 | GestureProperties gesture; | ||
| 327 | std::array<Core::HID::TouchFinger, MAX_POINTS> active_fingers; | ||
| 328 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | ||
| 329 | [](const auto& finger) { return finger.pressed; }); | ||
| 330 | gesture.active_points = | ||
| 331 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | ||
| 332 | |||
| 333 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 334 | const auto& [active_x, active_y] = active_fingers[id].position; | ||
| 335 | gesture.points[id] = { | ||
| 336 | .x = static_cast<s32>(active_x * Layout::ScreenUndocked::Width), | ||
| 337 | .y = static_cast<s32>(active_y * Layout::ScreenUndocked::Height), | ||
| 338 | }; | ||
| 339 | |||
| 340 | // Hack: There is no touch in docked but games still allow it | ||
| 341 | if (Settings::IsDockedMode()) { | ||
| 342 | gesture.points[id] = { | ||
| 343 | .x = static_cast<s32>(active_x * Layout::ScreenDocked::Width), | ||
| 344 | .y = static_cast<s32>(active_y * Layout::ScreenDocked::Height), | ||
| 345 | }; | ||
| 346 | } | ||
| 347 | |||
| 348 | gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); | ||
| 349 | gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); | ||
| 350 | } | ||
| 351 | |||
| 352 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 353 | const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + | ||
| 354 | Square(gesture.mid_point.y - gesture.points[id].y)); | ||
| 355 | gesture.average_distance += distance / static_cast<f32>(gesture.active_points); | ||
| 356 | } | ||
| 357 | |||
| 358 | gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), | ||
| 359 | static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); | ||
| 360 | |||
| 361 | gesture.detection_count = last_gesture.detection_count; | ||
| 362 | |||
| 363 | return gesture; | ||
| 364 | } | 51 | } |
| 365 | 52 | ||
| 366 | } // namespace Service::HID | 53 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <mutex> |
| 7 | 7 | ||
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "hid_core/resources/controller_base.h" | 9 | #include "core/hle/result.h" |
| 10 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 11 | |||
| 12 | namespace Core::HID { | ||
| 13 | class EmulatedConsole; | ||
| 14 | } | ||
| 15 | 10 | ||
| 16 | namespace Service::HID { | 11 | namespace Service::HID { |
| 17 | struct GestureSharedMemoryFormat; | 12 | class TouchResource; |
| 18 | 13 | ||
| 19 | class Gesture final : public ControllerBase { | 14 | /// Handles gesture request from HID interfaces |
| 15 | class Gesture { | ||
| 20 | public: | 16 | public: |
| 21 | explicit Gesture(Core::HID::HIDCore& hid_core_); | 17 | Gesture(std::shared_ptr<TouchResource> resource); |
| 22 | ~Gesture() override; | 18 | ~Gesture(); |
| 23 | 19 | ||
| 24 | // Called when the controller is initialized | 20 | Result Activate(); |
| 25 | void OnInit() override; | 21 | Result Activate(u64 aruid, u32 basic_gesture_id); |
| 26 | 22 | ||
| 27 | // When the controller is released | 23 | Result Deactivate(); |
| 28 | void OnRelease() override; | ||
| 29 | 24 | ||
| 30 | // When the controller is requesting an update for the shared memory | 25 | Result IsActive(bool& out_is_active) const; |
| 31 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 32 | 26 | ||
| 33 | private: | 27 | private: |
| 34 | // Reads input from all available input engines | 28 | mutable std::mutex mutex; |
| 35 | void ReadTouchInput(); | 29 | std::shared_ptr<TouchResource> touch_resource; |
| 36 | |||
| 37 | // Returns true if gesture state needs to be updated | ||
| 38 | bool ShouldUpdateGesture(const GestureProperties& gesture, f32 time_difference); | ||
| 39 | |||
| 40 | // Updates the shared memory to the next state | ||
| 41 | void UpdateGestureSharedMemory(GestureProperties& gesture, f32 time_difference); | ||
| 42 | |||
| 43 | // Initializes new gesture | ||
| 44 | void NewGesture(GestureProperties& gesture, GestureType& type, GestureAttribute& attributes); | ||
| 45 | |||
| 46 | // Updates existing gesture state | ||
| 47 | void UpdateExistingGesture(GestureProperties& gesture, GestureType& type, f32 time_difference); | ||
| 48 | |||
| 49 | // Terminates exiting gesture | ||
| 50 | void EndGesture(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 51 | GestureType& type, GestureAttribute& attributes, f32 time_difference); | ||
| 52 | |||
| 53 | // Set current event to a tap event | ||
| 54 | void SetTapEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 55 | GestureType& type, GestureAttribute& attributes); | ||
| 56 | |||
| 57 | // Calculates and set the extra parameters related to a pan event | ||
| 58 | void UpdatePanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 59 | GestureType& type, f32 time_difference); | ||
| 60 | |||
| 61 | // Terminates the pan event | ||
| 62 | void EndPanEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 63 | GestureType& type, f32 time_difference); | ||
| 64 | |||
| 65 | // Set current event to a swipe event | ||
| 66 | void SetSwipeEvent(GestureProperties& gesture, GestureProperties& last_gesture_props, | ||
| 67 | GestureType& type); | ||
| 68 | |||
| 69 | // Retrieves the last gesture entry, as indicated by shared memory indices. | ||
| 70 | [[nodiscard]] const GestureState& GetLastGestureEntry() const; | ||
| 71 | |||
| 72 | // Returns the average distance, angle and middle point of the active fingers | ||
| 73 | GestureProperties GetGestureProperties(); | ||
| 74 | |||
| 75 | GestureState next_state{}; | ||
| 76 | GestureSharedMemoryFormat* shared_memory; | ||
| 77 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 78 | |||
| 79 | std::array<Core::HID::TouchFinger, MAX_POINTS> fingers{}; | ||
| 80 | GestureProperties last_gesture{}; | ||
| 81 | s64 last_update_timestamp{}; | ||
| 82 | s64 last_tap_timestamp{}; | ||
| 83 | f32 last_pan_time_difference{}; | ||
| 84 | bool force_update{false}; | ||
| 85 | bool enable_press_and_tap{false}; | ||
| 86 | }; | 30 | }; |
| 31 | |||
| 87 | } // namespace Service::HID | 32 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/math_util.h" | ||
| 5 | #include "hid_core/resources/touch_screen/gesture_handler.h" | ||
| 6 | |||
| 7 | namespace Service::HID { | ||
| 8 | |||
| 9 | constexpr f32 Square(s32 num) { | ||
| 10 | return static_cast<f32>(num * num); | ||
| 11 | } | ||
| 12 | |||
| 13 | GestureHandler::GestureHandler() {} | ||
| 14 | |||
| 15 | GestureHandler::~GestureHandler() {} | ||
| 16 | |||
| 17 | void GestureHandler::SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp) { | ||
| 18 | gesture = {}; | ||
| 19 | gesture.active_points = std::min(MaxPoints, static_cast<std::size_t>(count)); | ||
| 20 | |||
| 21 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 22 | const auto& [active_x, active_y] = touch_state[id].position; | ||
| 23 | gesture.points[id] = { | ||
| 24 | .x = static_cast<s32>(active_x), | ||
| 25 | .y = static_cast<s32>(active_y), | ||
| 26 | }; | ||
| 27 | |||
| 28 | gesture.mid_point.x += static_cast<s32>(gesture.points[id].x / gesture.active_points); | ||
| 29 | gesture.mid_point.y += static_cast<s32>(gesture.points[id].y / gesture.active_points); | ||
| 30 | } | ||
| 31 | |||
| 32 | for (size_t id = 0; id < gesture.active_points; ++id) { | ||
| 33 | const f32 distance = std::sqrt(Square(gesture.mid_point.x - gesture.points[id].x) + | ||
| 34 | Square(gesture.mid_point.y - gesture.points[id].y)); | ||
| 35 | gesture.average_distance += distance / static_cast<f32>(gesture.active_points); | ||
| 36 | } | ||
| 37 | |||
| 38 | gesture.angle = std::atan2(static_cast<f32>(gesture.mid_point.y - gesture.points[0].y), | ||
| 39 | static_cast<f32>(gesture.mid_point.x - gesture.points[0].x)); | ||
| 40 | |||
| 41 | gesture.detection_count = last_gesture.detection_count; | ||
| 42 | |||
| 43 | if (last_update_timestamp > timestamp) { | ||
| 44 | timestamp = last_tap_timestamp; | ||
| 45 | } | ||
| 46 | |||
| 47 | time_difference = static_cast<f32>(timestamp - last_update_timestamp) / (1000 * 1000 * 1000); | ||
| 48 | } | ||
| 49 | |||
| 50 | bool GestureHandler::NeedsUpdate() { | ||
| 51 | if (force_update) { | ||
| 52 | force_update = false; | ||
| 53 | return true; | ||
| 54 | } | ||
| 55 | |||
| 56 | // Update if coordinates change | ||
| 57 | for (size_t id = 0; id < MaxPoints; id++) { | ||
| 58 | if (gesture.points[id] != last_gesture.points[id]) { | ||
| 59 | return true; | ||
| 60 | } | ||
| 61 | } | ||
| 62 | |||
| 63 | // Update on press and hold event after 0.5 seconds | ||
| 64 | if (last_gesture_state.type == GestureType::Touch && last_gesture_state.point_count == 1 && | ||
| 65 | time_difference > PressDelay) { | ||
| 66 | return enable_press_and_tap; | ||
| 67 | } | ||
| 68 | |||
| 69 | return false; | ||
| 70 | } | ||
| 71 | |||
| 72 | void GestureHandler::UpdateGestureState(GestureState& next_state, s64 timestamp) { | ||
| 73 | last_update_timestamp = timestamp; | ||
| 74 | |||
| 75 | GestureType type = GestureType::Idle; | ||
| 76 | GestureAttribute attributes{}; | ||
| 77 | |||
| 78 | // Reset next state to default | ||
| 79 | next_state.sampling_number = last_gesture_state.sampling_number + 1; | ||
| 80 | next_state.delta = {}; | ||
| 81 | next_state.vel_x = 0; | ||
| 82 | next_state.vel_y = 0; | ||
| 83 | next_state.direction = GestureDirection::None; | ||
| 84 | next_state.rotation_angle = 0; | ||
| 85 | next_state.scale = 0; | ||
| 86 | |||
| 87 | if (gesture.active_points > 0) { | ||
| 88 | if (last_gesture.active_points == 0) { | ||
| 89 | NewGesture(type, attributes); | ||
| 90 | } else { | ||
| 91 | UpdateExistingGesture(next_state, type); | ||
| 92 | } | ||
| 93 | } else { | ||
| 94 | EndGesture(next_state, type, attributes); | ||
| 95 | } | ||
| 96 | |||
| 97 | // Apply attributes | ||
| 98 | next_state.detection_count = gesture.detection_count; | ||
| 99 | next_state.type = type; | ||
| 100 | next_state.attributes = attributes; | ||
| 101 | next_state.pos = gesture.mid_point; | ||
| 102 | next_state.point_count = static_cast<s32>(gesture.active_points); | ||
| 103 | next_state.points = gesture.points; | ||
| 104 | last_gesture = gesture; | ||
| 105 | last_gesture_state = next_state; | ||
| 106 | } | ||
| 107 | |||
| 108 | void GestureHandler::NewGesture(GestureType& type, GestureAttribute& attributes) { | ||
| 109 | gesture.detection_count++; | ||
| 110 | type = GestureType::Touch; | ||
| 111 | |||
| 112 | // New touch after cancel is not considered new | ||
| 113 | if (last_gesture_state.type != GestureType::Cancel) { | ||
| 114 | attributes.is_new_touch.Assign(1); | ||
| 115 | enable_press_and_tap = true; | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | void GestureHandler::UpdateExistingGesture(GestureState& next_state, GestureType& type) { | ||
| 120 | // Promote to pan type if touch moved | ||
| 121 | for (size_t id = 0; id < MaxPoints; id++) { | ||
| 122 | if (gesture.points[id] != last_gesture.points[id]) { | ||
| 123 | type = GestureType::Pan; | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | // Number of fingers changed cancel the last event and clear data | ||
| 129 | if (gesture.active_points != last_gesture.active_points) { | ||
| 130 | type = GestureType::Cancel; | ||
| 131 | enable_press_and_tap = false; | ||
| 132 | gesture.active_points = 0; | ||
| 133 | gesture.mid_point = {}; | ||
| 134 | gesture.points.fill({}); | ||
| 135 | return; | ||
| 136 | } | ||
| 137 | |||
| 138 | // Calculate extra parameters of panning | ||
| 139 | if (type == GestureType::Pan) { | ||
| 140 | UpdatePanEvent(next_state, type); | ||
| 141 | return; | ||
| 142 | } | ||
| 143 | |||
| 144 | // Promote to press type | ||
| 145 | if (last_gesture_state.type == GestureType::Touch) { | ||
| 146 | type = GestureType::Press; | ||
| 147 | } | ||
| 148 | } | ||
| 149 | |||
| 150 | void GestureHandler::EndGesture(GestureState& next_state, GestureType& type, | ||
| 151 | GestureAttribute& attributes) { | ||
| 152 | if (last_gesture.active_points != 0) { | ||
| 153 | switch (last_gesture_state.type) { | ||
| 154 | case GestureType::Touch: | ||
| 155 | if (enable_press_and_tap) { | ||
| 156 | SetTapEvent(type, attributes); | ||
| 157 | return; | ||
| 158 | } | ||
| 159 | type = GestureType::Cancel; | ||
| 160 | force_update = true; | ||
| 161 | break; | ||
| 162 | case GestureType::Press: | ||
| 163 | case GestureType::Tap: | ||
| 164 | case GestureType::Swipe: | ||
| 165 | case GestureType::Pinch: | ||
| 166 | case GestureType::Rotate: | ||
| 167 | type = GestureType::Complete; | ||
| 168 | force_update = true; | ||
| 169 | break; | ||
| 170 | case GestureType::Pan: | ||
| 171 | EndPanEvent(next_state, type); | ||
| 172 | break; | ||
| 173 | default: | ||
| 174 | break; | ||
| 175 | } | ||
| 176 | return; | ||
| 177 | } | ||
| 178 | if (last_gesture_state.type == GestureType::Complete || | ||
| 179 | last_gesture_state.type == GestureType::Cancel) { | ||
| 180 | gesture.detection_count++; | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | void GestureHandler::SetTapEvent(GestureType& type, GestureAttribute& attributes) { | ||
| 185 | type = GestureType::Tap; | ||
| 186 | gesture = last_gesture; | ||
| 187 | force_update = true; | ||
| 188 | f32 tap_time_difference = | ||
| 189 | static_cast<f32>(last_update_timestamp - last_tap_timestamp) / (1000 * 1000 * 1000); | ||
| 190 | last_tap_timestamp = last_update_timestamp; | ||
| 191 | if (tap_time_difference < DoubleTapDelay) { | ||
| 192 | attributes.is_double_tap.Assign(1); | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | void GestureHandler::UpdatePanEvent(GestureState& next_state, GestureType& type) { | ||
| 197 | next_state.delta = gesture.mid_point - last_gesture_state.pos; | ||
| 198 | next_state.vel_x = static_cast<f32>(next_state.delta.x) / time_difference; | ||
| 199 | next_state.vel_y = static_cast<f32>(next_state.delta.y) / time_difference; | ||
| 200 | last_pan_time_difference = time_difference; | ||
| 201 | |||
| 202 | // Promote to pinch type | ||
| 203 | if (std::abs(gesture.average_distance - last_gesture.average_distance) > PinchThreshold) { | ||
| 204 | type = GestureType::Pinch; | ||
| 205 | next_state.scale = gesture.average_distance / last_gesture.average_distance; | ||
| 206 | } | ||
| 207 | |||
| 208 | const f32 angle_between_two_lines = std::atan((gesture.angle - last_gesture.angle) / | ||
| 209 | (1 + (gesture.angle * last_gesture.angle))); | ||
| 210 | // Promote to rotate type | ||
| 211 | if (std::abs(angle_between_two_lines) > AngleThreshold) { | ||
| 212 | type = GestureType::Rotate; | ||
| 213 | next_state.scale = 0; | ||
| 214 | next_state.rotation_angle = angle_between_two_lines * 180.0f / Common::PI; | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | void GestureHandler::EndPanEvent(GestureState& next_state, GestureType& type) { | ||
| 219 | next_state.vel_x = | ||
| 220 | static_cast<f32>(last_gesture_state.delta.x) / (last_pan_time_difference + time_difference); | ||
| 221 | next_state.vel_y = | ||
| 222 | static_cast<f32>(last_gesture_state.delta.y) / (last_pan_time_difference + time_difference); | ||
| 223 | const f32 curr_vel = | ||
| 224 | std::sqrt((next_state.vel_x * next_state.vel_x) + (next_state.vel_y * next_state.vel_y)); | ||
| 225 | |||
| 226 | // Set swipe event with parameters | ||
| 227 | if (curr_vel > SwipeThreshold) { | ||
| 228 | SetSwipeEvent(next_state, type); | ||
| 229 | return; | ||
| 230 | } | ||
| 231 | |||
| 232 | // End panning without swipe | ||
| 233 | type = GestureType::Complete; | ||
| 234 | next_state.vel_x = 0; | ||
| 235 | next_state.vel_y = 0; | ||
| 236 | force_update = true; | ||
| 237 | } | ||
| 238 | |||
| 239 | void GestureHandler::SetSwipeEvent(GestureState& next_state, GestureType& type) { | ||
| 240 | type = GestureType::Swipe; | ||
| 241 | gesture = last_gesture; | ||
| 242 | force_update = true; | ||
| 243 | next_state.delta = last_gesture_state.delta; | ||
| 244 | |||
| 245 | if (std::abs(next_state.delta.x) > std::abs(next_state.delta.y)) { | ||
| 246 | if (next_state.delta.x > 0) { | ||
| 247 | next_state.direction = GestureDirection::Right; | ||
| 248 | return; | ||
| 249 | } | ||
| 250 | next_state.direction = GestureDirection::Left; | ||
| 251 | return; | ||
| 252 | } | ||
| 253 | if (next_state.delta.y > 0) { | ||
| 254 | next_state.direction = GestureDirection::Down; | ||
| 255 | return; | ||
| 256 | } | ||
| 257 | next_state.direction = GestureDirection::Up; | ||
| 258 | } | ||
| 259 | |||
| 260 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <span> | ||
| 7 | |||
| 8 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 9 | |||
| 10 | namespace Service::HID { | ||
| 11 | |||
| 12 | class GestureHandler { | ||
| 13 | public: | ||
| 14 | GestureHandler(); | ||
| 15 | ~GestureHandler(); | ||
| 16 | |||
| 17 | void SetTouchState(std::span<TouchState> touch_state, u32 count, s64 timestamp); | ||
| 18 | |||
| 19 | bool NeedsUpdate(); | ||
| 20 | void UpdateGestureState(GestureState& next_state, s64 timestamp); | ||
| 21 | |||
| 22 | private: | ||
| 23 | // Initializes new gesture | ||
| 24 | void NewGesture(GestureType& type, GestureAttribute& attributes); | ||
| 25 | |||
| 26 | // Updates existing gesture state | ||
| 27 | void UpdateExistingGesture(GestureState& next_state, GestureType& type); | ||
| 28 | |||
| 29 | // Terminates exiting gesture | ||
| 30 | void EndGesture(GestureState& next_state, GestureType& type, GestureAttribute& attributes); | ||
| 31 | |||
| 32 | // Set current event to a tap event | ||
| 33 | void SetTapEvent(GestureType& type, GestureAttribute& attributes); | ||
| 34 | |||
| 35 | // Calculates and set the extra parameters related to a pan event | ||
| 36 | void UpdatePanEvent(GestureState& next_state, GestureType& type); | ||
| 37 | |||
| 38 | // Terminates the pan event | ||
| 39 | void EndPanEvent(GestureState& next_state, GestureType& type); | ||
| 40 | |||
| 41 | // Set current event to a swipe event | ||
| 42 | void SetSwipeEvent(GestureState& next_state, GestureType& type); | ||
| 43 | |||
| 44 | GestureProperties gesture{}; | ||
| 45 | GestureProperties last_gesture{}; | ||
| 46 | GestureState last_gesture_state{}; | ||
| 47 | s64 last_update_timestamp{}; | ||
| 48 | s64 last_tap_timestamp{}; | ||
| 49 | f32 last_pan_time_difference{}; | ||
| 50 | f32 time_difference{}; | ||
| 51 | bool force_update{true}; | ||
| 52 | bool enable_press_and_tap{false}; | ||
| 53 | }; | ||
| 54 | |||
| 55 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include "common/bit_field.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/point.h" | ||
| 10 | |||
| 11 | namespace Service::HID { | ||
| 12 | static constexpr size_t MAX_FINGERS = 16; | ||
| 13 | static constexpr size_t MAX_POINTS = 4; | ||
| 14 | |||
| 15 | // This is nn::hid::GestureType | ||
| 16 | enum class GestureType : u32 { | ||
| 17 | Idle, // Nothing touching the screen | ||
| 18 | Complete, // Set at the end of a touch event | ||
| 19 | Cancel, // Set when the number of fingers change | ||
| 20 | Touch, // A finger just touched the screen | ||
| 21 | Press, // Set if last type is touch and the finger hasn't moved | ||
| 22 | Tap, // Fast press then release | ||
| 23 | Pan, // All points moving together across the screen | ||
| 24 | Swipe, // Fast press movement and release of a single point | ||
| 25 | Pinch, // All points moving away/closer to the midpoint | ||
| 26 | Rotate, // All points rotating from the midpoint | ||
| 27 | }; | ||
| 28 | |||
| 29 | // This is nn::hid::GestureDirection | ||
| 30 | enum class GestureDirection : u32 { | ||
| 31 | None, | ||
| 32 | Left, | ||
| 33 | Up, | ||
| 34 | Right, | ||
| 35 | Down, | ||
| 36 | }; | ||
| 37 | |||
| 38 | // This is nn::hid::GestureAttribute | ||
| 39 | struct GestureAttribute { | ||
| 40 | union { | ||
| 41 | u32 raw{}; | ||
| 42 | |||
| 43 | BitField<4, 1, u32> is_new_touch; | ||
| 44 | BitField<8, 1, u32> is_double_tap; | ||
| 45 | }; | ||
| 46 | }; | ||
| 47 | static_assert(sizeof(GestureAttribute) == 4, "GestureAttribute is an invalid size"); | ||
| 48 | |||
| 49 | // This is nn::hid::GestureState | ||
| 50 | struct GestureState { | ||
| 51 | s64 sampling_number{}; | ||
| 52 | s64 detection_count{}; | ||
| 53 | GestureType type{GestureType::Idle}; | ||
| 54 | GestureDirection direction{GestureDirection::None}; | ||
| 55 | Common::Point<s32> pos{}; | ||
| 56 | Common::Point<s32> delta{}; | ||
| 57 | f32 vel_x{}; | ||
| 58 | f32 vel_y{}; | ||
| 59 | GestureAttribute attributes{}; | ||
| 60 | f32 scale{}; | ||
| 61 | f32 rotation_angle{}; | ||
| 62 | s32 point_count{}; | ||
| 63 | std::array<Common::Point<s32>, 4> points{}; | ||
| 64 | }; | ||
| 65 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); | ||
| 66 | |||
| 67 | struct GestureProperties { | ||
| 68 | std::array<Common::Point<s32>, MAX_POINTS> points{}; | ||
| 69 | std::size_t active_points{}; | ||
| 70 | Common::Point<s32> mid_point{}; | ||
| 71 | s64 detection_count{}; | ||
| 72 | u64 delta_time{}; | ||
| 73 | f32 average_distance{}; | ||
| 74 | f32 angle{}; | ||
| 75 | }; | ||
| 76 | |||
| 77 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include "hid_core/hid_types.h" |
| 5 | #include "common/common_types.h" | ||
| 6 | #include "common/settings.h" | ||
| 7 | #include "core/core_timing.h" | ||
| 8 | #include "core/frontend/emu_window.h" | ||
| 9 | #include "hid_core/frontend/emulated_console.h" | ||
| 10 | #include "hid_core/hid_core.h" | ||
| 11 | #include "hid_core/resources/applet_resource.h" | ||
| 12 | #include "hid_core/resources/shared_memory_format.h" | ||
| 13 | #include "hid_core/resources/touch_screen/touch_screen.h" | 5 | #include "hid_core/resources/touch_screen/touch_screen.h" |
| 6 | #include "hid_core/resources/touch_screen/touch_screen_resource.h" | ||
| 14 | 7 | ||
| 15 | namespace Service::HID { | 8 | namespace Service::HID { |
| 16 | 9 | ||
| 17 | TouchScreen::TouchScreen(Core::HID::HIDCore& hid_core_) | 10 | TouchScreen::TouchScreen(std::shared_ptr<TouchResource> resource) : touch_resource{resource} {} |
| 18 | : ControllerBase{hid_core_}, touchscreen_width(Layout::ScreenUndocked::Width), | ||
| 19 | touchscreen_height(Layout::ScreenUndocked::Height) { | ||
| 20 | console = hid_core.GetEmulatedConsole(); | ||
| 21 | } | ||
| 22 | 11 | ||
| 23 | TouchScreen::~TouchScreen() = default; | 12 | TouchScreen::~TouchScreen() = default; |
| 24 | 13 | ||
| 25 | void TouchScreen::OnInit() {} | 14 | Result TouchScreen::Activate() { |
| 15 | std::scoped_lock lock{mutex}; | ||
| 26 | 16 | ||
| 27 | void TouchScreen::OnRelease() {} | 17 | // TODO: Result result = CreateThread(); |
| 28 | 18 | Result result = ResultSuccess; | |
| 29 | void TouchScreen::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 19 | if (result.IsError()) { |
| 30 | const u64 aruid = applet_resource->GetActiveAruid(); | 20 | return result; |
| 31 | auto* data = applet_resource->GetAruidData(aruid); | 21 | } |
| 32 | 22 | ||
| 33 | if (data == nullptr || !data->flag.is_assigned) { | 23 | result = touch_resource->ActivateTouch(); |
| 34 | return; | 24 | if (result.IsError()) { |
| 25 | // TODO: StopThread(); | ||
| 35 | } | 26 | } |
| 36 | 27 | ||
| 37 | TouchScreenSharedMemoryFormat& shared_memory = data->shared_memory_format->touch_screen; | 28 | return result; |
| 38 | shared_memory.touch_screen_lifo.timestamp = core_timing.GetGlobalTimeNs().count(); | 29 | } |
| 39 | 30 | ||
| 40 | if (!IsControllerActivated()) { | 31 | Result TouchScreen::Activate(u64 aruid) { |
| 41 | shared_memory.touch_screen_lifo.buffer_count = 0; | 32 | std::scoped_lock lock{mutex}; |
| 42 | shared_memory.touch_screen_lifo.buffer_tail = 0; | 33 | return touch_resource->ActivateTouch(aruid); |
| 43 | return; | 34 | } |
| 44 | } | ||
| 45 | 35 | ||
| 46 | const auto touch_status = console->GetTouch(); | 36 | Result TouchScreen::Deactivate() { |
| 47 | for (std::size_t id = 0; id < MAX_FINGERS; id++) { | 37 | std::scoped_lock lock{mutex}; |
| 48 | const auto& current_touch = touch_status[id]; | 38 | const auto result = touch_resource->DeactivateTouch(); |
| 49 | auto& finger = fingers[id]; | ||
| 50 | finger.id = current_touch.id; | ||
| 51 | |||
| 52 | if (finger.attribute.start_touch) { | ||
| 53 | finger.attribute.raw = 0; | ||
| 54 | continue; | ||
| 55 | } | ||
| 56 | |||
| 57 | if (finger.attribute.end_touch) { | ||
| 58 | finger.attribute.raw = 0; | ||
| 59 | finger.pressed = false; | ||
| 60 | continue; | ||
| 61 | } | ||
| 62 | |||
| 63 | if (!finger.pressed && current_touch.pressed) { | ||
| 64 | // Ignore all touch fingers if disabled | ||
| 65 | if (!Settings::values.touchscreen.enabled) { | ||
| 66 | continue; | ||
| 67 | } | ||
| 68 | |||
| 69 | finger.attribute.start_touch.Assign(1); | ||
| 70 | finger.pressed = true; | ||
| 71 | finger.position = current_touch.position; | ||
| 72 | continue; | ||
| 73 | } | ||
| 74 | |||
| 75 | if (finger.pressed && !current_touch.pressed) { | ||
| 76 | finger.attribute.raw = 0; | ||
| 77 | finger.attribute.end_touch.Assign(1); | ||
| 78 | continue; | ||
| 79 | } | ||
| 80 | |||
| 81 | // Only update position if touch is not on a special frame | ||
| 82 | finger.position = current_touch.position; | ||
| 83 | } | ||
| 84 | 39 | ||
| 85 | std::array<Core::HID::TouchFinger, MAX_FINGERS> active_fingers; | 40 | if (result.IsError()) { |
| 86 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | 41 | return result; |
| 87 | [](const auto& finger) { return finger.pressed; }); | ||
| 88 | const auto active_fingers_count = | ||
| 89 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | ||
| 90 | |||
| 91 | const u64 timestamp = static_cast<u64>(core_timing.GetGlobalTimeNs().count()); | ||
| 92 | const auto& last_entry = shared_memory.touch_screen_lifo.ReadCurrentEntry().state; | ||
| 93 | |||
| 94 | next_state.sampling_number = last_entry.sampling_number + 1; | ||
| 95 | next_state.entry_count = static_cast<s32>(active_fingers_count); | ||
| 96 | |||
| 97 | for (std::size_t id = 0; id < MAX_FINGERS; ++id) { | ||
| 98 | auto& touch_entry = next_state.states[id]; | ||
| 99 | if (id < active_fingers_count) { | ||
| 100 | const auto& [active_x, active_y] = active_fingers[id].position; | ||
| 101 | touch_entry.position = { | ||
| 102 | .x = static_cast<u16>(active_x * static_cast<float>(touchscreen_width)), | ||
| 103 | .y = static_cast<u16>(active_y * static_cast<float>(touchscreen_height)), | ||
| 104 | }; | ||
| 105 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | ||
| 106 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | ||
| 107 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; | ||
| 108 | touch_entry.delta_time = timestamp - active_fingers[id].last_touch; | ||
| 109 | fingers[active_fingers[id].id].last_touch = timestamp; | ||
| 110 | touch_entry.finger = active_fingers[id].id; | ||
| 111 | touch_entry.attribute.raw = active_fingers[id].attribute.raw; | ||
| 112 | } else { | ||
| 113 | // Clear touch entry | ||
| 114 | touch_entry.attribute.raw = 0; | ||
| 115 | touch_entry.position = {}; | ||
| 116 | touch_entry.diameter_x = 0; | ||
| 117 | touch_entry.diameter_y = 0; | ||
| 118 | touch_entry.rotation_angle = 0; | ||
| 119 | touch_entry.delta_time = 0; | ||
| 120 | touch_entry.finger = 0; | ||
| 121 | } | ||
| 122 | } | 42 | } |
| 123 | 43 | ||
| 124 | shared_memory.touch_screen_lifo.WriteNextEntry(next_state); | 44 | // TODO: return StopThread(); |
| 45 | return ResultSuccess; | ||
| 46 | } | ||
| 47 | |||
| 48 | Result TouchScreen::IsActive(bool& out_is_active) const { | ||
| 49 | out_is_active = touch_resource->IsTouchActive(); | ||
| 50 | return ResultSuccess; | ||
| 51 | } | ||
| 52 | |||
| 53 | Result TouchScreen::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) { | ||
| 54 | std::scoped_lock lock{mutex}; | ||
| 55 | return touch_resource->SetTouchScreenAutoPilotState(auto_pilot_state); | ||
| 56 | } | ||
| 57 | |||
| 58 | Result TouchScreen::UnsetTouchScreenAutoPilotState() { | ||
| 59 | std::scoped_lock lock{mutex}; | ||
| 60 | return touch_resource->UnsetTouchScreenAutoPilotState(); | ||
| 61 | } | ||
| 62 | |||
| 63 | Result TouchScreen::RequestNextTouchInput() { | ||
| 64 | std::scoped_lock lock{mutex}; | ||
| 65 | return touch_resource->RequestNextTouchInput(); | ||
| 66 | } | ||
| 67 | |||
| 68 | Result TouchScreen::RequestNextDummyInput() { | ||
| 69 | std::scoped_lock lock{mutex}; | ||
| 70 | return touch_resource->RequestNextDummyInput(); | ||
| 71 | } | ||
| 72 | |||
| 73 | Result TouchScreen::ProcessTouchScreenAutoTune() { | ||
| 74 | std::scoped_lock lock{mutex}; | ||
| 75 | return touch_resource->ProcessTouchScreenAutoTune(); | ||
| 76 | } | ||
| 77 | |||
| 78 | Result TouchScreen::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, | ||
| 79 | f32 point2_y) { | ||
| 80 | std::scoped_lock lock{mutex}; | ||
| 81 | touch_resource->SetTouchScreenMagnification(point1_x, point1_y, point2_x, point2_y); | ||
| 82 | return ResultSuccess; | ||
| 83 | } | ||
| 84 | |||
| 85 | Result TouchScreen::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) { | ||
| 86 | std::scoped_lock lock{mutex}; | ||
| 87 | return touch_resource->SetTouchScreenResolution(width, height, aruid); | ||
| 88 | } | ||
| 89 | |||
| 90 | Result TouchScreen::SetTouchScreenConfiguration( | ||
| 91 | const Core::HID::TouchScreenConfigurationForNx& mode, u64 aruid) { | ||
| 92 | std::scoped_lock lock{mutex}; | ||
| 93 | return touch_resource->SetTouchScreenConfiguration(mode, aruid); | ||
| 94 | } | ||
| 95 | |||
| 96 | Result TouchScreen::GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode, | ||
| 97 | u64 aruid) const { | ||
| 98 | std::scoped_lock lock{mutex}; | ||
| 99 | return touch_resource->GetTouchScreenConfiguration(out_mode, aruid); | ||
| 100 | } | ||
| 101 | |||
| 102 | Result TouchScreen::SetTouchScreenDefaultConfiguration( | ||
| 103 | const Core::HID::TouchScreenConfigurationForNx& mode) { | ||
| 104 | std::scoped_lock lock{mutex}; | ||
| 105 | return touch_resource->SetTouchScreenDefaultConfiguration(mode); | ||
| 106 | } | ||
| 107 | |||
| 108 | Result TouchScreen::GetTouchScreenDefaultConfiguration( | ||
| 109 | Core::HID::TouchScreenConfigurationForNx& out_mode) const { | ||
| 110 | std::scoped_lock lock{mutex}; | ||
| 111 | return touch_resource->GetTouchScreenDefaultConfiguration(out_mode); | ||
| 125 | } | 112 | } |
| 126 | 113 | ||
| 127 | void TouchScreen::SetTouchscreenDimensions(u32 width, u32 height) { | 114 | void TouchScreen::OnTouchUpdate(u64 timestamp) { |
| 128 | touchscreen_width = width; | 115 | std::scoped_lock lock{mutex}; |
| 129 | touchscreen_height = height; | 116 | touch_resource->OnTouchUpdate(timestamp); |
| 130 | } | 117 | } |
| 131 | 118 | ||
| 132 | } // namespace Service::HID | 119 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <mutex> |
| 7 | 7 | ||
| 8 | #include "hid_core/hid_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "hid_core/resources/controller_base.h" | 9 | #include "core/hle/result.h" |
| 10 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 11 | 10 | ||
| 12 | namespace Core::HID { | 11 | namespace Core::HID { |
| 13 | class EmulatedConsole; | 12 | struct TouchScreenConfigurationForNx; |
| 14 | } // namespace Core::HID | 13 | } |
| 14 | |||
| 15 | namespace Core::Timing { | ||
| 16 | struct EventType; | ||
| 17 | } | ||
| 15 | 18 | ||
| 16 | namespace Service::HID { | 19 | namespace Service::HID { |
| 17 | struct TouchScreenSharedMemoryFormat; | 20 | class TouchResource; |
| 21 | struct AutoPilotState; | ||
| 18 | 22 | ||
| 19 | class TouchScreen final : public ControllerBase { | 23 | /// Handles touch request from HID interfaces |
| 24 | class TouchScreen { | ||
| 20 | public: | 25 | public: |
| 21 | explicit TouchScreen(Core::HID::HIDCore& hid_core_); | 26 | TouchScreen(std::shared_ptr<TouchResource> resource); |
| 22 | ~TouchScreen() override; | 27 | ~TouchScreen(); |
| 23 | 28 | ||
| 24 | // Called when the controller is initialized | 29 | Result Activate(); |
| 25 | void OnInit() override; | 30 | Result Activate(u64 aruid); |
| 26 | 31 | ||
| 27 | // When the controller is released | 32 | Result Deactivate(); |
| 28 | void OnRelease() override; | ||
| 29 | 33 | ||
| 30 | // When the controller is requesting an update for the shared memory | 34 | Result IsActive(bool& out_is_active) const; |
| 31 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | ||
| 32 | 35 | ||
| 33 | void SetTouchscreenDimensions(u32 width, u32 height); | 36 | Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state); |
| 37 | Result UnsetTouchScreenAutoPilotState(); | ||
| 34 | 38 | ||
| 35 | private: | 39 | Result RequestNextTouchInput(); |
| 36 | TouchScreenState next_state{}; | 40 | Result RequestNextDummyInput(); |
| 37 | Core::HID::EmulatedConsole* console = nullptr; | 41 | |
| 42 | Result ProcessTouchScreenAutoTune(); | ||
| 43 | |||
| 44 | Result SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y); | ||
| 45 | Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid); | ||
| 46 | |||
| 47 | Result SetTouchScreenConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode, | ||
| 48 | u64 aruid); | ||
| 49 | Result GetTouchScreenConfiguration(Core::HID::TouchScreenConfigurationForNx& out_mode, | ||
| 50 | u64 aruid) const; | ||
| 38 | 51 | ||
| 39 | std::array<Core::HID::TouchFinger, MAX_FINGERS> fingers{}; | 52 | Result SetTouchScreenDefaultConfiguration(const Core::HID::TouchScreenConfigurationForNx& mode); |
| 40 | u32 touchscreen_width; | 53 | Result GetTouchScreenDefaultConfiguration( |
| 41 | u32 touchscreen_height; | 54 | Core::HID::TouchScreenConfigurationForNx& out_mode) const; |
| 55 | |||
| 56 | void OnTouchUpdate(u64 timestamp); | ||
| 57 | |||
| 58 | private: | ||
| 59 | mutable std::mutex mutex; | ||
| 60 | std::shared_ptr<TouchResource> touch_resource; | ||
| 61 | std::shared_ptr<Core::Timing::EventType> touch_update_event; | ||
| 42 | }; | 62 | }; |
| 63 | |||
| 43 | } // namespace Service::HID | 64 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include <algorithm> | ||
| 5 | #include "common/settings.h" | ||
| 6 | #include "core/frontend/emu_window.h" | ||
| 7 | #include "hid_core/hid_core.h" | ||
| 8 | #include "hid_core/resources/touch_screen/touch_screen_driver.h" | ||
| 9 | |||
| 10 | namespace Service::HID { | ||
| 11 | |||
| 12 | TouchDriver::TouchDriver(Core::HID::HIDCore& hid_core) { | ||
| 13 | console = hid_core.GetEmulatedConsole(); | ||
| 14 | } | ||
| 15 | |||
| 16 | TouchDriver::~TouchDriver() = default; | ||
| 17 | |||
| 18 | Result TouchDriver::StartTouchSensor() { | ||
| 19 | is_running = true; | ||
| 20 | return ResultSuccess; | ||
| 21 | } | ||
| 22 | |||
| 23 | Result TouchDriver::StopTouchSensor() { | ||
| 24 | is_running = false; | ||
| 25 | return ResultSuccess; | ||
| 26 | } | ||
| 27 | |||
| 28 | bool TouchDriver::IsRunning() const { | ||
| 29 | return is_running; | ||
| 30 | } | ||
| 31 | |||
| 32 | void TouchDriver::ProcessTouchScreenAutoTune() const { | ||
| 33 | // TODO | ||
| 34 | } | ||
| 35 | |||
| 36 | Result TouchDriver::WaitForDummyInput() { | ||
| 37 | touch_status = {}; | ||
| 38 | return ResultSuccess; | ||
| 39 | } | ||
| 40 | |||
| 41 | Result TouchDriver::WaitForInput() { | ||
| 42 | touch_status = {}; | ||
| 43 | const auto touch_input = console->GetTouch(); | ||
| 44 | for (std::size_t id = 0; id < touch_status.states.size(); id++) { | ||
| 45 | const auto& current_touch = touch_input[id]; | ||
| 46 | auto& finger = fingers[id]; | ||
| 47 | finger.id = current_touch.id; | ||
| 48 | |||
| 49 | if (finger.attribute.start_touch) { | ||
| 50 | finger.attribute.raw = 0; | ||
| 51 | continue; | ||
| 52 | } | ||
| 53 | |||
| 54 | if (finger.attribute.end_touch) { | ||
| 55 | finger.attribute.raw = 0; | ||
| 56 | finger.pressed = false; | ||
| 57 | continue; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (!finger.pressed && current_touch.pressed) { | ||
| 61 | finger.attribute.start_touch.Assign(1); | ||
| 62 | finger.pressed = true; | ||
| 63 | finger.position = current_touch.position; | ||
| 64 | continue; | ||
| 65 | } | ||
| 66 | |||
| 67 | if (finger.pressed && !current_touch.pressed) { | ||
| 68 | finger.attribute.raw = 0; | ||
| 69 | finger.attribute.end_touch.Assign(1); | ||
| 70 | continue; | ||
| 71 | } | ||
| 72 | |||
| 73 | // Only update position if touch is not on a special frame | ||
| 74 | finger.position = current_touch.position; | ||
| 75 | } | ||
| 76 | |||
| 77 | std::array<Core::HID::TouchFinger, MaxFingers> active_fingers; | ||
| 78 | const auto end_iter = std::copy_if(fingers.begin(), fingers.end(), active_fingers.begin(), | ||
| 79 | [](const auto& finger) { return finger.pressed; }); | ||
| 80 | const auto active_fingers_count = | ||
| 81 | static_cast<std::size_t>(std::distance(active_fingers.begin(), end_iter)); | ||
| 82 | |||
| 83 | touch_status.entry_count = static_cast<s32>(active_fingers_count); | ||
| 84 | for (std::size_t id = 0; id < MaxFingers; ++id) { | ||
| 85 | auto& touch_entry = touch_status.states[id]; | ||
| 86 | if (id < active_fingers_count) { | ||
| 87 | const auto& [active_x, active_y] = active_fingers[id].position; | ||
| 88 | touch_entry.position = { | ||
| 89 | .x = static_cast<u16>(active_x * TouchSensorWidth), | ||
| 90 | .y = static_cast<u16>(active_y * TouchSensorHeight), | ||
| 91 | }; | ||
| 92 | touch_entry.diameter_x = Settings::values.touchscreen.diameter_x; | ||
| 93 | touch_entry.diameter_y = Settings::values.touchscreen.diameter_y; | ||
| 94 | touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle; | ||
| 95 | touch_entry.finger = active_fingers[id].id; | ||
| 96 | touch_entry.attribute.raw = active_fingers[id].attribute.raw; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | return ResultSuccess; | ||
| 100 | } | ||
| 101 | |||
| 102 | void TouchDriver::GetNextTouchState(TouchScreenState& out_state) const { | ||
| 103 | out_state = touch_status; | ||
| 104 | } | ||
| 105 | |||
| 106 | void TouchDriver::SetTouchMode(Core::HID::TouchScreenModeForNx mode) { | ||
| 107 | touch_mode = mode; | ||
| 108 | } | ||
| 109 | |||
| 110 | Core::HID::TouchScreenModeForNx TouchDriver::GetTouchMode() const { | ||
| 111 | return touch_mode; | ||
| 112 | } | ||
| 113 | |||
| 114 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "core/hle/result.h" | ||
| 8 | #include "hid_core/frontend/emulated_console.h" | ||
| 9 | #include "hid_core/hid_types.h" | ||
| 10 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 11 | |||
| 12 | namespace Core::HID { | ||
| 13 | class HIDCore; | ||
| 14 | } // namespace Core::HID | ||
| 15 | |||
| 16 | namespace Service::HID { | ||
| 17 | |||
| 18 | /// This handles all request to Ftm3bd56(TouchPanel) hardware | ||
| 19 | class TouchDriver { | ||
| 20 | public: | ||
| 21 | explicit TouchDriver(Core::HID::HIDCore& hid_core); | ||
| 22 | ~TouchDriver(); | ||
| 23 | |||
| 24 | Result StartTouchSensor(); | ||
| 25 | Result StopTouchSensor(); | ||
| 26 | bool IsRunning() const; | ||
| 27 | |||
| 28 | void ProcessTouchScreenAutoTune() const; | ||
| 29 | |||
| 30 | Result WaitForDummyInput(); | ||
| 31 | Result WaitForInput(); | ||
| 32 | |||
| 33 | void GetNextTouchState(TouchScreenState& out_state) const; | ||
| 34 | |||
| 35 | void SetTouchMode(Core::HID::TouchScreenModeForNx mode); | ||
| 36 | Core::HID::TouchScreenModeForNx GetTouchMode() const; | ||
| 37 | |||
| 38 | private: | ||
| 39 | bool is_running{}; | ||
| 40 | TouchScreenState touch_status{}; | ||
| 41 | Core::HID::TouchFingerState fingers{}; | ||
| 42 | Core::HID::TouchScreenModeForNx touch_mode{}; | ||
| 43 | |||
| 44 | Core::HID::EmulatedConsole* console = nullptr; | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #include "common/logging/log.h" | ||
| 5 | #include "core/core_timing.h" | ||
| 6 | #include "core/hle/kernel/k_event.h" | ||
| 7 | #include "core/hle/kernel/k_shared_memory.h" | ||
| 8 | #include "core/hle/service/set/system_settings_server.h" | ||
| 9 | #include "core/hle/service/sm/sm.h" | ||
| 10 | #include "hid_core/hid_result.h" | ||
| 11 | #include "hid_core/resources/applet_resource.h" | ||
| 12 | #include "hid_core/resources/shared_memory_format.h" | ||
| 13 | #include "hid_core/resources/touch_screen/touch_screen_driver.h" | ||
| 14 | #include "hid_core/resources/touch_screen/touch_screen_resource.h" | ||
| 15 | |||
| 16 | namespace Service::HID { | ||
| 17 | constexpr auto GestureUpdatePeriod = std::chrono::nanoseconds{4 * 1000 * 1000}; // (4ms, 1000Hz) | ||
| 18 | |||
| 19 | TouchResource::TouchResource(Core::System& system_) : system{system_} { | ||
| 20 | m_set_sys = system.ServiceManager().GetService<Service::Set::ISystemSettingsServer>("set:sys"); | ||
| 21 | } | ||
| 22 | |||
| 23 | TouchResource::~TouchResource() { | ||
| 24 | Finalize(); | ||
| 25 | }; | ||
| 26 | |||
| 27 | Result TouchResource::ActivateTouch() { | ||
| 28 | if (global_ref_counter == std::numeric_limits<s32>::max() - 1 || | ||
| 29 | touch_ref_counter == std::numeric_limits<s32>::max() - 1) { | ||
| 30 | return ResultTouchOverflow; | ||
| 31 | } | ||
| 32 | |||
| 33 | if (global_ref_counter == 0) { | ||
| 34 | std::scoped_lock lock{*shared_mutex}; | ||
| 35 | |||
| 36 | const auto result = touch_driver->StartTouchSensor(); | ||
| 37 | if (result.IsError()) { | ||
| 38 | return result; | ||
| 39 | } | ||
| 40 | |||
| 41 | is_initalized = true; | ||
| 42 | system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod, | ||
| 43 | timer_event); | ||
| 44 | current_touch_state = {}; | ||
| 45 | ReadTouchInput(); | ||
| 46 | gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, | ||
| 47 | 0); | ||
| 48 | } | ||
| 49 | |||
| 50 | Set::TouchScreenMode touch_mode{Set::TouchScreenMode::Standard}; | ||
| 51 | m_set_sys->GetTouchScreenMode(touch_mode); | ||
| 52 | default_touch_screen_mode = static_cast<Core::HID::TouchScreenModeForNx>(touch_mode); | ||
| 53 | |||
| 54 | global_ref_counter++; | ||
| 55 | touch_ref_counter++; | ||
| 56 | return ResultSuccess; | ||
| 57 | } | ||
| 58 | |||
| 59 | Result TouchResource::ActivateTouch(u64 aruid) { | ||
| 60 | std::scoped_lock lock{*shared_mutex}; | ||
| 61 | |||
| 62 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 63 | auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 64 | TouchAruidData& touch_data = aruid_data[aruid_index]; | ||
| 65 | |||
| 66 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { | ||
| 67 | touch_data = {}; | ||
| 68 | continue; | ||
| 69 | } | ||
| 70 | |||
| 71 | const u64 aruid_id = applet_data->aruid; | ||
| 72 | if (touch_data.aruid != aruid_id) { | ||
| 73 | touch_data = {}; | ||
| 74 | touch_data.aruid = aruid_id; | ||
| 75 | } | ||
| 76 | |||
| 77 | if (aruid != aruid_id) { | ||
| 78 | continue; | ||
| 79 | } | ||
| 80 | |||
| 81 | auto& touch_shared = applet_data->shared_memory_format->touch_screen; | ||
| 82 | |||
| 83 | if (touch_shared.touch_screen_lifo.buffer_count == 0) { | ||
| 84 | StorePreviousTouchState(previous_touch_state, touch_data.finger_map, | ||
| 85 | current_touch_state, | ||
| 86 | applet_data->flag.enable_touchscreen.Value() != 0); | ||
| 87 | touch_shared.touch_screen_lifo.WriteNextEntry(previous_touch_state); | ||
| 88 | } | ||
| 89 | } | ||
| 90 | return ResultSuccess; | ||
| 91 | } | ||
| 92 | |||
| 93 | Result TouchResource::ActivateGesture() { | ||
| 94 | if (global_ref_counter == std::numeric_limits<s32>::max() - 1 || | ||
| 95 | gesture_ref_counter == std::numeric_limits<s32>::max() - 1) { | ||
| 96 | return ResultGestureOverflow; | ||
| 97 | } | ||
| 98 | |||
| 99 | // Initialize first instance | ||
| 100 | if (global_ref_counter == 0) { | ||
| 101 | const auto result = touch_driver->StartTouchSensor(); | ||
| 102 | if (result.IsError()) { | ||
| 103 | return result; | ||
| 104 | } | ||
| 105 | |||
| 106 | is_initalized = true; | ||
| 107 | system.CoreTiming().ScheduleLoopingEvent(GestureUpdatePeriod, GestureUpdatePeriod, | ||
| 108 | timer_event); | ||
| 109 | current_touch_state = {}; | ||
| 110 | ReadTouchInput(); | ||
| 111 | gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, | ||
| 112 | 0); | ||
| 113 | } | ||
| 114 | |||
| 115 | global_ref_counter++; | ||
| 116 | gesture_ref_counter++; | ||
| 117 | return ResultSuccess; | ||
| 118 | } | ||
| 119 | |||
| 120 | Result TouchResource::ActivateGesture(u64 aruid, u32 basic_gesture_id) { | ||
| 121 | std::scoped_lock lock{*shared_mutex}; | ||
| 122 | |||
| 123 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 124 | auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 125 | TouchAruidData& touch_data = aruid_data[aruid_index]; | ||
| 126 | |||
| 127 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { | ||
| 128 | touch_data = {}; | ||
| 129 | continue; | ||
| 130 | } | ||
| 131 | |||
| 132 | const u64 aruid_id = applet_data->aruid; | ||
| 133 | if (touch_data.aruid != aruid_id) { | ||
| 134 | touch_data = {}; | ||
| 135 | touch_data.aruid = aruid_id; | ||
| 136 | } | ||
| 137 | |||
| 138 | if (aruid != aruid_id) { | ||
| 139 | continue; | ||
| 140 | } | ||
| 141 | |||
| 142 | auto& gesture_shared = applet_data->shared_memory_format->gesture; | ||
| 143 | if (touch_data.basic_gesture_id != basic_gesture_id) { | ||
| 144 | gesture_shared.gesture_lifo.buffer_count = 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | if (gesture_shared.gesture_lifo.buffer_count == 0) { | ||
| 148 | touch_data.basic_gesture_id = basic_gesture_id; | ||
| 149 | |||
| 150 | gesture_shared.gesture_lifo.WriteNextEntry(gesture_state); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | return ResultSuccess; | ||
| 155 | } | ||
| 156 | |||
| 157 | Result TouchResource::DeactivateTouch() { | ||
| 158 | if (touch_ref_counter == 0 || global_ref_counter == 0) { | ||
| 159 | return ResultTouchNotInitialized; | ||
| 160 | } | ||
| 161 | |||
| 162 | global_ref_counter--; | ||
| 163 | touch_ref_counter--; | ||
| 164 | |||
| 165 | if (touch_ref_counter + global_ref_counter != 0) { | ||
| 166 | return ResultSuccess; | ||
| 167 | } | ||
| 168 | |||
| 169 | return Finalize(); | ||
| 170 | } | ||
| 171 | |||
| 172 | Result TouchResource::DeactivateGesture() { | ||
| 173 | if (gesture_ref_counter == 0 || global_ref_counter == 0) { | ||
| 174 | return ResultGestureNotInitialized; | ||
| 175 | } | ||
| 176 | |||
| 177 | global_ref_counter--; | ||
| 178 | gesture_ref_counter--; | ||
| 179 | |||
| 180 | if (touch_ref_counter + global_ref_counter != 0) { | ||
| 181 | return ResultSuccess; | ||
| 182 | } | ||
| 183 | |||
| 184 | return Finalize(); | ||
| 185 | } | ||
| 186 | |||
| 187 | bool TouchResource::IsTouchActive() const { | ||
| 188 | return touch_ref_counter != 0; | ||
| 189 | } | ||
| 190 | |||
| 191 | bool TouchResource::IsGestureActive() const { | ||
| 192 | return gesture_ref_counter != 0; | ||
| 193 | } | ||
| 194 | |||
| 195 | void TouchResource::SetTouchDriver(std::shared_ptr<TouchDriver> driver) { | ||
| 196 | touch_driver = driver; | ||
| 197 | } | ||
| 198 | |||
| 199 | void TouchResource::SetAppletResource(std::shared_ptr<AppletResource> shared, | ||
| 200 | std::recursive_mutex* mutex) { | ||
| 201 | applet_resource = shared; | ||
| 202 | shared_mutex = mutex; | ||
| 203 | } | ||
| 204 | |||
| 205 | void TouchResource::SetInputEvent(Kernel::KEvent* event, std::mutex* mutex) { | ||
| 206 | input_event = event; | ||
| 207 | input_mutex = mutex; | ||
| 208 | } | ||
| 209 | |||
| 210 | void TouchResource::SetHandheldConfig(std::shared_ptr<HandheldConfig> config) { | ||
| 211 | handheld_config = config; | ||
| 212 | } | ||
| 213 | |||
| 214 | void TouchResource::SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event) { | ||
| 215 | timer_event = event; | ||
| 216 | } | ||
| 217 | |||
| 218 | Result TouchResource::SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state) { | ||
| 219 | if (global_ref_counter == 0) { | ||
| 220 | return ResultTouchNotInitialized; | ||
| 221 | } | ||
| 222 | |||
| 223 | if (!is_auto_pilot_initialized) { | ||
| 224 | is_auto_pilot_initialized = true; | ||
| 225 | auto_pilot = {}; | ||
| 226 | } | ||
| 227 | |||
| 228 | TouchScreenState state = { | ||
| 229 | .entry_count = static_cast<s32>(auto_pilot_state.count), | ||
| 230 | .states = auto_pilot_state.state, | ||
| 231 | }; | ||
| 232 | |||
| 233 | SanitizeInput(state); | ||
| 234 | |||
| 235 | auto_pilot.count = state.entry_count; | ||
| 236 | auto_pilot.state = state.states; | ||
| 237 | return ResultSuccess; | ||
| 238 | } | ||
| 239 | |||
| 240 | Result TouchResource::UnsetTouchScreenAutoPilotState() { | ||
| 241 | if (global_ref_counter == 0) { | ||
| 242 | return ResultTouchNotInitialized; | ||
| 243 | } | ||
| 244 | |||
| 245 | is_auto_pilot_initialized = false; | ||
| 246 | auto_pilot = {}; | ||
| 247 | return ResultSuccess; | ||
| 248 | } | ||
| 249 | |||
| 250 | Result TouchResource::RequestNextTouchInput() { | ||
| 251 | if (global_ref_counter == 0) { | ||
| 252 | return ResultTouchNotInitialized; | ||
| 253 | } | ||
| 254 | |||
| 255 | if (handheld_config->is_handheld_hid_enabled) { | ||
| 256 | const Result result = touch_driver->WaitForInput(); | ||
| 257 | if (result.IsError()) { | ||
| 258 | return result; | ||
| 259 | } | ||
| 260 | } | ||
| 261 | |||
| 262 | is_initalized = true; | ||
| 263 | return ResultSuccess; | ||
| 264 | } | ||
| 265 | |||
| 266 | Result TouchResource::RequestNextDummyInput() { | ||
| 267 | if (global_ref_counter == 0) { | ||
| 268 | return ResultTouchNotInitialized; | ||
| 269 | } | ||
| 270 | |||
| 271 | if (handheld_config->is_handheld_hid_enabled) { | ||
| 272 | const Result result = touch_driver->WaitForDummyInput(); | ||
| 273 | if (result.IsError()) { | ||
| 274 | return result; | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | is_initalized = false; | ||
| 279 | return ResultSuccess; | ||
| 280 | } | ||
| 281 | |||
| 282 | Result TouchResource::ProcessTouchScreenAutoTune() { | ||
| 283 | touch_driver->ProcessTouchScreenAutoTune(); | ||
| 284 | return ResultSuccess; | ||
| 285 | } | ||
| 286 | |||
| 287 | void TouchResource::SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, | ||
| 288 | f32 point2_y) { | ||
| 289 | offset = { | ||
| 290 | .x = point1_x, | ||
| 291 | .y = point1_y, | ||
| 292 | }; | ||
| 293 | magnification = { | ||
| 294 | .x = point2_x, | ||
| 295 | .y = point2_y, | ||
| 296 | }; | ||
| 297 | } | ||
| 298 | |||
| 299 | Result TouchResource::SetTouchScreenResolution(u32 width, u32 height, u64 aruid) { | ||
| 300 | std::scoped_lock lock{*shared_mutex}; | ||
| 301 | |||
| 302 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 303 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 304 | TouchAruidData& data = aruid_data[aruid_index]; | ||
| 305 | |||
| 306 | if (!applet_data->flag.is_assigned) { | ||
| 307 | continue; | ||
| 308 | } | ||
| 309 | if (aruid != data.aruid) { | ||
| 310 | continue; | ||
| 311 | } | ||
| 312 | data.resolution_width = static_cast<u16>(width); | ||
| 313 | data.resolution_height = static_cast<u16>(height); | ||
| 314 | } | ||
| 315 | |||
| 316 | return ResultSuccess; | ||
| 317 | } | ||
| 318 | |||
| 319 | Result TouchResource::SetTouchScreenConfiguration( | ||
| 320 | const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid) { | ||
| 321 | std::scoped_lock lock{*shared_mutex}; | ||
| 322 | |||
| 323 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 324 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 325 | TouchAruidData& data = aruid_data[aruid_index]; | ||
| 326 | |||
| 327 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { | ||
| 328 | continue; | ||
| 329 | } | ||
| 330 | if (aruid != data.aruid) { | ||
| 331 | continue; | ||
| 332 | } | ||
| 333 | data.finger_map.touch_mode = touch_configuration.mode; | ||
| 334 | } | ||
| 335 | |||
| 336 | return ResultSuccess; | ||
| 337 | } | ||
| 338 | |||
| 339 | Result TouchResource::GetTouchScreenConfiguration( | ||
| 340 | Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const { | ||
| 341 | std::scoped_lock lock{*shared_mutex}; | ||
| 342 | |||
| 343 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 344 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 345 | const TouchAruidData& data = aruid_data[aruid_index]; | ||
| 346 | |||
| 347 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { | ||
| 348 | continue; | ||
| 349 | } | ||
| 350 | if (aruid != data.aruid) { | ||
| 351 | continue; | ||
| 352 | } | ||
| 353 | out_touch_configuration.mode = data.finger_map.touch_mode; | ||
| 354 | } | ||
| 355 | |||
| 356 | return ResultSuccess; | ||
| 357 | } | ||
| 358 | |||
| 359 | Result TouchResource::SetTouchScreenDefaultConfiguration( | ||
| 360 | const Core::HID::TouchScreenConfigurationForNx& touch_configuration) { | ||
| 361 | default_touch_screen_mode = touch_configuration.mode; | ||
| 362 | return ResultSuccess; | ||
| 363 | } | ||
| 364 | |||
| 365 | Result TouchResource::GetTouchScreenDefaultConfiguration( | ||
| 366 | Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const { | ||
| 367 | out_touch_configuration.mode = default_touch_screen_mode; | ||
| 368 | return ResultSuccess; | ||
| 369 | } | ||
| 370 | |||
| 371 | Result TouchResource::Finalize() { | ||
| 372 | is_auto_pilot_initialized = false; | ||
| 373 | auto_pilot = {}; | ||
| 374 | system.CoreTiming().UnscheduleEvent(timer_event); | ||
| 375 | |||
| 376 | const auto result = touch_driver->StopTouchSensor(); | ||
| 377 | if (result.IsError()) { | ||
| 378 | return result; | ||
| 379 | } | ||
| 380 | |||
| 381 | is_initalized = false; | ||
| 382 | return ResultSuccess; | ||
| 383 | } | ||
| 384 | |||
| 385 | void TouchResource::StorePreviousTouchState(TouchScreenState& out_previous_touch, | ||
| 386 | TouchFingerMap& out_finger_map, | ||
| 387 | const TouchScreenState& current_touch, | ||
| 388 | bool is_touch_enabled) const { | ||
| 389 | s32 finger_count{}; | ||
| 390 | |||
| 391 | if (is_touch_enabled) { | ||
| 392 | finger_count = current_touch.entry_count; | ||
| 393 | if (finger_count < 1) { | ||
| 394 | out_finger_map.finger_count = 0; | ||
| 395 | out_finger_map.finger_ids = {}; | ||
| 396 | out_previous_touch.sampling_number = current_touch.sampling_number; | ||
| 397 | out_previous_touch.entry_count = 0; | ||
| 398 | out_previous_touch.states = {}; | ||
| 399 | return; | ||
| 400 | } | ||
| 401 | for (std::size_t i = 0; i < static_cast<u32>(finger_count); i++) { | ||
| 402 | out_finger_map.finger_ids[i] = current_touch.states[i].finger; | ||
| 403 | out_previous_touch.states[i] = current_touch.states[i]; | ||
| 404 | } | ||
| 405 | out_finger_map.finger_count = finger_count; | ||
| 406 | return; | ||
| 407 | } | ||
| 408 | |||
| 409 | if (!is_touch_enabled && out_finger_map.finger_count > 0 && current_touch.entry_count > 0) { | ||
| 410 | // TODO | ||
| 411 | } | ||
| 412 | |||
| 413 | // Zero out unused entries | ||
| 414 | for (std::size_t i = finger_count; i < MaxFingers; i++) { | ||
| 415 | out_finger_map.finger_ids[i] = 0; | ||
| 416 | out_previous_touch.states[i] = {}; | ||
| 417 | } | ||
| 418 | |||
| 419 | out_previous_touch.sampling_number = current_touch.sampling_number; | ||
| 420 | out_previous_touch.entry_count = finger_count; | ||
| 421 | } | ||
| 422 | |||
| 423 | void TouchResource::ReadTouchInput() { | ||
| 424 | previous_touch_state = current_touch_state; | ||
| 425 | |||
| 426 | if (!is_initalized || !handheld_config->is_handheld_hid_enabled || !touch_driver->IsRunning()) { | ||
| 427 | touch_driver->WaitForDummyInput(); | ||
| 428 | } else { | ||
| 429 | touch_driver->WaitForInput(); | ||
| 430 | } | ||
| 431 | |||
| 432 | touch_driver->GetNextTouchState(current_touch_state); | ||
| 433 | SanitizeInput(current_touch_state); | ||
| 434 | current_touch_state.sampling_number = sample_number; | ||
| 435 | sample_number++; | ||
| 436 | |||
| 437 | if (is_auto_pilot_initialized && current_touch_state.entry_count == 0) { | ||
| 438 | const std::size_t finger_count = static_cast<std::size_t>(auto_pilot.count); | ||
| 439 | current_touch_state.entry_count = static_cast<s32>(finger_count); | ||
| 440 | for (std::size_t i = 0; i < finger_count; i++) { | ||
| 441 | current_touch_state.states[i] = auto_pilot.state[i]; | ||
| 442 | } | ||
| 443 | |||
| 444 | std::size_t index = 0; | ||
| 445 | for (std::size_t i = 0; i < finger_count; i++) { | ||
| 446 | if (auto_pilot.state[i].attribute.end_touch) { | ||
| 447 | continue; | ||
| 448 | } | ||
| 449 | auto_pilot.state[i].attribute.raw = 0; | ||
| 450 | auto_pilot.state[index] = auto_pilot.state[i]; | ||
| 451 | index++; | ||
| 452 | } | ||
| 453 | |||
| 454 | auto_pilot.count = index; | ||
| 455 | for (std::size_t i = index; i < auto_pilot.state.size(); i++) { | ||
| 456 | auto_pilot.state[i] = {}; | ||
| 457 | } | ||
| 458 | } | ||
| 459 | |||
| 460 | for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) { | ||
| 461 | auto& state = current_touch_state.states[i]; | ||
| 462 | state.position.x = static_cast<u32>((magnification.y * static_cast<f32>(state.position.x)) + | ||
| 463 | (offset.x * static_cast<f32>(TouchSensorWidth))); | ||
| 464 | state.position.y = static_cast<u32>((magnification.y * static_cast<f32>(state.position.y)) + | ||
| 465 | (offset.x * static_cast<f32>(TouchSensorHeight))); | ||
| 466 | state.diameter_x = static_cast<u32>(magnification.x * static_cast<f32>(state.diameter_x)); | ||
| 467 | state.diameter_y = static_cast<u32>(magnification.y * static_cast<f32>(state.diameter_y)); | ||
| 468 | } | ||
| 469 | |||
| 470 | std::size_t index = 0; | ||
| 471 | for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); i++) { | ||
| 472 | const auto& old_state = current_touch_state.states[i]; | ||
| 473 | auto& state = current_touch_state.states[index]; | ||
| 474 | if ((TouchSensorWidth <= old_state.position.x) || | ||
| 475 | (TouchSensorHeight <= old_state.position.y)) { | ||
| 476 | continue; | ||
| 477 | } | ||
| 478 | state = old_state; | ||
| 479 | index++; | ||
| 480 | } | ||
| 481 | current_touch_state.entry_count = static_cast<s32>(index); | ||
| 482 | |||
| 483 | SanitizeInput(current_touch_state); | ||
| 484 | |||
| 485 | std::scoped_lock lock{*input_mutex}; | ||
| 486 | if (current_touch_state.entry_count == previous_touch_state.entry_count) { | ||
| 487 | if (current_touch_state.entry_count < 1) { | ||
| 488 | return; | ||
| 489 | } | ||
| 490 | bool has_moved = false; | ||
| 491 | for (std::size_t i = 0; i < static_cast<std::size_t>(current_touch_state.entry_count); | ||
| 492 | i++) { | ||
| 493 | s32 delta_x = std::abs(static_cast<s32>(current_touch_state.states[i].position.x) - | ||
| 494 | static_cast<s32>(previous_touch_state.states[i].position.x)); | ||
| 495 | s32 delta_y = std::abs(static_cast<s32>(current_touch_state.states[i].position.y) - | ||
| 496 | static_cast<s32>(previous_touch_state.states[i].position.y)); | ||
| 497 | if (delta_x > 1 || delta_y > 1) { | ||
| 498 | has_moved = true; | ||
| 499 | } | ||
| 500 | } | ||
| 501 | if (!has_moved) { | ||
| 502 | return; | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | input_event->Signal(); | ||
| 507 | } | ||
| 508 | |||
| 509 | void TouchResource::OnTouchUpdate(s64 timestamp) { | ||
| 510 | if (global_ref_counter == 0) { | ||
| 511 | return; | ||
| 512 | } | ||
| 513 | |||
| 514 | ReadTouchInput(); | ||
| 515 | gesture_handler.SetTouchState(current_touch_state.states, current_touch_state.entry_count, | ||
| 516 | timestamp); | ||
| 517 | |||
| 518 | std::scoped_lock lock{*shared_mutex}; | ||
| 519 | |||
| 520 | for (std::size_t aruid_index = 0; aruid_index < AruidIndexMax; aruid_index++) { | ||
| 521 | const auto* applet_data = applet_resource->GetAruidDataByIndex(aruid_index); | ||
| 522 | TouchAruidData& data = aruid_data[aruid_index]; | ||
| 523 | |||
| 524 | if (applet_data == nullptr || !applet_data->flag.is_assigned) { | ||
| 525 | data = {}; | ||
| 526 | continue; | ||
| 527 | } | ||
| 528 | |||
| 529 | if (data.aruid != applet_data->aruid) { | ||
| 530 | data = {}; | ||
| 531 | data.aruid = applet_data->aruid; | ||
| 532 | } | ||
| 533 | |||
| 534 | if (gesture_ref_counter != 0) { | ||
| 535 | if (!applet_data->flag.enable_touchscreen) { | ||
| 536 | gesture_state = {}; | ||
| 537 | } | ||
| 538 | if (gesture_handler.NeedsUpdate()) { | ||
| 539 | gesture_handler.UpdateGestureState(gesture_state, timestamp); | ||
| 540 | auto& gesture_shared = applet_data->shared_memory_format->gesture; | ||
| 541 | gesture_shared.gesture_lifo.WriteNextEntry(gesture_state); | ||
| 542 | } | ||
| 543 | } | ||
| 544 | |||
| 545 | if (touch_ref_counter != 0) { | ||
| 546 | auto touch_mode = data.finger_map.touch_mode; | ||
| 547 | if (touch_mode == Core::HID::TouchScreenModeForNx::UseSystemSetting) { | ||
| 548 | touch_mode = default_touch_screen_mode; | ||
| 549 | } | ||
| 550 | |||
| 551 | if (applet_resource->GetActiveAruid() == applet_data->aruid && | ||
| 552 | touch_mode != Core::HID::TouchScreenModeForNx::UseSystemSetting && is_initalized && | ||
| 553 | handheld_config->is_handheld_hid_enabled && touch_driver->IsRunning()) { | ||
| 554 | touch_driver->SetTouchMode(touch_mode); | ||
| 555 | } | ||
| 556 | |||
| 557 | auto& touch_shared = applet_data->shared_memory_format->touch_screen; | ||
| 558 | StorePreviousTouchState(previous_touch_state, data.finger_map, current_touch_state, | ||
| 559 | applet_data->flag.enable_touchscreen.As<bool>()); | ||
| 560 | touch_shared.touch_screen_lifo.WriteNextEntry(current_touch_state); | ||
| 561 | } | ||
| 562 | } | ||
| 563 | } | ||
| 564 | |||
| 565 | void TouchResource::SanitizeInput(TouchScreenState& state) const { | ||
| 566 | for (std::size_t i = 0; i < static_cast<std::size_t>(state.entry_count); i++) { | ||
| 567 | auto& entry = state.states[i]; | ||
| 568 | entry.position.x = | ||
| 569 | std::clamp(entry.position.x, TouchBorders, TouchSensorWidth - TouchBorders - 1); | ||
| 570 | entry.position.y = | ||
| 571 | std::clamp(entry.position.y, TouchBorders, TouchSensorHeight - TouchBorders - 1); | ||
| 572 | entry.diameter_x = std::clamp(entry.diameter_x, 0u, TouchSensorWidth - MaxTouchDiameter); | ||
| 573 | entry.diameter_y = std::clamp(entry.diameter_y, 0u, TouchSensorHeight - MaxTouchDiameter); | ||
| 574 | entry.rotation_angle = | ||
| 575 | std::clamp(entry.rotation_angle, -MaxRotationAngle, MaxRotationAngle); | ||
| 576 | } | ||
| 577 | } | ||
| 578 | |||
| 579 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-3.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <array> | ||
| 7 | #include <mutex> | ||
| 8 | |||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "common/point.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | #include "hid_core/hid_types.h" | ||
| 13 | #include "hid_core/resources/touch_screen/gesture_handler.h" | ||
| 14 | #include "hid_core/resources/touch_screen/touch_types.h" | ||
| 15 | |||
| 16 | namespace Core { | ||
| 17 | class System; | ||
| 18 | } | ||
| 19 | |||
| 20 | namespace Core::Timing { | ||
| 21 | struct EventType; | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace Kernel { | ||
| 25 | class KEvent; | ||
| 26 | } // namespace Kernel | ||
| 27 | |||
| 28 | namespace Service::Set { | ||
| 29 | class ISystemSettingsServer; | ||
| 30 | } | ||
| 31 | |||
| 32 | namespace Service::HID { | ||
| 33 | class AppletResource; | ||
| 34 | class TouchSharedMemoryManager; | ||
| 35 | class TouchDriver; | ||
| 36 | struct HandheldConfig; | ||
| 37 | |||
| 38 | class TouchResource { | ||
| 39 | public: | ||
| 40 | TouchResource(Core::System& system_); | ||
| 41 | ~TouchResource(); | ||
| 42 | |||
| 43 | Result ActivateTouch(); | ||
| 44 | Result ActivateTouch(u64 aruid); | ||
| 45 | |||
| 46 | Result ActivateGesture(); | ||
| 47 | Result ActivateGesture(u64 aruid, u32 basic_gesture_id); | ||
| 48 | |||
| 49 | Result DeactivateTouch(); | ||
| 50 | Result DeactivateGesture(); | ||
| 51 | |||
| 52 | bool IsTouchActive() const; | ||
| 53 | bool IsGestureActive() const; | ||
| 54 | |||
| 55 | void SetTouchDriver(std::shared_ptr<TouchDriver> driver); | ||
| 56 | void SetAppletResource(std::shared_ptr<AppletResource> shared, std::recursive_mutex* mutex); | ||
| 57 | void SetInputEvent(Kernel::KEvent* event, std::mutex* mutex); | ||
| 58 | void SetHandheldConfig(std::shared_ptr<HandheldConfig> config); | ||
| 59 | void SetTimerEvent(std::shared_ptr<Core::Timing::EventType> event); | ||
| 60 | |||
| 61 | Result SetTouchScreenAutoPilotState(const AutoPilotState& auto_pilot_state); | ||
| 62 | Result UnsetTouchScreenAutoPilotState(); | ||
| 63 | |||
| 64 | Result RequestNextTouchInput(); | ||
| 65 | Result RequestNextDummyInput(); | ||
| 66 | |||
| 67 | Result ProcessTouchScreenAutoTune(); | ||
| 68 | void SetTouchScreenMagnification(f32 point1_x, f32 point1_y, f32 point2_x, f32 point2_y); | ||
| 69 | Result SetTouchScreenResolution(u32 width, u32 height, u64 aruid); | ||
| 70 | |||
| 71 | Result SetTouchScreenConfiguration( | ||
| 72 | const Core::HID::TouchScreenConfigurationForNx& touch_configuration, u64 aruid); | ||
| 73 | Result GetTouchScreenConfiguration( | ||
| 74 | Core::HID::TouchScreenConfigurationForNx& out_touch_configuration, u64 aruid) const; | ||
| 75 | |||
| 76 | Result SetTouchScreenDefaultConfiguration( | ||
| 77 | const Core::HID::TouchScreenConfigurationForNx& touch_configuration); | ||
| 78 | Result GetTouchScreenDefaultConfiguration( | ||
| 79 | Core::HID::TouchScreenConfigurationForNx& out_touch_configuration) const; | ||
| 80 | |||
| 81 | void OnTouchUpdate(s64 timestamp); | ||
| 82 | |||
| 83 | private: | ||
| 84 | Result Finalize(); | ||
| 85 | |||
| 86 | void StorePreviousTouchState(TouchScreenState& out_previous_touch, | ||
| 87 | TouchFingerMap& out_finger_map, | ||
| 88 | const TouchScreenState& current_touch, | ||
| 89 | bool is_touch_enabled) const; | ||
| 90 | void ReadTouchInput(); | ||
| 91 | |||
| 92 | void SanitizeInput(TouchScreenState& state) const; | ||
| 93 | |||
| 94 | s32 global_ref_counter{}; | ||
| 95 | s32 gesture_ref_counter{}; | ||
| 96 | s32 touch_ref_counter{}; | ||
| 97 | bool is_initalized{}; | ||
| 98 | u64 sample_number{}; | ||
| 99 | |||
| 100 | // External resources | ||
| 101 | std::shared_ptr<Core::Timing::EventType> timer_event{nullptr}; | ||
| 102 | std::shared_ptr<TouchDriver> touch_driver{nullptr}; | ||
| 103 | std::shared_ptr<AppletResource> applet_resource{nullptr}; | ||
| 104 | std::recursive_mutex* shared_mutex{nullptr}; | ||
| 105 | std::shared_ptr<HandheldConfig> handheld_config{nullptr}; | ||
| 106 | Kernel::KEvent* input_event{nullptr}; | ||
| 107 | std::mutex* input_mutex{nullptr}; | ||
| 108 | |||
| 109 | // Internal state | ||
| 110 | TouchScreenState current_touch_state{}; | ||
| 111 | TouchScreenState previous_touch_state{}; | ||
| 112 | GestureState gesture_state{}; | ||
| 113 | bool is_auto_pilot_initialized{}; | ||
| 114 | AutoPilotState auto_pilot{}; | ||
| 115 | GestureHandler gesture_handler{}; | ||
| 116 | std::array<TouchAruidData, 0x20> aruid_data{}; | ||
| 117 | Common::Point<f32> magnification{1.0f, 1.0f}; | ||
| 118 | Common::Point<f32> offset{0.0f, 0.0f}; | ||
| 119 | Core::HID::TouchScreenModeForNx default_touch_screen_mode{ | ||
| 120 | Core::HID::TouchScreenModeForNx::Finger}; | ||
| 121 | |||
| 122 | Core::System& system; | ||
| 123 | std::shared_ptr<Service::Set::ISystemSettingsServer> m_set_sys; | ||
| 124 | }; | ||
| 125 | |||
| 126 | } // 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 @@ | |||
| 13 | #include "hid_core/hid_types.h" | 13 | #include "hid_core/hid_types.h" |
| 14 | 14 | ||
| 15 | namespace Service::HID { | 15 | namespace Service::HID { |
| 16 | static constexpr std::size_t MAX_FINGERS = 16; | 16 | constexpr std::size_t MaxFingers = 16; |
| 17 | static constexpr size_t MAX_POINTS = 4; | 17 | constexpr std::size_t MaxPoints = 4; |
| 18 | constexpr u32 TouchSensorWidth = 1280; | ||
| 19 | constexpr u32 TouchSensorHeight = 720; | ||
| 20 | constexpr s32 MaxRotationAngle = 270; | ||
| 21 | constexpr u32 MaxTouchDiameter = 30; | ||
| 22 | constexpr u32 TouchBorders = 15; | ||
| 23 | |||
| 24 | // HW is around 700, value is set to 400 to make it easier to trigger with mouse | ||
| 25 | constexpr f32 SwipeThreshold = 400.0f; // Threshold in pixels/s | ||
| 26 | constexpr f32 AngleThreshold = 0.015f; // Threshold in radians | ||
| 27 | constexpr f32 PinchThreshold = 0.5f; // Threshold in pixels | ||
| 28 | constexpr f32 PressDelay = 0.5f; // Time in seconds | ||
| 29 | constexpr f32 DoubleTapDelay = 0.35f; // Time in seconds | ||
| 18 | 30 | ||
| 19 | // This is nn::hid::GestureType | 31 | // This is nn::hid::GestureType |
| 20 | enum class GestureType : u32 { | 32 | enum class GestureType : u32 { |
| @@ -28,6 +40,7 @@ enum class GestureType : u32 { | |||
| 28 | Swipe, // Fast press movement and release of a single point | 40 | Swipe, // Fast press movement and release of a single point |
| 29 | Pinch, // All points moving away/closer to the midpoint | 41 | Pinch, // All points moving away/closer to the midpoint |
| 30 | Rotate, // All points rotating from the midpoint | 42 | Rotate, // All points rotating from the midpoint |
| 43 | GestureTypeMax = Rotate, | ||
| 31 | }; | 44 | }; |
| 32 | 45 | ||
| 33 | // This is nn::hid::GestureDirection | 46 | // This is nn::hid::GestureDirection |
| @@ -69,7 +82,7 @@ struct GestureState { | |||
| 69 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); | 82 | static_assert(sizeof(GestureState) == 0x60, "GestureState is an invalid size"); |
| 70 | 83 | ||
| 71 | struct GestureProperties { | 84 | struct GestureProperties { |
| 72 | std::array<Common::Point<s32>, MAX_POINTS> points{}; | 85 | std::array<Common::Point<s32>, MaxPoints> points{}; |
| 73 | std::size_t active_points{}; | 86 | std::size_t active_points{}; |
| 74 | Common::Point<s32> mid_point{}; | 87 | Common::Point<s32> mid_point{}; |
| 75 | s64 detection_count{}; | 88 | s64 detection_count{}; |
| @@ -78,13 +91,53 @@ struct GestureProperties { | |||
| 78 | f32 angle{}; | 91 | f32 angle{}; |
| 79 | }; | 92 | }; |
| 80 | 93 | ||
| 94 | // This is nn::hid::TouchState | ||
| 95 | struct TouchState { | ||
| 96 | u64 delta_time{}; | ||
| 97 | Core::HID::TouchAttribute attribute{}; | ||
| 98 | u32 finger{}; | ||
| 99 | Common::Point<u32> position{}; | ||
| 100 | u32 diameter_x{}; | ||
| 101 | u32 diameter_y{}; | ||
| 102 | s32 rotation_angle{}; | ||
| 103 | }; | ||
| 104 | static_assert(sizeof(TouchState) == 0x28, "Touchstate is an invalid size"); | ||
| 105 | |||
| 81 | // This is nn::hid::TouchScreenState | 106 | // This is nn::hid::TouchScreenState |
| 82 | struct TouchScreenState { | 107 | struct TouchScreenState { |
| 83 | s64 sampling_number{}; | 108 | s64 sampling_number{}; |
| 84 | s32 entry_count{}; | 109 | s32 entry_count{}; |
| 85 | INSERT_PADDING_BYTES(4); // Reserved | 110 | INSERT_PADDING_BYTES(4); // Reserved |
| 86 | std::array<Core::HID::TouchState, MAX_FINGERS> states{}; | 111 | std::array<TouchState, MaxFingers> states{}; |
| 87 | }; | 112 | }; |
| 88 | static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); | 113 | static_assert(sizeof(TouchScreenState) == 0x290, "TouchScreenState is an invalid size"); |
| 89 | 114 | ||
| 115 | struct TouchFingerMap { | ||
| 116 | s32 finger_count{}; | ||
| 117 | Core::HID::TouchScreenModeForNx touch_mode; | ||
| 118 | INSERT_PADDING_BYTES(3); | ||
| 119 | std::array<u32, MaxFingers> finger_ids{}; | ||
| 120 | }; | ||
| 121 | static_assert(sizeof(TouchFingerMap) == 0x48, "TouchFingerMap is an invalid size"); | ||
| 122 | |||
| 123 | struct TouchAruidData { | ||
| 124 | u64 aruid; | ||
| 125 | u32 basic_gesture_id; | ||
| 126 | u64 used_1; | ||
| 127 | u64 used_2; | ||
| 128 | u64 used_3; | ||
| 129 | u64 used_4; | ||
| 130 | GestureType gesture_type; | ||
| 131 | u16 resolution_width; | ||
| 132 | u16 resolution_height; | ||
| 133 | TouchFingerMap finger_map; | ||
| 134 | }; | ||
| 135 | static_assert(sizeof(TouchAruidData) == 0x80, "TouchAruidData is an invalid size"); | ||
| 136 | |||
| 137 | struct AutoPilotState { | ||
| 138 | u64 count; | ||
| 139 | std::array<TouchState, 16> state; | ||
| 140 | }; | ||
| 141 | static_assert(sizeof(AutoPilotState) == 0x288, "AutoPilotState is an invalid size"); | ||
| 142 | |||
| 90 | } // namespace Service::HID | 143 | } // 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() {} | |||
| 17 | void UniquePad::OnRelease() {} | 17 | void UniquePad::OnRelease() {} |
| 18 | 18 | ||
| 19 | void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { | 19 | void UniquePad::OnUpdate(const Core::Timing::CoreTiming& core_timing) { |
| 20 | if (!smart_update) { | ||
| 21 | return; | ||
| 22 | } | ||
| 23 | |||
| 24 | const u64 aruid = applet_resource->GetActiveAruid(); | 20 | const u64 aruid = applet_resource->GetActiveAruid(); |
| 25 | auto* data = applet_resource->GetAruidData(aruid); | 21 | auto* data = applet_resource->GetAruidData(aruid); |
| 26 | 22 | ||
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: | |||
| 20 | 20 | ||
| 21 | // When the controller is requesting an update for the shared memory | 21 | // When the controller is requesting an update for the shared memory |
| 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; | 22 | void OnUpdate(const Core::Timing::CoreTiming& core_timing) override; |
| 23 | |||
| 24 | private: | ||
| 25 | bool smart_update{}; | ||
| 26 | }; | 23 | }; |
| 27 | } // namespace Service::HID | 24 | } // 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 | |||
| 55 | engines/maxwell_dma.h | 55 | engines/maxwell_dma.h |
| 56 | engines/puller.cpp | 56 | engines/puller.cpp |
| 57 | engines/puller.h | 57 | engines/puller.h |
| 58 | framebuffer_config.cpp | ||
| 58 | framebuffer_config.h | 59 | framebuffer_config.h |
| 59 | fsr.cpp | 60 | fsr.cpp |
| 60 | fsr.h | 61 | fsr.h |
| @@ -115,8 +116,24 @@ add_library(video_core STATIC | |||
| 115 | renderer_null/null_rasterizer.h | 116 | renderer_null/null_rasterizer.h |
| 116 | renderer_null/renderer_null.cpp | 117 | renderer_null/renderer_null.cpp |
| 117 | renderer_null/renderer_null.h | 118 | renderer_null/renderer_null.h |
| 119 | renderer_opengl/present/filters.cpp | ||
| 120 | renderer_opengl/present/filters.h | ||
| 121 | renderer_opengl/present/fsr.cpp | ||
| 122 | renderer_opengl/present/fsr.h | ||
| 123 | renderer_opengl/present/fxaa.cpp | ||
| 124 | renderer_opengl/present/fxaa.h | ||
| 125 | renderer_opengl/present/layer.cpp | ||
| 126 | renderer_opengl/present/layer.h | ||
| 127 | renderer_opengl/present/present_uniforms.h | ||
| 128 | renderer_opengl/present/smaa.cpp | ||
| 129 | renderer_opengl/present/smaa.h | ||
| 130 | renderer_opengl/present/util.h | ||
| 131 | renderer_opengl/present/window_adapt_pass.cpp | ||
| 132 | renderer_opengl/present/window_adapt_pass.h | ||
| 118 | renderer_opengl/blit_image.cpp | 133 | renderer_opengl/blit_image.cpp |
| 119 | renderer_opengl/blit_image.h | 134 | renderer_opengl/blit_image.h |
| 135 | renderer_opengl/gl_blit_screen.cpp | ||
| 136 | renderer_opengl/gl_blit_screen.h | ||
| 120 | renderer_opengl/gl_buffer_cache_base.cpp | 137 | renderer_opengl/gl_buffer_cache_base.cpp |
| 121 | renderer_opengl/gl_buffer_cache.cpp | 138 | renderer_opengl/gl_buffer_cache.cpp |
| 122 | renderer_opengl/gl_buffer_cache.h | 139 | renderer_opengl/gl_buffer_cache.h |
| @@ -126,8 +143,6 @@ add_library(video_core STATIC | |||
| 126 | renderer_opengl/gl_device.h | 143 | renderer_opengl/gl_device.h |
| 127 | renderer_opengl/gl_fence_manager.cpp | 144 | renderer_opengl/gl_fence_manager.cpp |
| 128 | renderer_opengl/gl_fence_manager.h | 145 | renderer_opengl/gl_fence_manager.h |
| 129 | renderer_opengl/gl_fsr.cpp | ||
| 130 | renderer_opengl/gl_fsr.h | ||
| 131 | renderer_opengl/gl_graphics_pipeline.cpp | 146 | renderer_opengl/gl_graphics_pipeline.cpp |
| 132 | renderer_opengl/gl_graphics_pipeline.h | 147 | renderer_opengl/gl_graphics_pipeline.h |
| 133 | renderer_opengl/gl_rasterizer.cpp | 148 | renderer_opengl/gl_rasterizer.cpp |
| @@ -155,6 +170,22 @@ add_library(video_core STATIC | |||
| 155 | renderer_opengl/renderer_opengl.h | 170 | renderer_opengl/renderer_opengl.h |
| 156 | renderer_opengl/util_shaders.cpp | 171 | renderer_opengl/util_shaders.cpp |
| 157 | renderer_opengl/util_shaders.h | 172 | renderer_opengl/util_shaders.h |
| 173 | renderer_vulkan/present/anti_alias_pass.h | ||
| 174 | renderer_vulkan/present/filters.cpp | ||
| 175 | renderer_vulkan/present/filters.h | ||
| 176 | renderer_vulkan/present/fsr.cpp | ||
| 177 | renderer_vulkan/present/fsr.h | ||
| 178 | renderer_vulkan/present/fxaa.cpp | ||
| 179 | renderer_vulkan/present/fxaa.h | ||
| 180 | renderer_vulkan/present/layer.cpp | ||
| 181 | renderer_vulkan/present/layer.h | ||
| 182 | renderer_vulkan/present/present_push_constants.h | ||
| 183 | renderer_vulkan/present/smaa.cpp | ||
| 184 | renderer_vulkan/present/smaa.h | ||
| 185 | renderer_vulkan/present/util.cpp | ||
| 186 | renderer_vulkan/present/util.h | ||
| 187 | renderer_vulkan/present/window_adapt_pass.cpp | ||
| 188 | renderer_vulkan/present/window_adapt_pass.h | ||
| 158 | renderer_vulkan/blit_image.cpp | 189 | renderer_vulkan/blit_image.cpp |
| 159 | renderer_vulkan/blit_image.h | 190 | renderer_vulkan/blit_image.h |
| 160 | renderer_vulkan/fixed_pipeline_state.cpp | 191 | renderer_vulkan/fixed_pipeline_state.cpp |
| @@ -181,8 +212,6 @@ add_library(video_core STATIC | |||
| 181 | renderer_vulkan/vk_descriptor_pool.h | 212 | renderer_vulkan/vk_descriptor_pool.h |
| 182 | renderer_vulkan/vk_fence_manager.cpp | 213 | renderer_vulkan/vk_fence_manager.cpp |
| 183 | renderer_vulkan/vk_fence_manager.h | 214 | renderer_vulkan/vk_fence_manager.h |
| 184 | renderer_vulkan/vk_fsr.cpp | ||
| 185 | renderer_vulkan/vk_fsr.h | ||
| 186 | renderer_vulkan/vk_graphics_pipeline.cpp | 215 | renderer_vulkan/vk_graphics_pipeline.cpp |
| 187 | renderer_vulkan/vk_graphics_pipeline.h | 216 | renderer_vulkan/vk_graphics_pipeline.h |
| 188 | renderer_vulkan/vk_master_semaphore.cpp | 217 | renderer_vulkan/vk_master_semaphore.cpp |
| @@ -203,8 +232,6 @@ add_library(video_core STATIC | |||
| 203 | renderer_vulkan/vk_scheduler.h | 232 | renderer_vulkan/vk_scheduler.h |
| 204 | renderer_vulkan/vk_shader_util.cpp | 233 | renderer_vulkan/vk_shader_util.cpp |
| 205 | renderer_vulkan/vk_shader_util.h | 234 | renderer_vulkan/vk_shader_util.h |
| 206 | renderer_vulkan/vk_smaa.cpp | ||
| 207 | renderer_vulkan/vk_smaa.h | ||
| 208 | renderer_vulkan/vk_staging_buffer_pool.cpp | 235 | renderer_vulkan/vk_staging_buffer_pool.cpp |
| 209 | renderer_vulkan/vk_staging_buffer_pool.h | 236 | renderer_vulkan/vk_staging_buffer_pool.h |
| 210 | renderer_vulkan/vk_state_tracker.cpp | 237 | 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 @@ | |||
| 8 | #include <vector> | 8 | #include <vector> |
| 9 | 9 | ||
| 10 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 11 | #include "common/common_funcs.h" | ||
| 11 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 12 | #include "common/scratch_buffer.h" | 13 | #include "common/scratch_buffer.h" |
| 13 | #include "video_core/engines/engine_interface.h" | 14 | #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 | |||
| 111 | } | 111 | } |
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | template <bool unpack> | ||
| 115 | void ProcessPitchLinear(std::span<const u8> input, std::span<u8> output, size_t extent_x, | ||
| 116 | size_t extent_y, u32 pitch, u32 x0, u32 y0, size_t bpp) { | ||
| 117 | const size_t base_offset = x0 * bpp; | ||
| 118 | const size_t copy_size = extent_x * bpp; | ||
| 119 | for (size_t y = 0; y < extent_y; y++) { | ||
| 120 | const size_t first_offset = (y + y0) * pitch + base_offset; | ||
| 121 | const size_t second_offset = y * extent_x * bpp; | ||
| 122 | u8* write_to = unpack ? &output[first_offset] : &output[second_offset]; | ||
| 123 | const u8* read_from = unpack ? &input[second_offset] : &input[first_offset]; | ||
| 124 | std::memcpy(write_to, read_from, copy_size); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 114 | } // namespace | 128 | } // namespace |
| 115 | 129 | ||
| 116 | struct SoftwareBlitEngine::BlitEngineImpl { | 130 | struct SoftwareBlitEngine::BlitEngineImpl { |
| @@ -138,19 +152,6 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, | |||
| 138 | } | 152 | } |
| 139 | return static_cast<size_t>(surface.pitch * surface.height); | 153 | return static_cast<size_t>(surface.pitch * surface.height); |
| 140 | }; | 154 | }; |
| 141 | const auto process_pitch_linear = [](bool unpack, std::span<const u8> input, | ||
| 142 | std::span<u8> output, u32 extent_x, u32 extent_y, | ||
| 143 | u32 pitch, u32 x0, u32 y0, size_t bpp) { | ||
| 144 | const size_t base_offset = x0 * bpp; | ||
| 145 | const size_t copy_size = extent_x * bpp; | ||
| 146 | for (u32 y = y0; y < extent_y; y++) { | ||
| 147 | const size_t first_offset = y * pitch + base_offset; | ||
| 148 | const size_t second_offset = y * extent_x * bpp; | ||
| 149 | u8* write_to = unpack ? &output[first_offset] : &output[second_offset]; | ||
| 150 | const u8* read_from = unpack ? &input[second_offset] : &input[first_offset]; | ||
| 151 | std::memcpy(write_to, read_from, copy_size); | ||
| 152 | } | ||
| 153 | }; | ||
| 154 | 155 | ||
| 155 | const u32 src_extent_x = config.src_x1 - config.src_x0; | 156 | const u32 src_extent_x = config.src_x1 - config.src_x0; |
| 156 | const u32 src_extent_y = config.src_y1 - config.src_y0; | 157 | const u32 src_extent_y = config.src_y1 - config.src_y0; |
| @@ -205,8 +206,8 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, | |||
| 205 | src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y, | 206 | src.depth, config.src_x0, config.src_y0, src_extent_x, src_extent_y, |
| 206 | src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel); | 207 | src.block_height, src.block_depth, src_extent_x * src_bytes_per_pixel); |
| 207 | } else { | 208 | } else { |
| 208 | process_pitch_linear(false, tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, | 209 | ProcessPitchLinear<false>(tmp_buffer, impl->src_buffer, src_extent_x, src_extent_y, |
| 209 | src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); | 210 | src.pitch, config.src_x0, config.src_y0, src_bytes_per_pixel); |
| 210 | } | 211 | } |
| 211 | 212 | ||
| 212 | // Conversion Phase | 213 | // Conversion Phase |
| @@ -229,9 +230,9 @@ bool SoftwareBlitEngine::Blit(Fermi2D::Surface& src, Fermi2D::Surface& dst, | |||
| 229 | dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y, | 230 | dst.depth, config.dst_x0, config.dst_y0, dst_extent_x, dst_extent_y, |
| 230 | dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel); | 231 | dst.block_height, dst.block_depth, dst_extent_x * dst_bytes_per_pixel); |
| 231 | } else { | 232 | } else { |
| 232 | process_pitch_linear(true, impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, | 233 | ProcessPitchLinear<true>(impl->dst_buffer, tmp_buffer2, dst_extent_x, dst_extent_y, |
| 233 | dst.pitch, config.dst_x0, config.dst_y0, | 234 | dst.pitch, config.dst_x0, config.dst_y0, |
| 234 | static_cast<size_t>(dst_bytes_per_pixel)); | 235 | static_cast<size_t>(dst_bytes_per_pixel)); |
| 235 | } | 236 | } |
| 236 | return true; | 237 | return true; |
| 237 | } | 238 | } |
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/assert.h" | ||
| 5 | #include "video_core/framebuffer_config.h" | ||
| 6 | |||
| 7 | namespace Tegra { | ||
| 8 | |||
| 9 | Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, | ||
| 10 | u32 texture_height) { | ||
| 11 | f32 left, top, right, bottom; | ||
| 12 | |||
| 13 | if (!framebuffer.crop_rect.IsEmpty()) { | ||
| 14 | // If crop rectangle is not empty, apply properties from rectangle. | ||
| 15 | left = static_cast<f32>(framebuffer.crop_rect.left); | ||
| 16 | top = static_cast<f32>(framebuffer.crop_rect.top); | ||
| 17 | right = static_cast<f32>(framebuffer.crop_rect.right); | ||
| 18 | bottom = static_cast<f32>(framebuffer.crop_rect.bottom); | ||
| 19 | } else { | ||
| 20 | // Otherwise, fall back to framebuffer dimensions. | ||
| 21 | left = 0; | ||
| 22 | top = 0; | ||
| 23 | right = static_cast<f32>(framebuffer.width); | ||
| 24 | bottom = static_cast<f32>(framebuffer.height); | ||
| 25 | } | ||
| 26 | |||
| 27 | // Apply transformation flags. | ||
| 28 | auto framebuffer_transform_flags = framebuffer.transform_flags; | ||
| 29 | |||
| 30 | if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) { | ||
| 31 | // Switch left and right. | ||
| 32 | std::swap(left, right); | ||
| 33 | } | ||
| 34 | if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) { | ||
| 35 | // Switch top and bottom. | ||
| 36 | std::swap(top, bottom); | ||
| 37 | } | ||
| 38 | |||
| 39 | framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH; | ||
| 40 | framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV; | ||
| 41 | if (True(framebuffer_transform_flags)) { | ||
| 42 | UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}", | ||
| 43 | static_cast<u32>(framebuffer_transform_flags)); | ||
| 44 | } | ||
| 45 | |||
| 46 | // Normalize coordinate space. | ||
| 47 | left /= static_cast<f32>(texture_width); | ||
| 48 | top /= static_cast<f32>(texture_height); | ||
| 49 | right /= static_cast<f32>(texture_width); | ||
| 50 | bottom /= static_cast<f32>(texture_height); | ||
| 51 | |||
| 52 | return Common::Rectangle<f32>(left, top, right, bottom); | ||
| 53 | } | ||
| 54 | |||
| 55 | } // 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 @@ | |||
| 7 | #include "common/math_util.h" | 7 | #include "common/math_util.h" |
| 8 | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" | 8 | #include "core/hle/service/nvnflinger/buffer_transform_flags.h" |
| 9 | #include "core/hle/service/nvnflinger/pixel_format.h" | 9 | #include "core/hle/service/nvnflinger/pixel_format.h" |
| 10 | #include "core/hle/service/nvnflinger/ui/fence.h" | ||
| 10 | 11 | ||
| 11 | namespace Tegra { | 12 | namespace Tegra { |
| 12 | 13 | ||
| @@ -21,7 +22,10 @@ struct FramebufferConfig { | |||
| 21 | u32 stride{}; | 22 | u32 stride{}; |
| 22 | Service::android::PixelFormat pixel_format{}; | 23 | Service::android::PixelFormat pixel_format{}; |
| 23 | Service::android::BufferTransformFlags transform_flags{}; | 24 | Service::android::BufferTransformFlags transform_flags{}; |
| 24 | Common::Rectangle<int> crop_rect; | 25 | Common::Rectangle<int> crop_rect{}; |
| 25 | }; | 26 | }; |
| 26 | 27 | ||
| 28 | Common::Rectangle<f32> NormalizeCrop(const FramebufferConfig& framebuffer, u32 texture_width, | ||
| 29 | u32 texture_height); | ||
| 30 | |||
| 27 | } // namespace Tegra | 31 | } // 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 { | |||
| 274 | } | 274 | } |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | /// Swap buffers (render frame) | ||
| 278 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 279 | gpu_thread.SwapBuffers(framebuffer); | ||
| 280 | } | ||
| 281 | |||
| 282 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 277 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 283 | void FlushRegion(DAddr addr, u64 size) { | 278 | void FlushRegion(DAddr addr, u64 size) { |
| 284 | gpu_thread.FlushRegion(addr, size); | 279 | gpu_thread.FlushRegion(addr, size); |
| @@ -313,8 +308,9 @@ struct GPU::Impl { | |||
| 313 | gpu_thread.FlushAndInvalidateRegion(addr, size); | 308 | gpu_thread.FlushAndInvalidateRegion(addr, size); |
| 314 | } | 309 | } |
| 315 | 310 | ||
| 316 | void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, | 311 | void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, |
| 317 | std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { | 312 | std::vector<Service::Nvidia::NvFence>&& fences) { |
| 313 | size_t num_fences{fences.size()}; | ||
| 318 | size_t current_request_counter{}; | 314 | size_t current_request_counter{}; |
| 319 | { | 315 | { |
| 320 | std::unique_lock<std::mutex> lk(request_swap_mutex); | 316 | std::unique_lock<std::mutex> lk(request_swap_mutex); |
| @@ -328,13 +324,12 @@ struct GPU::Impl { | |||
| 328 | } | 324 | } |
| 329 | } | 325 | } |
| 330 | const auto wait_fence = | 326 | const auto wait_fence = |
| 331 | RequestSyncOperation([this, current_request_counter, framebuffer, fences, num_fences] { | 327 | RequestSyncOperation([this, current_request_counter, &layers, &fences, num_fences] { |
| 332 | auto& syncpoint_manager = host1x.GetSyncpointManager(); | 328 | auto& syncpoint_manager = host1x.GetSyncpointManager(); |
| 333 | if (num_fences == 0) { | 329 | if (num_fences == 0) { |
| 334 | renderer->SwapBuffers(framebuffer); | 330 | renderer->Composite(layers); |
| 335 | } | 331 | } |
| 336 | const auto executer = [this, current_request_counter, | 332 | const auto executer = [this, current_request_counter, layers_copy = layers]() { |
| 337 | framebuffer_copy = *framebuffer]() { | ||
| 338 | { | 333 | { |
| 339 | std::unique_lock<std::mutex> lk(request_swap_mutex); | 334 | std::unique_lock<std::mutex> lk(request_swap_mutex); |
| 340 | if (--request_swap_counters[current_request_counter] != 0) { | 335 | if (--request_swap_counters[current_request_counter] != 0) { |
| @@ -342,7 +337,7 @@ struct GPU::Impl { | |||
| 342 | } | 337 | } |
| 343 | free_swap_counters.push_back(current_request_counter); | 338 | free_swap_counters.push_back(current_request_counter); |
| 344 | } | 339 | } |
| 345 | renderer->SwapBuffers(&framebuffer_copy); | 340 | renderer->Composite(layers_copy); |
| 346 | }; | 341 | }; |
| 347 | for (size_t i = 0; i < num_fences; i++) { | 342 | for (size_t i = 0; i < num_fences; i++) { |
| 348 | syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); | 343 | syncpoint_manager.RegisterGuestAction(fences[i].id, fences[i].value, executer); |
| @@ -505,9 +500,9 @@ const VideoCore::ShaderNotify& GPU::ShaderNotify() const { | |||
| 505 | return impl->ShaderNotify(); | 500 | return impl->ShaderNotify(); |
| 506 | } | 501 | } |
| 507 | 502 | ||
| 508 | void GPU::RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, | 503 | void GPU::RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, |
| 509 | std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences) { | 504 | std::vector<Service::Nvidia::NvFence>&& fences) { |
| 510 | impl->RequestSwapBuffers(framebuffer, fences, num_fences); | 505 | impl->RequestComposite(std::move(layers), std::move(fences)); |
| 511 | } | 506 | } |
| 512 | 507 | ||
| 513 | u64 GPU::GetTicks() const { | 508 | u64 GPU::GetTicks() const { |
| @@ -554,10 +549,6 @@ void GPU::ClearCdmaInstance(u32 id) { | |||
| 554 | impl->ClearCdmaInstance(id); | 549 | impl->ClearCdmaInstance(id); |
| 555 | } | 550 | } |
| 556 | 551 | ||
| 557 | void GPU::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 558 | impl->SwapBuffers(framebuffer); | ||
| 559 | } | ||
| 560 | |||
| 561 | VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { | 552 | VideoCore::RasterizerDownloadArea GPU::OnCPURead(PAddr addr, u64 size) { |
| 562 | return impl->OnCPURead(addr, size); | 553 | return impl->OnCPURead(addr, size); |
| 563 | } | 554 | } |
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: | |||
| 212 | 212 | ||
| 213 | void RendererFrameEndNotify(); | 213 | void RendererFrameEndNotify(); |
| 214 | 214 | ||
| 215 | void RequestSwapBuffers(const Tegra::FramebufferConfig* framebuffer, | 215 | void RequestComposite(std::vector<Tegra::FramebufferConfig>&& layers, |
| 216 | std::array<Service::Nvidia::NvFence, 4>& fences, size_t num_fences); | 216 | std::vector<Service::Nvidia::NvFence>&& fences); |
| 217 | 217 | ||
| 218 | /// Performs any additional setup necessary in order to begin GPU emulation. | 218 | /// Performs any additional setup necessary in order to begin GPU emulation. |
| 219 | /// This can be used to launch any necessary threads and register any necessary | 219 | /// 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, | |||
| 40 | } | 40 | } |
| 41 | if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { | 41 | if (auto* submit_list = std::get_if<SubmitListCommand>(&next.data)) { |
| 42 | scheduler.Push(submit_list->channel, std::move(submit_list->entries)); | 42 | scheduler.Push(submit_list->channel, std::move(submit_list->entries)); |
| 43 | } else if (const auto* data = std::get_if<SwapBuffersCommand>(&next.data)) { | ||
| 44 | renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr); | ||
| 45 | } else if (std::holds_alternative<GPUTickCommand>(next.data)) { | 43 | } else if (std::holds_alternative<GPUTickCommand>(next.data)) { |
| 46 | system.GPU().TickWork(); | 44 | system.GPU().TickWork(); |
| 47 | } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { | 45 | } else if (const auto* flush = std::get_if<FlushRegionCommand>(&next.data)) { |
| @@ -78,10 +76,6 @@ void ThreadManager::SubmitList(s32 channel, Tegra::CommandList&& entries) { | |||
| 78 | PushCommand(SubmitListCommand(channel, std::move(entries))); | 76 | PushCommand(SubmitListCommand(channel, std::move(entries))); |
| 79 | } | 77 | } |
| 80 | 78 | ||
| 81 | void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | ||
| 82 | PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt)); | ||
| 83 | } | ||
| 84 | |||
| 85 | void ThreadManager::FlushRegion(DAddr addr, u64 size) { | 79 | void ThreadManager::FlushRegion(DAddr addr, u64 size) { |
| 86 | if (!is_async) { | 80 | if (!is_async) { |
| 87 | // Always flush with synchronous GPU mode | 81 | // 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 { | |||
| 44 | Tegra::CommandList entries; | 44 | Tegra::CommandList entries; |
| 45 | }; | 45 | }; |
| 46 | 46 | ||
| 47 | /// Command to signal to the GPU thread that a swap buffers is pending | ||
| 48 | struct SwapBuffersCommand final { | ||
| 49 | explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer_) | ||
| 50 | : framebuffer{std::move(framebuffer_)} {} | ||
| 51 | |||
| 52 | std::optional<Tegra::FramebufferConfig> framebuffer; | ||
| 53 | }; | ||
| 54 | |||
| 55 | /// Command to signal to the GPU thread to flush a region | 47 | /// Command to signal to the GPU thread to flush a region |
| 56 | struct FlushRegionCommand final { | 48 | struct FlushRegionCommand final { |
| 57 | explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} | 49 | explicit constexpr FlushRegionCommand(DAddr addr_, u64 size_) : addr{addr_}, size{size_} {} |
| @@ -81,8 +73,8 @@ struct FlushAndInvalidateRegionCommand final { | |||
| 81 | struct GPUTickCommand final {}; | 73 | struct GPUTickCommand final {}; |
| 82 | 74 | ||
| 83 | using CommandData = | 75 | using CommandData = |
| 84 | std::variant<std::monostate, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand, | 76 | std::variant<std::monostate, SubmitListCommand, FlushRegionCommand, InvalidateRegionCommand, |
| 85 | InvalidateRegionCommand, FlushAndInvalidateRegionCommand, GPUTickCommand>; | 77 | FlushAndInvalidateRegionCommand, GPUTickCommand>; |
| 86 | 78 | ||
| 87 | struct CommandDataContainer { | 79 | struct CommandDataContainer { |
| 88 | CommandDataContainer() = default; | 80 | CommandDataContainer() = default; |
| @@ -118,9 +110,6 @@ public: | |||
| 118 | /// Push GPU command entries to be processed | 110 | /// Push GPU command entries to be processed |
| 119 | void SubmitList(s32 channel, Tegra::CommandList&& entries); | 111 | void SubmitList(s32 channel, Tegra::CommandList&& entries); |
| 120 | 112 | ||
| 121 | /// Swap buffers (render frame) | ||
| 122 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer); | ||
| 123 | |||
| 124 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory | 113 | /// Notify rasterizer that any caches of the specified region should be flushed to Switch memory |
| 125 | void FlushRegion(DAddr addr, u64 size); | 114 | void FlushRegion(DAddr addr, u64 size); |
| 126 | 115 | ||
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 | |||
| 9 | ) | 9 | ) |
| 10 | 10 | ||
| 11 | set(GLSL_INCLUDES | 11 | set(GLSL_INCLUDES |
| 12 | fidelityfx_fsr.comp | 12 | fidelityfx_fsr.frag |
| 13 | ${FIDELITYFX_FILES} | 13 | ${FIDELITYFX_FILES} |
| 14 | ) | 14 | ) |
| 15 | 15 | ||
| @@ -56,10 +56,11 @@ set(SHADER_FILES | |||
| 56 | vulkan_color_clear.frag | 56 | vulkan_color_clear.frag |
| 57 | vulkan_color_clear.vert | 57 | vulkan_color_clear.vert |
| 58 | vulkan_depthstencil_clear.frag | 58 | vulkan_depthstencil_clear.frag |
| 59 | vulkan_fidelityfx_fsr_easu_fp16.comp | 59 | vulkan_fidelityfx_fsr.vert |
| 60 | vulkan_fidelityfx_fsr_easu_fp32.comp | 60 | vulkan_fidelityfx_fsr_easu_fp16.frag |
| 61 | vulkan_fidelityfx_fsr_rcas_fp16.comp | 61 | vulkan_fidelityfx_fsr_easu_fp32.frag |
| 62 | vulkan_fidelityfx_fsr_rcas_fp32.comp | 62 | vulkan_fidelityfx_fsr_rcas_fp16.frag |
| 63 | vulkan_fidelityfx_fsr_rcas_fp32.frag | ||
| 63 | vulkan_present.frag | 64 | vulkan_present.frag |
| 64 | vulkan_present.vert | 65 | vulkan_present.vert |
| 65 | vulkan_present_scaleforce_fp16.frag | 66 | 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 { | |||
| 34 | }; | 34 | }; |
| 35 | 35 | ||
| 36 | layout(set=0,binding=0) uniform sampler2D InputTexture; | 36 | layout(set=0,binding=0) uniform sampler2D InputTexture; |
| 37 | layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture; | ||
| 38 | 37 | ||
| 39 | #define A_GPU 1 | 38 | #define A_GPU 1 |
| 40 | #define A_GLSL 1 | 39 | #define A_GLSL 1 |
| @@ -72,44 +71,40 @@ layout(set=0,binding=1,rgba16f) uniform image2D OutputTexture; | |||
| 72 | 71 | ||
| 73 | #include "ffx_fsr1.h" | 72 | #include "ffx_fsr1.h" |
| 74 | 73 | ||
| 75 | void CurrFilter(AU2 pos) { | 74 | #if USE_RCAS |
| 76 | #if USE_BILINEAR | 75 | layout(location = 0) in vec2 frag_texcoord; |
| 77 | 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); | ||
| 78 | imageStore(OutputTexture, ASU2(pos), textureLod(InputTexture, pp, 0.0)); | ||
| 79 | #endif | 76 | #endif |
| 77 | layout (location = 0) out vec4 frag_color; | ||
| 78 | |||
| 79 | void CurrFilter(AU2 pos) { | ||
| 80 | #if USE_EASU | 80 | #if USE_EASU |
| 81 | #ifndef YUZU_USE_FP16 | 81 | #ifndef YUZU_USE_FP16 |
| 82 | AF3 c; | 82 | AF3 c; |
| 83 | FsrEasuF(c, pos, Const0, Const1, Const2, Const3); | 83 | FsrEasuF(c, pos, Const0, Const1, Const2, Const3); |
| 84 | imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); | 84 | frag_color = AF4(c, 1.0); |
| 85 | #else | 85 | #else |
| 86 | AH3 c; | 86 | AH3 c; |
| 87 | FsrEasuH(c, pos, Const0, Const1, Const2, Const3); | 87 | FsrEasuH(c, pos, Const0, Const1, Const2, Const3); |
| 88 | imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); | 88 | frag_color = AH4(c, 1.0); |
| 89 | #endif | 89 | #endif |
| 90 | #endif | 90 | #endif |
| 91 | #if USE_RCAS | 91 | #if USE_RCAS |
| 92 | #ifndef YUZU_USE_FP16 | 92 | #ifndef YUZU_USE_FP16 |
| 93 | AF3 c; | 93 | AF3 c; |
| 94 | FsrRcasF(c.r, c.g, c.b, pos, Const0); | 94 | FsrRcasF(c.r, c.g, c.b, pos, Const0); |
| 95 | imageStore(OutputTexture, ASU2(pos), AF4(c, 1)); | 95 | frag_color = AF4(c, 1.0); |
| 96 | #else | 96 | #else |
| 97 | AH3 c; | 97 | AH3 c; |
| 98 | FsrRcasH(c.r, c.g, c.b, pos, Const0); | 98 | FsrRcasH(c.r, c.g, c.b, pos, Const0); |
| 99 | imageStore(OutputTexture, ASU2(pos), AH4(c, 1)); | 99 | frag_color = AH4(c, 1.0); |
| 100 | #endif | 100 | #endif |
| 101 | #endif | 101 | #endif |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | layout(local_size_x=64) in; | ||
| 105 | void main() { | 104 | void main() { |
| 106 | // Do remapping of local xy in workgroup for a more PS-like swizzle pattern. | 105 | #if USE_RCAS |
| 107 | AU2 gxy = ARmp8x8(gl_LocalInvocationID.x) + AU2(gl_WorkGroupID.x << 4u, gl_WorkGroupID.y << 4u); | 106 | CurrFilter(AU2(frag_texcoord * vec2(textureSize(InputTexture, 0)))); |
| 108 | CurrFilter(gxy); | 107 | #else |
| 109 | gxy.x += 8u; | 108 | CurrFilter(AU2(gl_FragCoord.xy)); |
| 110 | CurrFilter(gxy); | 109 | #endif |
| 111 | gxy.y += 8u; | ||
| 112 | CurrFilter(gxy); | ||
| 113 | gxy.x -= 8u; | ||
| 114 | CurrFilter(gxy); | ||
| 115 | } | 110 | } |
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 { | |||
| 7 | vec4 gl_Position; | 7 | vec4 gl_Position; |
| 8 | }; | 8 | }; |
| 9 | 9 | ||
| 10 | const vec2 vertices[4] = | 10 | const vec2 vertices[3] = |
| 11 | vec2[4](vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0)); | 11 | vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3)); |
| 12 | 12 | ||
| 13 | layout (location = 0) out vec4 posPos; | 13 | layout (location = 0) out vec4 posPos; |
| 14 | 14 | ||
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 @@ | |||
| 26 | 26 | ||
| 27 | #endif | 27 | #endif |
| 28 | 28 | ||
| 29 | #ifdef VULKAN | ||
| 30 | |||
| 31 | #define BINDING_COLOR_TEXTURE 1 | ||
| 32 | |||
| 33 | #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv | ||
| 34 | |||
| 35 | #define BINDING_COLOR_TEXTURE 0 | ||
| 36 | |||
| 37 | #endif | ||
| 38 | |||
| 39 | layout (location = 0) in vec2 tex_coord; | 29 | layout (location = 0) in vec2 tex_coord; |
| 40 | 30 | ||
| 41 | layout (location = 0) out vec4 frag_color; | 31 | layout (location = 0) out vec4 frag_color; |
| 42 | 32 | ||
| 43 | layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D input_texture; | 33 | layout (binding = 0) uniform sampler2D input_texture; |
| 44 | 34 | ||
| 45 | const bool ignore_alpha = true; | 35 | const bool ignore_alpha = true; |
| 46 | 36 | ||
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 @@ | |||
| 3 | 3 | ||
| 4 | #version 460 core | 4 | #version 460 core |
| 5 | 5 | ||
| 6 | #ifdef VULKAN | ||
| 7 | |||
| 8 | #define BINDING_COLOR_TEXTURE 1 | ||
| 9 | |||
| 10 | #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv | ||
| 11 | |||
| 12 | #define BINDING_COLOR_TEXTURE 0 | ||
| 13 | |||
| 14 | #endif | ||
| 15 | |||
| 16 | 6 | ||
| 17 | layout (location = 0) in vec2 frag_tex_coord; | 7 | layout (location = 0) in vec2 frag_tex_coord; |
| 18 | 8 | ||
| 19 | layout (location = 0) out vec4 color; | 9 | layout (location = 0) out vec4 color; |
| 20 | 10 | ||
| 21 | layout (binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; | 11 | layout (binding = 0) uniform sampler2D color_texture; |
| 22 | 12 | ||
| 23 | vec4 cubic(float v) { | 13 | vec4 cubic(float v) { |
| 24 | vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; | 14 | 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 @@ | |||
| 7 | 7 | ||
| 8 | #version 460 core | 8 | #version 460 core |
| 9 | 9 | ||
| 10 | #ifdef VULKAN | ||
| 11 | |||
| 12 | #define BINDING_COLOR_TEXTURE 1 | ||
| 13 | |||
| 14 | #else // ^^^ Vulkan ^^^ // vvv OpenGL vvv | ||
| 15 | |||
| 16 | #define BINDING_COLOR_TEXTURE 0 | ||
| 17 | |||
| 18 | #endif | ||
| 19 | |||
| 20 | layout(location = 0) in vec2 frag_tex_coord; | 10 | layout(location = 0) in vec2 frag_tex_coord; |
| 21 | 11 | ||
| 22 | layout(location = 0) out vec4 color; | 12 | layout(location = 0) out vec4 color; |
| 23 | 13 | ||
| 24 | layout(binding = BINDING_COLOR_TEXTURE) uniform sampler2D color_texture; | 14 | layout(binding = 0) uniform sampler2D color_texture; |
| 25 | 15 | ||
| 26 | const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308); | 16 | const float offset[3] = float[](0.0, 1.3846153846, 3.2307692308); |
| 27 | const float weight[3] = float[](0.2270270270, 0.3162162162, 0.0702702703); | 17 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #version 450 | ||
| 5 | |||
| 6 | layout(location = 0) out vec2 texcoord; | ||
| 7 | |||
| 8 | void main() { | ||
| 9 | float x = float((gl_VertexIndex & 1) << 2); | ||
| 10 | float y = float((gl_VertexIndex & 2) << 1); | ||
| 11 | gl_Position = vec4(x - 1.0, y - 1.0, 0.0, 1.0); | ||
| 12 | texcoord = vec2(x, y) / 2.0; | ||
| 13 | } | ||
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 @@ | |||
| 7 | #define YUZU_USE_FP16 | 7 | #define YUZU_USE_FP16 |
| 8 | #define USE_EASU 1 | 8 | #define USE_EASU 1 |
| 9 | 9 | ||
| 10 | #include "fidelityfx_fsr.comp" | 10 | #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 @@ | |||
| 6 | 6 | ||
| 7 | #define USE_EASU 1 | 7 | #define USE_EASU 1 |
| 8 | 8 | ||
| 9 | #include "fidelityfx_fsr.comp" | 9 | #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 @@ | |||
| 7 | #define YUZU_USE_FP16 | 7 | #define YUZU_USE_FP16 |
| 8 | #define USE_RCAS 1 | 8 | #define USE_RCAS 1 |
| 9 | 9 | ||
| 10 | #include "fidelityfx_fsr.comp" | 10 | #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 @@ | |||
| 6 | 6 | ||
| 7 | #define USE_RCAS 1 | 7 | #define USE_RCAS 1 |
| 8 | 8 | ||
| 9 | #include "fidelityfx_fsr.comp" | 9 | #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; | |||
| 7 | 7 | ||
| 8 | layout (location = 0) out vec4 color; | 8 | layout (location = 0) out vec4 color; |
| 9 | 9 | ||
| 10 | layout (binding = 1) uniform sampler2D color_texture; | 10 | layout (binding = 0) uniform sampler2D color_texture; |
| 11 | 11 | ||
| 12 | void main() { | 12 | void main() { |
| 13 | color = texture(color_texture, frag_tex_coord); | 13 | 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 @@ | |||
| 3 | 3 | ||
| 4 | #version 460 core | 4 | #version 460 core |
| 5 | 5 | ||
| 6 | layout (location = 0) in vec2 vert_position; | ||
| 7 | layout (location = 1) in vec2 vert_tex_coord; | ||
| 8 | |||
| 9 | layout (location = 0) out vec2 frag_tex_coord; | 6 | layout (location = 0) out vec2 frag_tex_coord; |
| 10 | 7 | ||
| 11 | layout (set = 0, binding = 0) uniform MatrixBlock { | 8 | struct ScreenRectVertex { |
| 9 | vec2 position; | ||
| 10 | vec2 tex_coord; | ||
| 11 | }; | ||
| 12 | |||
| 13 | layout (push_constant) uniform PushConstants { | ||
| 12 | mat4 modelview_matrix; | 14 | mat4 modelview_matrix; |
| 15 | ScreenRectVertex vertices[4]; | ||
| 13 | }; | 16 | }; |
| 14 | 17 | ||
| 18 | // Vulkan spec 15.8.1: | ||
| 19 | // Any member of a push constant block that is declared as an | ||
| 20 | // array must only be accessed with dynamically uniform indices. | ||
| 21 | ScreenRectVertex GetVertex(int index) { | ||
| 22 | switch (index) { | ||
| 23 | case 0: | ||
| 24 | default: | ||
| 25 | return vertices[0]; | ||
| 26 | case 1: | ||
| 27 | return vertices[1]; | ||
| 28 | case 2: | ||
| 29 | return vertices[2]; | ||
| 30 | case 3: | ||
| 31 | return vertices[3]; | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 15 | void main() { | 35 | void main() { |
| 16 | gl_Position = modelview_matrix * vec4(vert_position, 0.0, 1.0); | 36 | ScreenRectVertex vertex = GetVertex(gl_VertexIndex); |
| 17 | frag_tex_coord = vert_tex_coord; | 37 | gl_Position = modelview_matrix * vec4(vertex.position, 0.0, 1.0); |
| 38 | frag_tex_coord = vertex.tex_coord; | ||
| 18 | } | 39 | } |
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 @@ | |||
| 5 | 5 | ||
| 6 | #extension GL_GOOGLE_include_directive : enable | 6 | #extension GL_GOOGLE_include_directive : enable |
| 7 | 7 | ||
| 8 | #define VERSION 1 | ||
| 8 | #define YUZU_USE_FP16 | 9 | #define YUZU_USE_FP16 |
| 9 | 10 | ||
| 10 | #include "opengl_present_scaleforce.frag" | 11 | #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 @@ | |||
| 5 | 5 | ||
| 6 | #extension GL_GOOGLE_include_directive : enable | 6 | #extension GL_GOOGLE_include_directive : enable |
| 7 | 7 | ||
| 8 | #define VERSION 1 | ||
| 9 | |||
| 8 | #include "opengl_present_scaleforce.frag" | 10 | #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: | |||
| 155 | virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, | 155 | virtual void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, |
| 156 | std::span<const u8> memory) = 0; | 156 | std::span<const u8> memory) = 0; |
| 157 | 157 | ||
| 158 | /// Attempt to use a faster method to display the framebuffer to screen | ||
| 159 | [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, | ||
| 160 | DAddr framebuffer_addr, u32 pixel_stride) { | ||
| 161 | return false; | ||
| 162 | } | ||
| 163 | |||
| 164 | /// Initialize disk cached resources for the game being emulated | 158 | /// Initialize disk cached resources for the game being emulated |
| 165 | virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, | 159 | virtual void LoadDiskResources(u64 title_id, std::stop_token stop_loading, |
| 166 | const DiskResourceLoadCallback& callback) {} | 160 | 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: | |||
| 38 | virtual ~RendererBase(); | 38 | virtual ~RendererBase(); |
| 39 | 39 | ||
| 40 | /// Finalize rendering the guest frame and draw into the presentation texture | 40 | /// Finalize rendering the guest frame and draw into the presentation texture |
| 41 | virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0; | 41 | virtual void Composite(std::span<const Tegra::FramebufferConfig> layers) = 0; |
| 42 | 42 | ||
| 43 | [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; | 43 | [[nodiscard]] virtual RasterizerInterface* ReadRasterizer() = 0; |
| 44 | 44 | ||
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 | |||
| 92 | } | 92 | } |
| 93 | void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, | 93 | void RasterizerNull::AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, |
| 94 | std::span<const u8> memory) {} | 94 | std::span<const u8> memory) {} |
| 95 | bool RasterizerNull::AccelerateDisplay(const Tegra::FramebufferConfig& config, | ||
| 96 | DAddr framebuffer_addr, u32 pixel_stride) { | ||
| 97 | return true; | ||
| 98 | } | ||
| 99 | void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | 95 | void RasterizerNull::LoadDiskResources(u64 title_id, std::stop_token stop_loading, |
| 100 | const VideoCore::DiskResourceLoadCallback& callback) {} | 96 | const VideoCore::DiskResourceLoadCallback& callback) {} |
| 101 | void RasterizerNull::InitializeChannel(Tegra::Control::ChannelState& channel) { | 97 | 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: | |||
| 77 | Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; | 77 | Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; |
| 78 | void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, | 78 | void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, |
| 79 | std::span<const u8> memory) override; | 79 | std::span<const u8> memory) override; |
| 80 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, | ||
| 81 | u32 pixel_stride) override; | ||
| 82 | void LoadDiskResources(u64 title_id, std::stop_token stop_loading, | 80 | void LoadDiskResources(u64 title_id, std::stop_token stop_loading, |
| 83 | const VideoCore::DiskResourceLoadCallback& callback) override; | 81 | const VideoCore::DiskResourceLoadCallback& callback) override; |
| 84 | void InitializeChannel(Tegra::Control::ChannelState& channel) override; | 82 | 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 | |||
| 13 | 13 | ||
| 14 | RendererNull::~RendererNull() = default; | 14 | RendererNull::~RendererNull() = default; |
| 15 | 15 | ||
| 16 | void RendererNull::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 16 | void RendererNull::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 17 | if (!framebuffer) { | 17 | if (framebuffers.empty()) { |
| 18 | return; | 18 | return; |
| 19 | } | 19 | } |
| 20 | 20 | ||
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: | |||
| 17 | std::unique_ptr<Core::Frontend::GraphicsContext> context); | 17 | std::unique_ptr<Core::Frontend::GraphicsContext> context); |
| 18 | ~RendererNull() override; | 18 | ~RendererNull() override; |
| 19 | 19 | ||
| 20 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 20 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffer) override; |
| 21 | 21 | ||
| 22 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 22 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 23 | return &m_rasterizer; | 23 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/settings.h" | ||
| 5 | #include "video_core/renderer_opengl/gl_blit_screen.h" | ||
| 6 | #include "video_core/renderer_opengl/gl_state_tracker.h" | ||
| 7 | #include "video_core/renderer_opengl/present/filters.h" | ||
| 8 | #include "video_core/renderer_opengl/present/layer.h" | ||
| 9 | #include "video_core/renderer_opengl/present/window_adapt_pass.h" | ||
| 10 | |||
| 11 | namespace OpenGL { | ||
| 12 | |||
| 13 | BlitScreen::BlitScreen(RasterizerOpenGL& rasterizer_, | ||
| 14 | Tegra::MaxwellDeviceMemoryManager& device_memory_, | ||
| 15 | StateTracker& state_tracker_, ProgramManager& program_manager_, | ||
| 16 | Device& device_) | ||
| 17 | : rasterizer(rasterizer_), device_memory(device_memory_), state_tracker(state_tracker_), | ||
| 18 | program_manager(program_manager_), device(device_) {} | ||
| 19 | |||
| 20 | BlitScreen::~BlitScreen() = default; | ||
| 21 | |||
| 22 | void BlitScreen::DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, | ||
| 23 | const Layout::FramebufferLayout& layout) { | ||
| 24 | // TODO: Signal state tracker about these changes | ||
| 25 | state_tracker.NotifyScreenDrawVertexArray(); | ||
| 26 | state_tracker.NotifyPolygonModes(); | ||
| 27 | state_tracker.NotifyViewport0(); | ||
| 28 | state_tracker.NotifyScissor0(); | ||
| 29 | state_tracker.NotifyColorMask(0); | ||
| 30 | state_tracker.NotifyBlend0(); | ||
| 31 | state_tracker.NotifyFramebuffer(); | ||
| 32 | state_tracker.NotifyFrontFace(); | ||
| 33 | state_tracker.NotifyCullTest(); | ||
| 34 | state_tracker.NotifyDepthTest(); | ||
| 35 | state_tracker.NotifyStencilTest(); | ||
| 36 | state_tracker.NotifyPolygonOffset(); | ||
| 37 | state_tracker.NotifyRasterizeEnable(); | ||
| 38 | state_tracker.NotifyFramebufferSRGB(); | ||
| 39 | state_tracker.NotifyLogicOp(); | ||
| 40 | state_tracker.NotifyClipControl(); | ||
| 41 | state_tracker.NotifyAlphaTest(); | ||
| 42 | state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); | ||
| 43 | |||
| 44 | glEnable(GL_CULL_FACE); | ||
| 45 | glDisable(GL_COLOR_LOGIC_OP); | ||
| 46 | glDisable(GL_DEPTH_TEST); | ||
| 47 | glDisable(GL_STENCIL_TEST); | ||
| 48 | glDisable(GL_POLYGON_OFFSET_FILL); | ||
| 49 | glDisable(GL_RASTERIZER_DISCARD); | ||
| 50 | glDisable(GL_ALPHA_TEST); | ||
| 51 | glDisablei(GL_BLEND, 0); | ||
| 52 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | ||
| 53 | glCullFace(GL_BACK); | ||
| 54 | glFrontFace(GL_CW); | ||
| 55 | glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | ||
| 56 | glDepthRangeIndexed(0, 0.0, 0.0); | ||
| 57 | |||
| 58 | while (layers.size() < framebuffers.size()) { | ||
| 59 | layers.emplace_back(rasterizer, device_memory); | ||
| 60 | } | ||
| 61 | |||
| 62 | CreateWindowAdapt(); | ||
| 63 | window_adapt->DrawToFramebuffer(program_manager, layers, framebuffers, layout); | ||
| 64 | |||
| 65 | // TODO | ||
| 66 | // program_manager.RestoreGuestPipeline(); | ||
| 67 | } | ||
| 68 | |||
| 69 | void BlitScreen::CreateWindowAdapt() { | ||
| 70 | if (window_adapt && Settings::values.scaling_filter.GetValue() == current_window_adapt) { | ||
| 71 | return; | ||
| 72 | } | ||
| 73 | |||
| 74 | current_window_adapt = Settings::values.scaling_filter.GetValue(); | ||
| 75 | switch (current_window_adapt) { | ||
| 76 | case Settings::ScalingFilter::NearestNeighbor: | ||
| 77 | window_adapt = MakeNearestNeighbor(device); | ||
| 78 | break; | ||
| 79 | case Settings::ScalingFilter::Bicubic: | ||
| 80 | window_adapt = MakeBicubic(device); | ||
| 81 | break; | ||
| 82 | case Settings::ScalingFilter::Gaussian: | ||
| 83 | window_adapt = MakeGaussian(device); | ||
| 84 | break; | ||
| 85 | case Settings::ScalingFilter::ScaleForce: | ||
| 86 | window_adapt = MakeScaleForce(device); | ||
| 87 | break; | ||
| 88 | case Settings::ScalingFilter::Fsr: | ||
| 89 | case Settings::ScalingFilter::Bilinear: | ||
| 90 | default: | ||
| 91 | window_adapt = MakeBilinear(device); | ||
| 92 | break; | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <list> | ||
| 7 | #include <memory> | ||
| 8 | #include <span> | ||
| 9 | |||
| 10 | #include "core/hle/service/nvnflinger/pixel_format.h" | ||
| 11 | #include "video_core/host1x/gpu_device_memory_manager.h" | ||
| 12 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 13 | |||
| 14 | namespace Layout { | ||
| 15 | struct FramebufferLayout; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Tegra { | ||
| 19 | struct FramebufferConfig; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace Settings { | ||
| 23 | enum class ScalingFilter : u32; | ||
| 24 | } | ||
| 25 | |||
| 26 | namespace OpenGL { | ||
| 27 | |||
| 28 | class Device; | ||
| 29 | class Layer; | ||
| 30 | class ProgramManager; | ||
| 31 | class RasterizerOpenGL; | ||
| 32 | class StateTracker; | ||
| 33 | class WindowAdaptPass; | ||
| 34 | |||
| 35 | /// Structure used for storing information about the display target for the Switch screen | ||
| 36 | struct FramebufferTextureInfo { | ||
| 37 | GLuint display_texture{}; | ||
| 38 | u32 width; | ||
| 39 | u32 height; | ||
| 40 | u32 scaled_width; | ||
| 41 | u32 scaled_height; | ||
| 42 | }; | ||
| 43 | |||
| 44 | class BlitScreen { | ||
| 45 | public: | ||
| 46 | explicit BlitScreen(RasterizerOpenGL& rasterizer, | ||
| 47 | Tegra::MaxwellDeviceMemoryManager& device_memory, | ||
| 48 | StateTracker& state_tracker, ProgramManager& program_manager, | ||
| 49 | Device& device); | ||
| 50 | ~BlitScreen(); | ||
| 51 | |||
| 52 | /// Draws the emulated screens to the emulator window. | ||
| 53 | void DrawScreen(std::span<const Tegra::FramebufferConfig> framebuffers, | ||
| 54 | const Layout::FramebufferLayout& layout); | ||
| 55 | |||
| 56 | private: | ||
| 57 | void CreateWindowAdapt(); | ||
| 58 | |||
| 59 | RasterizerOpenGL& rasterizer; | ||
| 60 | Tegra::MaxwellDeviceMemoryManager& device_memory; | ||
| 61 | StateTracker& state_tracker; | ||
| 62 | ProgramManager& program_manager; | ||
| 63 | Device& device; | ||
| 64 | |||
| 65 | Settings::ScalingFilter current_window_adapt{}; | ||
| 66 | std::unique_ptr<WindowAdaptPass> window_adapt; | ||
| 67 | |||
| 68 | std::list<Layer> layers; | ||
| 69 | }; | ||
| 70 | |||
| 71 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/settings.h" | ||
| 5 | #include "video_core/fsr.h" | ||
| 6 | #include "video_core/renderer_opengl/gl_fsr.h" | ||
| 7 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 8 | #include "video_core/renderer_opengl/gl_shader_util.h" | ||
| 9 | |||
| 10 | namespace OpenGL { | ||
| 11 | using namespace FSR; | ||
| 12 | |||
| 13 | using FsrConstants = std::array<u32, 4 * 4>; | ||
| 14 | |||
| 15 | FSR::FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source, | ||
| 16 | std::string_view fsr_rcas_source) | ||
| 17 | : fsr_vertex{CreateProgram(fsr_vertex_source, GL_VERTEX_SHADER)}, | ||
| 18 | fsr_easu_frag{CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER)}, | ||
| 19 | fsr_rcas_frag{CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER)} { | ||
| 20 | glProgramUniform2f(fsr_vertex.handle, 0, 1.0f, 1.0f); | ||
| 21 | glProgramUniform2f(fsr_vertex.handle, 1, 0.0f, 0.0f); | ||
| 22 | } | ||
| 23 | |||
| 24 | FSR::~FSR() = default; | ||
| 25 | |||
| 26 | void FSR::Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen, | ||
| 27 | u32 input_image_width, u32 input_image_height, | ||
| 28 | const Common::Rectangle<int>& crop_rect) { | ||
| 29 | |||
| 30 | const auto output_image_width = screen.GetWidth(); | ||
| 31 | const auto output_image_height = screen.GetHeight(); | ||
| 32 | |||
| 33 | if (fsr_intermediate_tex.handle) { | ||
| 34 | GLint fsr_tex_width, fsr_tex_height; | ||
| 35 | glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_WIDTH, | ||
| 36 | &fsr_tex_width); | ||
| 37 | glGetTextureLevelParameteriv(fsr_intermediate_tex.handle, 0, GL_TEXTURE_HEIGHT, | ||
| 38 | &fsr_tex_height); | ||
| 39 | if (static_cast<u32>(fsr_tex_width) != output_image_width || | ||
| 40 | static_cast<u32>(fsr_tex_height) != output_image_height) { | ||
| 41 | fsr_intermediate_tex.Release(); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | if (!fsr_intermediate_tex.handle) { | ||
| 45 | fsr_intermediate_tex.Create(GL_TEXTURE_2D); | ||
| 46 | glTextureStorage2D(fsr_intermediate_tex.handle, 1, GL_RGB16F, output_image_width, | ||
| 47 | output_image_height); | ||
| 48 | glNamedFramebufferTexture(fsr_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 49 | fsr_intermediate_tex.handle, 0); | ||
| 50 | } | ||
| 51 | |||
| 52 | GLint old_draw_fb; | ||
| 53 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); | ||
| 54 | |||
| 55 | glFrontFace(GL_CW); | ||
| 56 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fsr_framebuffer.handle); | ||
| 57 | glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(output_image_width), | ||
| 58 | static_cast<GLfloat>(output_image_height)); | ||
| 59 | |||
| 60 | FsrConstants constants; | ||
| 61 | FsrEasuConOffset( | ||
| 62 | constants.data() + 0, constants.data() + 4, constants.data() + 8, constants.data() + 12, | ||
| 63 | |||
| 64 | static_cast<f32>(crop_rect.GetWidth()), static_cast<f32>(crop_rect.GetHeight()), | ||
| 65 | static_cast<f32>(input_image_width), static_cast<f32>(input_image_height), | ||
| 66 | static_cast<f32>(output_image_width), static_cast<f32>(output_image_height), | ||
| 67 | static_cast<f32>(crop_rect.left), static_cast<f32>(crop_rect.top)); | ||
| 68 | |||
| 69 | glProgramUniform4uiv(fsr_easu_frag.handle, 0, sizeof(constants), std::data(constants)); | ||
| 70 | |||
| 71 | program_manager.BindPresentPrograms(fsr_vertex.handle, fsr_easu_frag.handle); | ||
| 72 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 73 | |||
| 74 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||
| 75 | glBindTextureUnit(0, fsr_intermediate_tex.handle); | ||
| 76 | |||
| 77 | const float sharpening = | ||
| 78 | static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; | ||
| 79 | |||
| 80 | FsrRcasCon(constants.data(), sharpening); | ||
| 81 | glProgramUniform4uiv(fsr_rcas_frag.handle, 0, sizeof(constants), std::data(constants)); | ||
| 82 | } | ||
| 83 | |||
| 84 | void FSR::InitBuffers() { | ||
| 85 | fsr_framebuffer.Create(); | ||
| 86 | } | ||
| 87 | |||
| 88 | void FSR::ReleaseBuffers() { | ||
| 89 | fsr_framebuffer.Release(); | ||
| 90 | fsr_intermediate_tex.Release(); | ||
| 91 | } | ||
| 92 | |||
| 93 | const OGLProgram& FSR::GetPresentFragmentProgram() const noexcept { | ||
| 94 | return fsr_rcas_frag; | ||
| 95 | } | ||
| 96 | |||
| 97 | bool FSR::AreBuffersInitialized() const noexcept { | ||
| 98 | return fsr_framebuffer.handle; | ||
| 99 | } | ||
| 100 | |||
| 101 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <string_view> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/math_util.h" | ||
| 10 | #include "video_core/fsr.h" | ||
| 11 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 12 | |||
| 13 | namespace OpenGL { | ||
| 14 | |||
| 15 | class ProgramManager; | ||
| 16 | |||
| 17 | class FSR { | ||
| 18 | public: | ||
| 19 | explicit FSR(std::string_view fsr_vertex_source, std::string_view fsr_easu_source, | ||
| 20 | std::string_view fsr_rcas_source); | ||
| 21 | ~FSR(); | ||
| 22 | |||
| 23 | void Draw(ProgramManager& program_manager, const Common::Rectangle<u32>& screen, | ||
| 24 | u32 input_image_width, u32 input_image_height, | ||
| 25 | const Common::Rectangle<int>& crop_rect); | ||
| 26 | |||
| 27 | void InitBuffers(); | ||
| 28 | |||
| 29 | void ReleaseBuffers(); | ||
| 30 | |||
| 31 | [[nodiscard]] const OGLProgram& GetPresentFragmentProgram() const noexcept; | ||
| 32 | |||
| 33 | [[nodiscard]] bool AreBuffersInitialized() const noexcept; | ||
| 34 | |||
| 35 | private: | ||
| 36 | OGLFramebuffer fsr_framebuffer; | ||
| 37 | OGLProgram fsr_vertex; | ||
| 38 | OGLProgram fsr_easu_frag; | ||
| 39 | OGLProgram fsr_rcas_frag; | ||
| 40 | OGLTexture fsr_intermediate_tex; | ||
| 41 | }; | ||
| 42 | |||
| 43 | } // 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 | |||
| 71 | 71 | ||
| 72 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, | 72 | RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, |
| 73 | Tegra::MaxwellDeviceMemoryManager& device_memory_, | 73 | Tegra::MaxwellDeviceMemoryManager& device_memory_, |
| 74 | const Device& device_, ScreenInfo& screen_info_, | 74 | const Device& device_, ProgramManager& program_manager_, |
| 75 | ProgramManager& program_manager_, StateTracker& state_tracker_) | 75 | StateTracker& state_tracker_) |
| 76 | : gpu(gpu_), device_memory(device_memory_), device(device_), screen_info(screen_info_), | 76 | : gpu(gpu_), device_memory(device_memory_), device(device_), program_manager(program_manager_), |
| 77 | program_manager(program_manager_), state_tracker(state_tracker_), | 77 | state_tracker(state_tracker_), |
| 78 | texture_cache_runtime(device, program_manager, state_tracker, staging_buffer_pool), | 78 | texture_cache_runtime(device, program_manager, state_tracker, staging_buffer_pool), |
| 79 | texture_cache(texture_cache_runtime, device_memory_), | 79 | texture_cache(texture_cache_runtime, device_memory_), |
| 80 | buffer_cache_runtime(device, staging_buffer_pool), | 80 | buffer_cache_runtime(device, staging_buffer_pool), |
| @@ -739,27 +739,29 @@ void RasterizerOpenGL::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si | |||
| 739 | query_cache.InvalidateRegion(*cpu_addr, copy_size); | 739 | query_cache.InvalidateRegion(*cpu_addr, copy_size); |
| 740 | } | 740 | } |
| 741 | 741 | ||
| 742 | bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config, | 742 | std::optional<FramebufferTextureInfo> RasterizerOpenGL::AccelerateDisplay( |
| 743 | DAddr framebuffer_addr, u32 pixel_stride) { | 743 | const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, u32 pixel_stride) { |
| 744 | if (framebuffer_addr == 0) { | 744 | if (framebuffer_addr == 0) { |
| 745 | return false; | 745 | return {}; |
| 746 | } | 746 | } |
| 747 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); | 747 | MICROPROFILE_SCOPE(OpenGL_CacheManagement); |
| 748 | 748 | ||
| 749 | std::scoped_lock lock{texture_cache.mutex}; | 749 | std::scoped_lock lock{texture_cache.mutex}; |
| 750 | ImageView* const image_view{ | 750 | const auto [image_view, scaled] = |
| 751 | texture_cache.TryFindFramebufferImageView(config, framebuffer_addr)}; | 751 | texture_cache.TryFindFramebufferImageView(config, framebuffer_addr); |
| 752 | if (!image_view) { | 752 | if (!image_view) { |
| 753 | return false; | 753 | return {}; |
| 754 | } | 754 | } |
| 755 | // Verify that the cached surface is the same size and format as the requested framebuffer | ||
| 756 | // ASSERT_MSG(image_view->size.width == config.width, "Framebuffer width is different"); | ||
| 757 | // ASSERT_MSG(image_view->size.height == config.height, "Framebuffer height is different"); | ||
| 758 | 755 | ||
| 759 | screen_info.texture.width = image_view->size.width; | 756 | const auto& resolution = Settings::values.resolution_info; |
| 760 | screen_info.texture.height = image_view->size.height; | 757 | |
| 761 | screen_info.display_texture = image_view->Handle(Shader::TextureType::Color2D); | 758 | FramebufferTextureInfo info{}; |
| 762 | return true; | 759 | info.display_texture = image_view->Handle(Shader::TextureType::Color2D); |
| 760 | info.width = image_view->size.width; | ||
| 761 | info.height = image_view->size.height; | ||
| 762 | info.scaled_width = scaled ? resolution.ScaleUp(info.width) : info.width; | ||
| 763 | info.scaled_height = scaled ? resolution.ScaleUp(info.height) : info.height; | ||
| 764 | return info; | ||
| 763 | } | 765 | } |
| 764 | 766 | ||
| 765 | void RasterizerOpenGL::SyncState() { | 767 | 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 @@ | |||
| 16 | #include "video_core/engines/maxwell_dma.h" | 16 | #include "video_core/engines/maxwell_dma.h" |
| 17 | #include "video_core/rasterizer_interface.h" | 17 | #include "video_core/rasterizer_interface.h" |
| 18 | #include "video_core/renderer_opengl/blit_image.h" | 18 | #include "video_core/renderer_opengl/blit_image.h" |
| 19 | #include "video_core/renderer_opengl/gl_blit_screen.h" | ||
| 19 | #include "video_core/renderer_opengl/gl_buffer_cache.h" | 20 | #include "video_core/renderer_opengl/gl_buffer_cache.h" |
| 20 | #include "video_core/renderer_opengl/gl_device.h" | 21 | #include "video_core/renderer_opengl/gl_device.h" |
| 21 | #include "video_core/renderer_opengl/gl_fence_manager.h" | 22 | #include "video_core/renderer_opengl/gl_fence_manager.h" |
| @@ -37,7 +38,7 @@ class MemoryManager; | |||
| 37 | 38 | ||
| 38 | namespace OpenGL { | 39 | namespace OpenGL { |
| 39 | 40 | ||
| 40 | struct ScreenInfo; | 41 | struct FramebufferTextureInfo; |
| 41 | struct ShaderEntries; | 42 | struct ShaderEntries; |
| 42 | 43 | ||
| 43 | struct BindlessSSBO { | 44 | struct BindlessSSBO { |
| @@ -76,8 +77,8 @@ class RasterizerOpenGL : public VideoCore::RasterizerInterface, | |||
| 76 | public: | 77 | public: |
| 77 | explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, | 78 | explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, |
| 78 | Tegra::MaxwellDeviceMemoryManager& device_memory_, | 79 | Tegra::MaxwellDeviceMemoryManager& device_memory_, |
| 79 | const Device& device_, ScreenInfo& screen_info_, | 80 | const Device& device_, ProgramManager& program_manager_, |
| 80 | ProgramManager& program_manager_, StateTracker& state_tracker_); | 81 | StateTracker& state_tracker_); |
| 81 | ~RasterizerOpenGL() override; | 82 | ~RasterizerOpenGL() override; |
| 82 | 83 | ||
| 83 | void Draw(bool is_indexed, u32 instance_count) override; | 84 | void Draw(bool is_indexed, u32 instance_count) override; |
| @@ -122,8 +123,6 @@ public: | |||
| 122 | Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; | 123 | Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; |
| 123 | void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, | 124 | void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, |
| 124 | std::span<const u8> memory) override; | 125 | std::span<const u8> memory) override; |
| 125 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, | ||
| 126 | u32 pixel_stride) override; | ||
| 127 | void LoadDiskResources(u64 title_id, std::stop_token stop_loading, | 126 | void LoadDiskResources(u64 title_id, std::stop_token stop_loading, |
| 128 | const VideoCore::DiskResourceLoadCallback& callback) override; | 127 | const VideoCore::DiskResourceLoadCallback& callback) override; |
| 129 | 128 | ||
| @@ -144,6 +143,10 @@ public: | |||
| 144 | return true; | 143 | return true; |
| 145 | } | 144 | } |
| 146 | 145 | ||
| 146 | std::optional<FramebufferTextureInfo> AccelerateDisplay(const Tegra::FramebufferConfig& config, | ||
| 147 | VAddr framebuffer_addr, | ||
| 148 | u32 pixel_stride); | ||
| 149 | |||
| 147 | private: | 150 | private: |
| 148 | static constexpr size_t MAX_TEXTURES = 192; | 151 | static constexpr size_t MAX_TEXTURES = 192; |
| 149 | static constexpr size_t MAX_IMAGES = 48; | 152 | static constexpr size_t MAX_IMAGES = 48; |
| @@ -237,7 +240,6 @@ private: | |||
| 237 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 240 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| 238 | 241 | ||
| 239 | const Device& device; | 242 | const Device& device; |
| 240 | ScreenInfo& screen_info; | ||
| 241 | ProgramManager& program_manager; | 243 | ProgramManager& program_manager; |
| 242 | StateTracker& state_tracker; | 244 | StateTracker& state_tracker; |
| 243 | 245 | ||
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) { | |||
| 1051 | state_tracker.NotifyScissor0(); | 1051 | state_tracker.NotifyScissor0(); |
| 1052 | } | 1052 | } |
| 1053 | 1053 | ||
| 1054 | bool Image::IsRescaled() const { | ||
| 1055 | return True(flags & ImageFlagBits::Rescaled); | ||
| 1056 | } | ||
| 1057 | |||
| 1054 | bool Image::ScaleUp(bool ignore) { | 1058 | bool Image::ScaleUp(bool ignore) { |
| 1055 | const auto& resolution = runtime->resolution; | 1059 | const auto& resolution = runtime->resolution; |
| 1056 | if (!resolution.active) { | 1060 | 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: | |||
| 217 | return gl_type; | 217 | return gl_type; |
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | bool IsRescaled() const; | ||
| 221 | |||
| 220 | bool ScaleUp(bool ignore = false); | 222 | bool ScaleUp(bool ignore = false); |
| 221 | 223 | ||
| 222 | bool ScaleDown(bool ignore = false); | 224 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "video_core/host_shaders/opengl_present_frag.h" | ||
| 5 | #include "video_core/host_shaders/opengl_present_scaleforce_frag.h" | ||
| 6 | #include "video_core/host_shaders/present_bicubic_frag.h" | ||
| 7 | #include "video_core/host_shaders/present_gaussian_frag.h" | ||
| 8 | #include "video_core/renderer_opengl/present/filters.h" | ||
| 9 | #include "video_core/renderer_opengl/present/util.h" | ||
| 10 | |||
| 11 | namespace OpenGL { | ||
| 12 | |||
| 13 | std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device) { | ||
| 14 | return std::make_unique<WindowAdaptPass>(device, CreateNearestNeighborSampler(), | ||
| 15 | HostShaders::OPENGL_PRESENT_FRAG); | ||
| 16 | } | ||
| 17 | |||
| 18 | std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device) { | ||
| 19 | return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), | ||
| 20 | HostShaders::OPENGL_PRESENT_FRAG); | ||
| 21 | } | ||
| 22 | |||
| 23 | std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device) { | ||
| 24 | return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), | ||
| 25 | HostShaders::PRESENT_BICUBIC_FRAG); | ||
| 26 | } | ||
| 27 | |||
| 28 | std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device) { | ||
| 29 | return std::make_unique<WindowAdaptPass>(device, CreateBilinearSampler(), | ||
| 30 | HostShaders::PRESENT_GAUSSIAN_FRAG); | ||
| 31 | } | ||
| 32 | |||
| 33 | std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device) { | ||
| 34 | return std::make_unique<WindowAdaptPass>( | ||
| 35 | device, CreateBilinearSampler(), | ||
| 36 | fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG)); | ||
| 37 | } | ||
| 38 | |||
| 39 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include "video_core/renderer_opengl/present/window_adapt_pass.h" | ||
| 8 | |||
| 9 | namespace OpenGL { | ||
| 10 | |||
| 11 | std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device); | ||
| 12 | std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device); | ||
| 13 | std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device); | ||
| 14 | std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device); | ||
| 15 | std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device); | ||
| 16 | |||
| 17 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/settings.h" | ||
| 5 | #include "video_core/fsr.h" | ||
| 6 | #include "video_core/host_shaders/ffx_a_h.h" | ||
| 7 | #include "video_core/host_shaders/ffx_fsr1_h.h" | ||
| 8 | #include "video_core/host_shaders/full_screen_triangle_vert.h" | ||
| 9 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h" | ||
| 10 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h" | ||
| 11 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h" | ||
| 12 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 13 | #include "video_core/renderer_opengl/gl_shader_util.h" | ||
| 14 | #include "video_core/renderer_opengl/present/fsr.h" | ||
| 15 | #include "video_core/renderer_opengl/present/util.h" | ||
| 16 | |||
| 17 | namespace OpenGL { | ||
| 18 | using namespace FSR; | ||
| 19 | |||
| 20 | using FsrConstants = std::array<u32, 4 * 4>; | ||
| 21 | |||
| 22 | FSR::FSR(u32 output_width_, u32 output_height_) : width(output_width_), height(output_height_) { | ||
| 23 | std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; | ||
| 24 | ReplaceInclude(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); | ||
| 25 | ReplaceInclude(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); | ||
| 26 | |||
| 27 | std::string fsr_easu_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; | ||
| 28 | std::string fsr_rcas_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; | ||
| 29 | ReplaceInclude(fsr_easu_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||
| 30 | ReplaceInclude(fsr_rcas_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||
| 31 | |||
| 32 | vert = CreateProgram(HostShaders::FULL_SCREEN_TRIANGLE_VERT, GL_VERTEX_SHADER); | ||
| 33 | easu_frag = CreateProgram(fsr_easu_source, GL_FRAGMENT_SHADER); | ||
| 34 | rcas_frag = CreateProgram(fsr_rcas_source, GL_FRAGMENT_SHADER); | ||
| 35 | |||
| 36 | glProgramUniform2f(vert.handle, 0, 1.0f, -1.0f); | ||
| 37 | glProgramUniform2f(vert.handle, 1, 0.0f, 1.0f); | ||
| 38 | |||
| 39 | sampler = CreateBilinearSampler(); | ||
| 40 | framebuffer.Create(); | ||
| 41 | |||
| 42 | easu_tex.Create(GL_TEXTURE_2D); | ||
| 43 | glTextureStorage2D(easu_tex.handle, 1, GL_RGBA16F, width, height); | ||
| 44 | |||
| 45 | rcas_tex.Create(GL_TEXTURE_2D); | ||
| 46 | glTextureStorage2D(rcas_tex.handle, 1, GL_RGBA16F, width, height); | ||
| 47 | } | ||
| 48 | |||
| 49 | FSR::~FSR() = default; | ||
| 50 | |||
| 51 | GLuint FSR::Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width, | ||
| 52 | u32 input_image_height, const Common::Rectangle<f32>& crop_rect) { | ||
| 53 | const f32 input_width = static_cast<f32>(input_image_width); | ||
| 54 | const f32 input_height = static_cast<f32>(input_image_height); | ||
| 55 | const f32 output_width = static_cast<f32>(width); | ||
| 56 | const f32 output_height = static_cast<f32>(height); | ||
| 57 | const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_width; | ||
| 58 | const f32 viewport_x = crop_rect.left * input_width; | ||
| 59 | const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_height; | ||
| 60 | const f32 viewport_y = crop_rect.top * input_height; | ||
| 61 | |||
| 62 | FsrConstants easu_con{}; | ||
| 63 | FsrConstants rcas_con{}; | ||
| 64 | |||
| 65 | FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8, | ||
| 66 | easu_con.data() + 12, viewport_width, viewport_height, input_width, | ||
| 67 | input_height, output_width, output_height, viewport_x, viewport_y); | ||
| 68 | |||
| 69 | const float sharpening = | ||
| 70 | static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; | ||
| 71 | |||
| 72 | FsrRcasCon(rcas_con.data(), sharpening); | ||
| 73 | |||
| 74 | glProgramUniform4uiv(easu_frag.handle, 0, sizeof(easu_con), easu_con.data()); | ||
| 75 | glProgramUniform4uiv(rcas_frag.handle, 0, sizeof(rcas_con), rcas_con.data()); | ||
| 76 | |||
| 77 | glFrontFace(GL_CW); | ||
| 78 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle); | ||
| 79 | glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, easu_tex.handle, 0); | ||
| 80 | glViewportIndexedf(0, 0.0f, 0.0f, output_width, output_height); | ||
| 81 | program_manager.BindPresentPrograms(vert.handle, easu_frag.handle); | ||
| 82 | glBindTextureUnit(0, texture); | ||
| 83 | glBindSampler(0, sampler.handle); | ||
| 84 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 85 | |||
| 86 | glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, rcas_tex.handle, 0); | ||
| 87 | program_manager.BindPresentPrograms(vert.handle, rcas_frag.handle); | ||
| 88 | glBindTextureUnit(0, easu_tex.handle); | ||
| 89 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 90 | |||
| 91 | return rcas_tex.handle; | ||
| 92 | } | ||
| 93 | |||
| 94 | bool FSR::NeedsRecreation(const Common::Rectangle<u32>& screen) { | ||
| 95 | return screen.GetWidth() != width || screen.GetHeight() != height; | ||
| 96 | } | ||
| 97 | |||
| 98 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <string_view> | ||
| 7 | |||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/math_util.h" | ||
| 10 | #include "video_core/fsr.h" | ||
| 11 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 12 | |||
| 13 | namespace OpenGL { | ||
| 14 | |||
| 15 | class ProgramManager; | ||
| 16 | |||
| 17 | class FSR { | ||
| 18 | public: | ||
| 19 | explicit FSR(u32 output_width, u32 output_height); | ||
| 20 | ~FSR(); | ||
| 21 | |||
| 22 | GLuint Draw(ProgramManager& program_manager, GLuint texture, u32 input_image_width, | ||
| 23 | u32 input_image_height, const Common::Rectangle<f32>& crop_rect); | ||
| 24 | |||
| 25 | bool NeedsRecreation(const Common::Rectangle<u32>& screen); | ||
| 26 | |||
| 27 | private: | ||
| 28 | const u32 width; | ||
| 29 | const u32 height; | ||
| 30 | OGLFramebuffer framebuffer; | ||
| 31 | OGLSampler sampler; | ||
| 32 | OGLProgram vert; | ||
| 33 | OGLProgram easu_frag; | ||
| 34 | OGLProgram rcas_frag; | ||
| 35 | OGLTexture easu_tex; | ||
| 36 | OGLTexture rcas_tex; | ||
| 37 | }; | ||
| 38 | |||
| 39 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "video_core/host_shaders/fxaa_frag.h" | ||
| 5 | #include "video_core/host_shaders/fxaa_vert.h" | ||
| 6 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 7 | #include "video_core/renderer_opengl/gl_shader_util.h" | ||
| 8 | #include "video_core/renderer_opengl/present/fxaa.h" | ||
| 9 | #include "video_core/renderer_opengl/present/util.h" | ||
| 10 | |||
| 11 | namespace OpenGL { | ||
| 12 | |||
| 13 | FXAA::FXAA(u32 width, u32 height) { | ||
| 14 | vert_shader = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); | ||
| 15 | frag_shader = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); | ||
| 16 | |||
| 17 | sampler = CreateBilinearSampler(); | ||
| 18 | |||
| 19 | framebuffer.Create(); | ||
| 20 | |||
| 21 | texture.Create(GL_TEXTURE_2D); | ||
| 22 | glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height); | ||
| 23 | glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0); | ||
| 24 | } | ||
| 25 | |||
| 26 | FXAA::~FXAA() = default; | ||
| 27 | |||
| 28 | GLuint FXAA::Draw(ProgramManager& program_manager, GLuint input_texture) { | ||
| 29 | glFrontFace(GL_CCW); | ||
| 30 | |||
| 31 | program_manager.BindPresentPrograms(vert_shader.handle, frag_shader.handle); | ||
| 32 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle); | ||
| 33 | glBindTextureUnit(0, input_texture); | ||
| 34 | glBindSampler(0, sampler.handle); | ||
| 35 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 36 | glFrontFace(GL_CW); | ||
| 37 | |||
| 38 | return texture.handle; | ||
| 39 | } | ||
| 40 | |||
| 41 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 7 | |||
| 8 | namespace OpenGL { | ||
| 9 | |||
| 10 | class ProgramManager; | ||
| 11 | |||
| 12 | class FXAA { | ||
| 13 | public: | ||
| 14 | explicit FXAA(u32 width, u32 height); | ||
| 15 | ~FXAA(); | ||
| 16 | |||
| 17 | GLuint Draw(ProgramManager& program_manager, GLuint input_texture); | ||
| 18 | |||
| 19 | private: | ||
| 20 | OGLProgram vert_shader; | ||
| 21 | OGLProgram frag_shader; | ||
| 22 | OGLSampler sampler; | ||
| 23 | OGLFramebuffer framebuffer; | ||
| 24 | OGLTexture texture; | ||
| 25 | }; | ||
| 26 | |||
| 27 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "video_core/framebuffer_config.h" | ||
| 5 | #include "video_core/renderer_opengl/gl_blit_screen.h" | ||
| 6 | #include "video_core/renderer_opengl/gl_rasterizer.h" | ||
| 7 | #include "video_core/renderer_opengl/present/fsr.h" | ||
| 8 | #include "video_core/renderer_opengl/present/fxaa.h" | ||
| 9 | #include "video_core/renderer_opengl/present/layer.h" | ||
| 10 | #include "video_core/renderer_opengl/present/present_uniforms.h" | ||
| 11 | #include "video_core/renderer_opengl/present/smaa.h" | ||
| 12 | #include "video_core/surface.h" | ||
| 13 | #include "video_core/textures/decoders.h" | ||
| 14 | |||
| 15 | namespace OpenGL { | ||
| 16 | |||
| 17 | Layer::Layer(RasterizerOpenGL& rasterizer_, Tegra::MaxwellDeviceMemoryManager& device_memory_) | ||
| 18 | : rasterizer(rasterizer_), device_memory(device_memory_) { | ||
| 19 | // Allocate textures for the screen | ||
| 20 | framebuffer_texture.resource.Create(GL_TEXTURE_2D); | ||
| 21 | |||
| 22 | const GLuint texture = framebuffer_texture.resource.handle; | ||
| 23 | glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); | ||
| 24 | |||
| 25 | // Clear screen to black | ||
| 26 | const u8 framebuffer_data[4] = {0, 0, 0, 0}; | ||
| 27 | glClearTexImage(framebuffer_texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, | ||
| 28 | framebuffer_data); | ||
| 29 | } | ||
| 30 | |||
| 31 | Layer::~Layer() = default; | ||
| 32 | |||
| 33 | GLuint Layer::ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, | ||
| 34 | std::array<ScreenRectVertex, 4>& out_vertices, | ||
| 35 | ProgramManager& program_manager, | ||
| 36 | const Tegra::FramebufferConfig& framebuffer, | ||
| 37 | const Layout::FramebufferLayout& layout) { | ||
| 38 | FramebufferTextureInfo info = PrepareRenderTarget(framebuffer); | ||
| 39 | auto crop = Tegra::NormalizeCrop(framebuffer, info.width, info.height); | ||
| 40 | GLuint texture = info.display_texture; | ||
| 41 | |||
| 42 | auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); | ||
| 43 | if (anti_aliasing != Settings::AntiAliasing::None) { | ||
| 44 | glEnablei(GL_SCISSOR_TEST, 0); | ||
| 45 | auto viewport_width = Settings::values.resolution_info.ScaleUp(framebuffer_texture.width); | ||
| 46 | auto viewport_height = Settings::values.resolution_info.ScaleUp(framebuffer_texture.height); | ||
| 47 | |||
| 48 | glScissorIndexed(0, 0, 0, viewport_width, viewport_height); | ||
| 49 | glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width), | ||
| 50 | static_cast<GLfloat>(viewport_height)); | ||
| 51 | |||
| 52 | switch (anti_aliasing) { | ||
| 53 | case Settings::AntiAliasing::Fxaa: | ||
| 54 | CreateFXAA(); | ||
| 55 | texture = fxaa->Draw(program_manager, info.display_texture); | ||
| 56 | break; | ||
| 57 | case Settings::AntiAliasing::Smaa: | ||
| 58 | default: | ||
| 59 | CreateSMAA(); | ||
| 60 | texture = smaa->Draw(program_manager, info.display_texture); | ||
| 61 | break; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | glDisablei(GL_SCISSOR_TEST, 0); | ||
| 66 | |||
| 67 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 68 | if (!fsr || fsr->NeedsRecreation(layout.screen)) { | ||
| 69 | fsr = std::make_unique<FSR>(layout.screen.GetWidth(), layout.screen.GetHeight()); | ||
| 70 | } | ||
| 71 | |||
| 72 | texture = fsr->Draw(program_manager, texture, info.scaled_width, info.scaled_height, crop); | ||
| 73 | crop = {0, 0, 1, 1}; | ||
| 74 | } | ||
| 75 | |||
| 76 | out_matrix = | ||
| 77 | MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); | ||
| 78 | |||
| 79 | // Map the coordinates to the screen. | ||
| 80 | const auto& screen = layout.screen; | ||
| 81 | const auto x = screen.left; | ||
| 82 | const auto y = screen.top; | ||
| 83 | const auto w = screen.GetWidth(); | ||
| 84 | const auto h = screen.GetHeight(); | ||
| 85 | |||
| 86 | out_vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); | ||
| 87 | out_vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); | ||
| 88 | out_vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); | ||
| 89 | out_vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); | ||
| 90 | |||
| 91 | return texture; | ||
| 92 | } | ||
| 93 | |||
| 94 | FramebufferTextureInfo Layer::PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer) { | ||
| 95 | // If framebuffer is provided, reload it from memory to a texture | ||
| 96 | if (framebuffer_texture.width != static_cast<GLsizei>(framebuffer.width) || | ||
| 97 | framebuffer_texture.height != static_cast<GLsizei>(framebuffer.height) || | ||
| 98 | framebuffer_texture.pixel_format != framebuffer.pixel_format || | ||
| 99 | gl_framebuffer_data.empty()) { | ||
| 100 | // Reallocate texture if the framebuffer size has changed. | ||
| 101 | // This is expected to not happen very often and hence should not be a | ||
| 102 | // performance problem. | ||
| 103 | ConfigureFramebufferTexture(framebuffer); | ||
| 104 | } | ||
| 105 | |||
| 106 | // Load the framebuffer from memory if needed | ||
| 107 | return LoadFBToScreenInfo(framebuffer); | ||
| 108 | } | ||
| 109 | |||
| 110 | FramebufferTextureInfo Layer::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { | ||
| 111 | const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; | ||
| 112 | const auto accelerated_info = | ||
| 113 | rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride); | ||
| 114 | if (accelerated_info) { | ||
| 115 | return *accelerated_info; | ||
| 116 | } | ||
| 117 | |||
| 118 | // Reset the screen info's display texture to its own permanent texture | ||
| 119 | FramebufferTextureInfo info{}; | ||
| 120 | info.display_texture = framebuffer_texture.resource.handle; | ||
| 121 | info.width = framebuffer.width; | ||
| 122 | info.height = framebuffer.height; | ||
| 123 | info.scaled_width = framebuffer.width; | ||
| 124 | info.scaled_height = framebuffer.height; | ||
| 125 | |||
| 126 | // TODO(Rodrigo): Read this from HLE | ||
| 127 | constexpr u32 block_height_log2 = 4; | ||
| 128 | const auto pixel_format{ | ||
| 129 | VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||
| 130 | const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||
| 131 | const u64 size_in_bytes{Tegra::Texture::CalculateSize( | ||
| 132 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||
| 133 | const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; | ||
| 134 | const std::span<const u8> input_data(host_ptr, size_in_bytes); | ||
| 135 | Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, | ||
| 136 | framebuffer.width, framebuffer.height, 1, block_height_log2, | ||
| 137 | 0); | ||
| 138 | |||
| 139 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | ||
| 140 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); | ||
| 141 | |||
| 142 | // Update existing texture | ||
| 143 | // TODO: Test what happens on hardware when you change the framebuffer dimensions so that | ||
| 144 | // they differ from the LCD resolution. | ||
| 145 | // TODO: Applications could theoretically crash yuzu here by specifying too large | ||
| 146 | // framebuffer sizes. We should make sure that this cannot happen. | ||
| 147 | glTextureSubImage2D(framebuffer_texture.resource.handle, 0, 0, 0, framebuffer.width, | ||
| 148 | framebuffer.height, framebuffer_texture.gl_format, | ||
| 149 | framebuffer_texture.gl_type, gl_framebuffer_data.data()); | ||
| 150 | |||
| 151 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||
| 152 | |||
| 153 | return info; | ||
| 154 | } | ||
| 155 | |||
| 156 | void Layer::ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer) { | ||
| 157 | framebuffer_texture.width = framebuffer.width; | ||
| 158 | framebuffer_texture.height = framebuffer.height; | ||
| 159 | framebuffer_texture.pixel_format = framebuffer.pixel_format; | ||
| 160 | |||
| 161 | const auto pixel_format{ | ||
| 162 | VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||
| 163 | const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||
| 164 | gl_framebuffer_data.resize(framebuffer_texture.width * framebuffer_texture.height * | ||
| 165 | bytes_per_pixel); | ||
| 166 | |||
| 167 | GLint internal_format; | ||
| 168 | switch (framebuffer.pixel_format) { | ||
| 169 | case Service::android::PixelFormat::Rgba8888: | ||
| 170 | internal_format = GL_RGBA8; | ||
| 171 | framebuffer_texture.gl_format = GL_RGBA; | ||
| 172 | framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 173 | break; | ||
| 174 | case Service::android::PixelFormat::Rgb565: | ||
| 175 | internal_format = GL_RGB565; | ||
| 176 | framebuffer_texture.gl_format = GL_RGB; | ||
| 177 | framebuffer_texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; | ||
| 178 | break; | ||
| 179 | default: | ||
| 180 | internal_format = GL_RGBA8; | ||
| 181 | framebuffer_texture.gl_format = GL_RGBA; | ||
| 182 | framebuffer_texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 183 | // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||
| 184 | // static_cast<u32>(framebuffer.pixel_format)); | ||
| 185 | break; | ||
| 186 | } | ||
| 187 | |||
| 188 | framebuffer_texture.resource.Release(); | ||
| 189 | framebuffer_texture.resource.Create(GL_TEXTURE_2D); | ||
| 190 | glTextureStorage2D(framebuffer_texture.resource.handle, 1, internal_format, | ||
| 191 | framebuffer_texture.width, framebuffer_texture.height); | ||
| 192 | |||
| 193 | fxaa.reset(); | ||
| 194 | smaa.reset(); | ||
| 195 | } | ||
| 196 | |||
| 197 | void Layer::CreateFXAA() { | ||
| 198 | smaa.reset(); | ||
| 199 | if (!fxaa) { | ||
| 200 | fxaa = std::make_unique<FXAA>( | ||
| 201 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||
| 202 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||
| 203 | } | ||
| 204 | } | ||
| 205 | |||
| 206 | void Layer::CreateSMAA() { | ||
| 207 | fxaa.reset(); | ||
| 208 | if (!smaa) { | ||
| 209 | smaa = std::make_unique<SMAA>( | ||
| 210 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.width), | ||
| 211 | Settings::values.resolution_info.ScaleUp(framebuffer_texture.height)); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <memory> | ||
| 7 | #include <vector> | ||
| 8 | |||
| 9 | #include "video_core/host1x/gpu_device_memory_manager.h" | ||
| 10 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 11 | |||
| 12 | namespace Layout { | ||
| 13 | struct FramebufferLayout; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Service::android { | ||
| 17 | enum class PixelFormat : u32; | ||
| 18 | }; | ||
| 19 | |||
| 20 | namespace Tegra { | ||
| 21 | struct FramebufferConfig; | ||
| 22 | } | ||
| 23 | |||
| 24 | namespace OpenGL { | ||
| 25 | |||
| 26 | struct FramebufferTextureInfo; | ||
| 27 | class FSR; | ||
| 28 | class FXAA; | ||
| 29 | class ProgramManager; | ||
| 30 | class RasterizerOpenGL; | ||
| 31 | class SMAA; | ||
| 32 | |||
| 33 | /// Structure used for storing information about the textures for the Switch screen | ||
| 34 | struct TextureInfo { | ||
| 35 | OGLTexture resource; | ||
| 36 | GLsizei width; | ||
| 37 | GLsizei height; | ||
| 38 | GLenum gl_format; | ||
| 39 | GLenum gl_type; | ||
| 40 | Service::android::PixelFormat pixel_format; | ||
| 41 | }; | ||
| 42 | |||
| 43 | struct ScreenRectVertex; | ||
| 44 | |||
| 45 | class Layer { | ||
| 46 | public: | ||
| 47 | explicit Layer(RasterizerOpenGL& rasterizer, Tegra::MaxwellDeviceMemoryManager& device_memory); | ||
| 48 | ~Layer(); | ||
| 49 | |||
| 50 | GLuint ConfigureDraw(std::array<GLfloat, 3 * 2>& out_matrix, | ||
| 51 | std::array<ScreenRectVertex, 4>& out_vertices, | ||
| 52 | ProgramManager& program_manager, | ||
| 53 | const Tegra::FramebufferConfig& framebuffer, | ||
| 54 | const Layout::FramebufferLayout& layout); | ||
| 55 | |||
| 56 | private: | ||
| 57 | /// Loads framebuffer from emulated memory into the active OpenGL texture. | ||
| 58 | FramebufferTextureInfo LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); | ||
| 59 | FramebufferTextureInfo PrepareRenderTarget(const Tegra::FramebufferConfig& framebuffer); | ||
| 60 | void ConfigureFramebufferTexture(const Tegra::FramebufferConfig& framebuffer); | ||
| 61 | |||
| 62 | void CreateFXAA(); | ||
| 63 | void CreateSMAA(); | ||
| 64 | |||
| 65 | private: | ||
| 66 | RasterizerOpenGL& rasterizer; | ||
| 67 | Tegra::MaxwellDeviceMemoryManager& device_memory; | ||
| 68 | |||
| 69 | /// OpenGL framebuffer data | ||
| 70 | std::vector<u8> gl_framebuffer_data; | ||
| 71 | |||
| 72 | /// Display information for Switch screen | ||
| 73 | TextureInfo framebuffer_texture; | ||
| 74 | |||
| 75 | std::unique_ptr<FSR> fsr; | ||
| 76 | std::unique_ptr<FXAA> fxaa; | ||
| 77 | std::unique_ptr<SMAA> smaa; | ||
| 78 | }; | ||
| 79 | |||
| 80 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 7 | |||
| 8 | namespace OpenGL { | ||
| 9 | |||
| 10 | constexpr GLint PositionLocation = 0; | ||
| 11 | constexpr GLint TexCoordLocation = 1; | ||
| 12 | constexpr GLint ModelViewMatrixLocation = 0; | ||
| 13 | |||
| 14 | struct ScreenRectVertex { | ||
| 15 | constexpr ScreenRectVertex() = default; | ||
| 16 | |||
| 17 | constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) | ||
| 18 | : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} | ||
| 19 | |||
| 20 | std::array<GLfloat, 2> position{}; | ||
| 21 | std::array<GLfloat, 2> tex_coord{}; | ||
| 22 | }; | ||
| 23 | |||
| 24 | /** | ||
| 25 | * Defines a 1:1 pixel orthographic projection matrix with (0,0) on the top-left | ||
| 26 | * corner and (width, height) on the lower-bottom. | ||
| 27 | * | ||
| 28 | * The projection part of the matrix is trivial, hence these operations are represented | ||
| 29 | * by a 3x2 matrix. | ||
| 30 | */ | ||
| 31 | static inline std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { | ||
| 32 | std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order | ||
| 33 | |||
| 34 | // clang-format off | ||
| 35 | matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; | ||
| 36 | matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; | ||
| 37 | // Last matrix row is implicitly assumed to be [0, 0, 1]. | ||
| 38 | // clang-format on | ||
| 39 | |||
| 40 | return matrix; | ||
| 41 | } | ||
| 42 | |||
| 43 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "video_core/host_shaders/opengl_smaa_glsl.h" | ||
| 5 | #include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" | ||
| 6 | #include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" | ||
| 7 | #include "video_core/host_shaders/smaa_edge_detection_frag.h" | ||
| 8 | #include "video_core/host_shaders/smaa_edge_detection_vert.h" | ||
| 9 | #include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" | ||
| 10 | #include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" | ||
| 11 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 12 | #include "video_core/renderer_opengl/gl_shader_util.h" | ||
| 13 | #include "video_core/renderer_opengl/present/smaa.h" | ||
| 14 | #include "video_core/renderer_opengl/present/util.h" | ||
| 15 | #include "video_core/smaa_area_tex.h" | ||
| 16 | #include "video_core/smaa_search_tex.h" | ||
| 17 | |||
| 18 | namespace OpenGL { | ||
| 19 | |||
| 20 | SMAA::SMAA(u32 width, u32 height) { | ||
| 21 | const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { | ||
| 22 | std::string shader_source{specialized_source}; | ||
| 23 | ReplaceInclude(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); | ||
| 24 | return CreateProgram(shader_source, stage); | ||
| 25 | }; | ||
| 26 | |||
| 27 | edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); | ||
| 28 | edge_detection_frag = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); | ||
| 29 | blending_weight_calculation_vert = | ||
| 30 | SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); | ||
| 31 | blending_weight_calculation_frag = | ||
| 32 | SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); | ||
| 33 | neighborhood_blending_vert = | ||
| 34 | SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); | ||
| 35 | neighborhood_blending_frag = | ||
| 36 | SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); | ||
| 37 | |||
| 38 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | ||
| 39 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||
| 40 | |||
| 41 | area_tex.Create(GL_TEXTURE_2D); | ||
| 42 | glTextureStorage2D(area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); | ||
| 43 | glTextureSubImage2D(area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, | ||
| 44 | GL_UNSIGNED_BYTE, areaTexBytes); | ||
| 45 | search_tex.Create(GL_TEXTURE_2D); | ||
| 46 | glTextureStorage2D(search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); | ||
| 47 | glTextureSubImage2D(search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, | ||
| 48 | GL_UNSIGNED_BYTE, searchTexBytes); | ||
| 49 | |||
| 50 | edges_tex.Create(GL_TEXTURE_2D); | ||
| 51 | glTextureStorage2D(edges_tex.handle, 1, GL_RG16F, width, height); | ||
| 52 | |||
| 53 | blend_tex.Create(GL_TEXTURE_2D); | ||
| 54 | glTextureStorage2D(blend_tex.handle, 1, GL_RGBA16F, width, height); | ||
| 55 | |||
| 56 | sampler = CreateBilinearSampler(); | ||
| 57 | |||
| 58 | framebuffer.Create(); | ||
| 59 | |||
| 60 | texture.Create(GL_TEXTURE_2D); | ||
| 61 | glTextureStorage2D(texture.handle, 1, GL_RGBA16F, width, height); | ||
| 62 | glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0); | ||
| 63 | } | ||
| 64 | |||
| 65 | SMAA::~SMAA() = default; | ||
| 66 | |||
| 67 | GLuint SMAA::Draw(ProgramManager& program_manager, GLuint input_texture) { | ||
| 68 | glClearColor(0, 0, 0, 0); | ||
| 69 | glFrontFace(GL_CCW); | ||
| 70 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.handle); | ||
| 71 | glBindSampler(0, sampler.handle); | ||
| 72 | glBindSampler(1, sampler.handle); | ||
| 73 | glBindSampler(2, sampler.handle); | ||
| 74 | |||
| 75 | glBindTextureUnit(0, input_texture); | ||
| 76 | glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, edges_tex.handle, 0); | ||
| 77 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 78 | program_manager.BindPresentPrograms(edge_detection_vert.handle, edge_detection_frag.handle); | ||
| 79 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 80 | |||
| 81 | glBindTextureUnit(0, edges_tex.handle); | ||
| 82 | glBindTextureUnit(1, area_tex.handle); | ||
| 83 | glBindTextureUnit(2, search_tex.handle); | ||
| 84 | glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, blend_tex.handle, 0); | ||
| 85 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 86 | program_manager.BindPresentPrograms(blending_weight_calculation_vert.handle, | ||
| 87 | blending_weight_calculation_frag.handle); | ||
| 88 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 89 | |||
| 90 | glBindTextureUnit(0, input_texture); | ||
| 91 | glBindTextureUnit(1, blend_tex.handle); | ||
| 92 | glNamedFramebufferTexture(framebuffer.handle, GL_COLOR_ATTACHMENT0, texture.handle, 0); | ||
| 93 | program_manager.BindPresentPrograms(neighborhood_blending_vert.handle, | ||
| 94 | neighborhood_blending_frag.handle); | ||
| 95 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 96 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 97 | glFrontFace(GL_CW); | ||
| 98 | |||
| 99 | return texture.handle; | ||
| 100 | } | ||
| 101 | |||
| 102 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 7 | |||
| 8 | namespace OpenGL { | ||
| 9 | |||
| 10 | class ProgramManager; | ||
| 11 | |||
| 12 | class SMAA { | ||
| 13 | public: | ||
| 14 | explicit SMAA(u32 width, u32 height); | ||
| 15 | ~SMAA(); | ||
| 16 | |||
| 17 | GLuint Draw(ProgramManager& program_manager, GLuint input_texture); | ||
| 18 | |||
| 19 | private: | ||
| 20 | OGLProgram edge_detection_vert; | ||
| 21 | OGLProgram blending_weight_calculation_vert; | ||
| 22 | OGLProgram neighborhood_blending_vert; | ||
| 23 | OGLProgram edge_detection_frag; | ||
| 24 | OGLProgram blending_weight_calculation_frag; | ||
| 25 | OGLProgram neighborhood_blending_frag; | ||
| 26 | OGLTexture area_tex; | ||
| 27 | OGLTexture search_tex; | ||
| 28 | OGLTexture edges_tex; | ||
| 29 | OGLTexture blend_tex; | ||
| 30 | OGLSampler sampler; | ||
| 31 | OGLFramebuffer framebuffer; | ||
| 32 | OGLTexture texture; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <string> | ||
| 7 | |||
| 8 | #include "common/assert.h" | ||
| 9 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 10 | |||
| 11 | namespace OpenGL { | ||
| 12 | |||
| 13 | static inline void ReplaceInclude(std::string& shader_source, std::string_view include_name, | ||
| 14 | std::string_view include_content) { | ||
| 15 | const std::string include_string = fmt::format("#include \"{}\"", include_name); | ||
| 16 | const std::size_t pos = shader_source.find(include_string); | ||
| 17 | ASSERT(pos != std::string::npos); | ||
| 18 | shader_source.replace(pos, include_string.size(), include_content); | ||
| 19 | }; | ||
| 20 | |||
| 21 | static inline OGLSampler CreateBilinearSampler() { | ||
| 22 | OGLSampler sampler; | ||
| 23 | sampler.Create(); | ||
| 24 | glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 25 | glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
| 26 | glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 27 | glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 28 | glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | ||
| 29 | return sampler; | ||
| 30 | } | ||
| 31 | |||
| 32 | static inline OGLSampler CreateNearestNeighborSampler() { | ||
| 33 | OGLSampler sampler; | ||
| 34 | sampler.Create(); | ||
| 35 | glSamplerParameteri(sampler.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
| 36 | glSamplerParameteri(sampler.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
| 37 | glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 38 | glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 39 | glSamplerParameteri(sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | ||
| 40 | return sampler; | ||
| 41 | } | ||
| 42 | |||
| 43 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/settings.h" | ||
| 5 | #include "video_core/framebuffer_config.h" | ||
| 6 | #include "video_core/host_shaders/opengl_present_vert.h" | ||
| 7 | #include "video_core/renderer_opengl/gl_device.h" | ||
| 8 | #include "video_core/renderer_opengl/gl_shader_manager.h" | ||
| 9 | #include "video_core/renderer_opengl/gl_shader_util.h" | ||
| 10 | #include "video_core/renderer_opengl/present/layer.h" | ||
| 11 | #include "video_core/renderer_opengl/present/present_uniforms.h" | ||
| 12 | #include "video_core/renderer_opengl/present/window_adapt_pass.h" | ||
| 13 | |||
| 14 | namespace OpenGL { | ||
| 15 | |||
| 16 | WindowAdaptPass::WindowAdaptPass(const Device& device_, OGLSampler&& sampler_, | ||
| 17 | std::string_view frag_source) | ||
| 18 | : device(device_), sampler(std::move(sampler_)) { | ||
| 19 | vert = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); | ||
| 20 | frag = CreateProgram(frag_source, GL_FRAGMENT_SHADER); | ||
| 21 | |||
| 22 | // Generate VBO handle for drawing | ||
| 23 | vertex_buffer.Create(); | ||
| 24 | |||
| 25 | // Attach vertex data to VAO | ||
| 26 | glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); | ||
| 27 | |||
| 28 | // Query vertex buffer address when the driver supports unified vertex attributes | ||
| 29 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 30 | glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); | ||
| 31 | glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, | ||
| 32 | &vertex_buffer_address); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | |||
| 36 | WindowAdaptPass::~WindowAdaptPass() = default; | ||
| 37 | |||
| 38 | void WindowAdaptPass::DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, | ||
| 39 | std::span<const Tegra::FramebufferConfig> framebuffers, | ||
| 40 | const Layout::FramebufferLayout& layout) { | ||
| 41 | GLint old_read_fb; | ||
| 42 | GLint old_draw_fb; | ||
| 43 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); | ||
| 44 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); | ||
| 45 | |||
| 46 | const size_t layer_count = framebuffers.size(); | ||
| 47 | std::vector<GLuint> textures(layer_count); | ||
| 48 | std::vector<std::array<GLfloat, 3 * 2>> matrices(layer_count); | ||
| 49 | std::vector<std::array<ScreenRectVertex, 4>> vertices(layer_count); | ||
| 50 | |||
| 51 | auto layer_it = layers.begin(); | ||
| 52 | for (size_t i = 0; i < layer_count; i++) { | ||
| 53 | textures[i] = layer_it->ConfigureDraw(matrices[i], vertices[i], program_manager, | ||
| 54 | framebuffers[i], layout); | ||
| 55 | layer_it++; | ||
| 56 | } | ||
| 57 | |||
| 58 | glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||
| 59 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||
| 60 | |||
| 61 | program_manager.BindPresentPrograms(vert.handle, frag.handle); | ||
| 62 | |||
| 63 | glDisable(GL_FRAMEBUFFER_SRGB); | ||
| 64 | glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), | ||
| 65 | static_cast<GLfloat>(layout.height)); | ||
| 66 | |||
| 67 | glEnableVertexAttribArray(PositionLocation); | ||
| 68 | glEnableVertexAttribArray(TexCoordLocation); | ||
| 69 | glVertexAttribDivisor(PositionLocation, 0); | ||
| 70 | glVertexAttribDivisor(TexCoordLocation, 0); | ||
| 71 | glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, | ||
| 72 | offsetof(ScreenRectVertex, position)); | ||
| 73 | glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, | ||
| 74 | offsetof(ScreenRectVertex, tex_coord)); | ||
| 75 | glVertexAttribBinding(PositionLocation, 0); | ||
| 76 | glVertexAttribBinding(TexCoordLocation, 0); | ||
| 77 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 78 | glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); | ||
| 79 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, | ||
| 80 | sizeof(decltype(vertices)::value_type)); | ||
| 81 | } else { | ||
| 82 | glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); | ||
| 83 | } | ||
| 84 | |||
| 85 | glBindSampler(0, sampler.handle); | ||
| 86 | |||
| 87 | // Update background color before drawing | ||
| 88 | glClearColor(Settings::values.bg_red.GetValue() / 255.0f, | ||
| 89 | Settings::values.bg_green.GetValue() / 255.0f, | ||
| 90 | Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); | ||
| 91 | |||
| 92 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 93 | |||
| 94 | for (size_t i = 0; i < layer_count; i++) { | ||
| 95 | glBindTextureUnit(0, textures[i]); | ||
| 96 | glProgramUniformMatrix3x2fv(vert.handle, ModelViewMatrixLocation, 1, GL_FALSE, | ||
| 97 | matrices[i].data()); | ||
| 98 | glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices[i]), std::data(vertices[i])); | ||
| 99 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <list> | ||
| 7 | #include <span> | ||
| 8 | |||
| 9 | #include "common/math_util.h" | ||
| 10 | #include "video_core/renderer_opengl/gl_resource_manager.h" | ||
| 11 | |||
| 12 | namespace Layout { | ||
| 13 | struct FramebufferLayout; | ||
| 14 | } | ||
| 15 | |||
| 16 | namespace Tegra { | ||
| 17 | struct FramebufferConfig; | ||
| 18 | } | ||
| 19 | |||
| 20 | namespace OpenGL { | ||
| 21 | |||
| 22 | class Device; | ||
| 23 | class Layer; | ||
| 24 | class ProgramManager; | ||
| 25 | |||
| 26 | class WindowAdaptPass final { | ||
| 27 | public: | ||
| 28 | explicit WindowAdaptPass(const Device& device, OGLSampler&& sampler, | ||
| 29 | std::string_view frag_source); | ||
| 30 | ~WindowAdaptPass(); | ||
| 31 | |||
| 32 | void DrawToFramebuffer(ProgramManager& program_manager, std::list<Layer>& layers, | ||
| 33 | std::span<const Tegra::FramebufferConfig> framebuffers, | ||
| 34 | const Layout::FramebufferLayout& layout); | ||
| 35 | |||
| 36 | private: | ||
| 37 | const Device& device; | ||
| 38 | OGLSampler sampler; | ||
| 39 | OGLProgram vert; | ||
| 40 | OGLProgram frag; | ||
| 41 | OGLBuffer vertex_buffer; | ||
| 42 | |||
| 43 | // GPU address of the vertex buffer | ||
| 44 | GLuint64EXT vertex_buffer_address = 0; | ||
| 45 | }; | ||
| 46 | |||
| 47 | } // 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 @@ | |||
| 16 | #include "core/core_timing.h" | 16 | #include "core/core_timing.h" |
| 17 | #include "core/frontend/emu_window.h" | 17 | #include "core/frontend/emu_window.h" |
| 18 | #include "core/telemetry_session.h" | 18 | #include "core/telemetry_session.h" |
| 19 | #include "video_core/host_shaders/ffx_a_h.h" | 19 | #include "video_core/renderer_opengl/gl_blit_screen.h" |
| 20 | #include "video_core/host_shaders/ffx_fsr1_h.h" | ||
| 21 | #include "video_core/host_shaders/full_screen_triangle_vert.h" | ||
| 22 | #include "video_core/host_shaders/fxaa_frag.h" | ||
| 23 | #include "video_core/host_shaders/fxaa_vert.h" | ||
| 24 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_easu_frag.h" | ||
| 25 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_frag.h" | ||
| 26 | #include "video_core/host_shaders/opengl_fidelityfx_fsr_rcas_frag.h" | ||
| 27 | #include "video_core/host_shaders/opengl_present_frag.h" | ||
| 28 | #include "video_core/host_shaders/opengl_present_scaleforce_frag.h" | ||
| 29 | #include "video_core/host_shaders/opengl_present_vert.h" | ||
| 30 | #include "video_core/host_shaders/opengl_smaa_glsl.h" | ||
| 31 | #include "video_core/host_shaders/present_bicubic_frag.h" | ||
| 32 | #include "video_core/host_shaders/present_gaussian_frag.h" | ||
| 33 | #include "video_core/host_shaders/smaa_blending_weight_calculation_frag.h" | ||
| 34 | #include "video_core/host_shaders/smaa_blending_weight_calculation_vert.h" | ||
| 35 | #include "video_core/host_shaders/smaa_edge_detection_frag.h" | ||
| 36 | #include "video_core/host_shaders/smaa_edge_detection_vert.h" | ||
| 37 | #include "video_core/host_shaders/smaa_neighborhood_blending_frag.h" | ||
| 38 | #include "video_core/host_shaders/smaa_neighborhood_blending_vert.h" | ||
| 39 | #include "video_core/renderer_opengl/gl_fsr.h" | ||
| 40 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 20 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 41 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 21 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| 42 | #include "video_core/renderer_opengl/gl_shader_util.h" | 22 | #include "video_core/renderer_opengl/gl_shader_util.h" |
| 43 | #include "video_core/renderer_opengl/renderer_opengl.h" | 23 | #include "video_core/renderer_opengl/renderer_opengl.h" |
| 44 | #include "video_core/smaa_area_tex.h" | ||
| 45 | #include "video_core/smaa_search_tex.h" | ||
| 46 | #include "video_core/textures/decoders.h" | 24 | #include "video_core/textures/decoders.h" |
| 47 | 25 | ||
| 48 | namespace OpenGL { | 26 | namespace OpenGL { |
| 49 | namespace { | 27 | namespace { |
| 50 | constexpr GLint PositionLocation = 0; | ||
| 51 | constexpr GLint TexCoordLocation = 1; | ||
| 52 | constexpr GLint ModelViewMatrixLocation = 0; | ||
| 53 | |||
| 54 | struct ScreenRectVertex { | ||
| 55 | constexpr ScreenRectVertex(u32 x, u32 y, GLfloat u, GLfloat v) | ||
| 56 | : position{{static_cast<GLfloat>(x), static_cast<GLfloat>(y)}}, tex_coord{{u, v}} {} | ||
| 57 | |||
| 58 | std::array<GLfloat, 2> position; | ||
| 59 | std::array<GLfloat, 2> tex_coord; | ||
| 60 | }; | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left | ||
| 64 | * corner and (width, height) on the lower-bottom. | ||
| 65 | * | ||
| 66 | * The projection part of the matrix is trivial, hence these operations are represented | ||
| 67 | * by a 3x2 matrix. | ||
| 68 | */ | ||
| 69 | std::array<GLfloat, 3 * 2> MakeOrthographicMatrix(float width, float height) { | ||
| 70 | std::array<GLfloat, 3 * 2> matrix; // Laid out in column-major order | ||
| 71 | |||
| 72 | // clang-format off | ||
| 73 | matrix[0] = 2.f / width; matrix[2] = 0.f; matrix[4] = -1.f; | ||
| 74 | matrix[1] = 0.f; matrix[3] = -2.f / height; matrix[5] = 1.f; | ||
| 75 | // Last matrix row is implicitly assumed to be [0, 0, 1]. | ||
| 76 | // clang-format on | ||
| 77 | |||
| 78 | return matrix; | ||
| 79 | } | ||
| 80 | |||
| 81 | const char* GetSource(GLenum source) { | 28 | const char* GetSource(GLenum source) { |
| 82 | switch (source) { | 29 | switch (source) { |
| 83 | case GL_DEBUG_SOURCE_API: | 30 | case GL_DEBUG_SOURCE_API: |
| @@ -148,15 +95,13 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | |||
| 148 | : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, | 95 | : RendererBase{emu_window_, std::move(context_)}, telemetry_session{telemetry_session_}, |
| 149 | emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_}, | 96 | emu_window{emu_window_}, device_memory{device_memory_}, gpu{gpu_}, device{emu_window_}, |
| 150 | state_tracker{}, program_manager{device}, | 97 | state_tracker{}, program_manager{device}, |
| 151 | rasterizer(emu_window, gpu, device_memory, device, screen_info, program_manager, | 98 | rasterizer(emu_window, gpu, device_memory, device, program_manager, state_tracker) { |
| 152 | state_tracker) { | ||
| 153 | if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { | 99 | if (Settings::values.renderer_debug && GLAD_GL_KHR_debug) { |
| 154 | glEnable(GL_DEBUG_OUTPUT); | 100 | glEnable(GL_DEBUG_OUTPUT); |
| 155 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); | 101 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); |
| 156 | glDebugMessageCallback(DebugHandler, nullptr); | 102 | glDebugMessageCallback(DebugHandler, nullptr); |
| 157 | } | 103 | } |
| 158 | AddTelemetryFields(); | 104 | AddTelemetryFields(); |
| 159 | InitOpenGLObjects(); | ||
| 160 | 105 | ||
| 161 | // Initialize default attributes to match hardware's disabled attributes | 106 | // Initialize default attributes to match hardware's disabled attributes |
| 162 | GLint max_attribs{}; | 107 | GLint max_attribs{}; |
| @@ -168,27 +113,27 @@ RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_, | |||
| 168 | if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { | 113 | if (!GLAD_GL_ARB_seamless_cubemap_per_texture && !GLAD_GL_AMD_seamless_cubemap_per_texture) { |
| 169 | glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); | 114 | glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); |
| 170 | } | 115 | } |
| 171 | // Enable unified vertex attributes and query vertex buffer address when the driver supports it | 116 | |
| 117 | // Enable unified vertex attributes when the driver supports it | ||
| 172 | if (device.HasVertexBufferUnifiedMemory()) { | 118 | if (device.HasVertexBufferUnifiedMemory()) { |
| 173 | glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); | 119 | glEnableClientState(GL_VERTEX_ATTRIB_ARRAY_UNIFIED_NV); |
| 174 | glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); | 120 | glEnableClientState(GL_ELEMENT_ARRAY_UNIFIED_NV); |
| 175 | glMakeNamedBufferResidentNV(vertex_buffer.handle, GL_READ_ONLY); | ||
| 176 | glGetNamedBufferParameterui64vNV(vertex_buffer.handle, GL_BUFFER_GPU_ADDRESS_NV, | ||
| 177 | &vertex_buffer_address); | ||
| 178 | } | 121 | } |
| 122 | blit_screen = std::make_unique<BlitScreen>(rasterizer, device_memory, state_tracker, | ||
| 123 | program_manager, device); | ||
| 179 | } | 124 | } |
| 180 | 125 | ||
| 181 | RendererOpenGL::~RendererOpenGL() = default; | 126 | RendererOpenGL::~RendererOpenGL() = default; |
| 182 | 127 | ||
| 183 | void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 128 | void RendererOpenGL::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 184 | if (!framebuffer) { | 129 | if (framebuffers.empty()) { |
| 185 | return; | 130 | return; |
| 186 | } | 131 | } |
| 187 | PrepareRendertarget(framebuffer); | 132 | |
| 188 | RenderScreenshot(); | 133 | RenderScreenshot(framebuffers); |
| 189 | 134 | ||
| 190 | state_tracker.BindFramebuffer(0); | 135 | state_tracker.BindFramebuffer(0); |
| 191 | DrawScreen(emu_window.GetFramebufferLayout()); | 136 | blit_screen->DrawScreen(framebuffers, emu_window.GetFramebufferLayout()); |
| 192 | 137 | ||
| 193 | ++m_current_frame; | 138 | ++m_current_frame; |
| 194 | 139 | ||
| @@ -199,172 +144,6 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | |||
| 199 | render_window.OnFrameDisplayed(); | 144 | render_window.OnFrameDisplayed(); |
| 200 | } | 145 | } |
| 201 | 146 | ||
| 202 | void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) { | ||
| 203 | if (!framebuffer) { | ||
| 204 | return; | ||
| 205 | } | ||
| 206 | // If framebuffer is provided, reload it from memory to a texture | ||
| 207 | if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) || | ||
| 208 | screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) || | ||
| 209 | screen_info.texture.pixel_format != framebuffer->pixel_format || | ||
| 210 | gl_framebuffer_data.empty()) { | ||
| 211 | // Reallocate texture if the framebuffer size has changed. | ||
| 212 | // This is expected to not happen very often and hence should not be a | ||
| 213 | // performance problem. | ||
| 214 | ConfigureFramebufferTexture(screen_info.texture, *framebuffer); | ||
| 215 | } | ||
| 216 | |||
| 217 | // Load the framebuffer from memory, draw it to the screen, and swap buffers | ||
| 218 | LoadFBToScreenInfo(*framebuffer); | ||
| 219 | } | ||
| 220 | |||
| 221 | void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) { | ||
| 222 | // Framebuffer orientation handling | ||
| 223 | framebuffer_transform_flags = framebuffer.transform_flags; | ||
| 224 | framebuffer_crop_rect = framebuffer.crop_rect; | ||
| 225 | framebuffer_width = framebuffer.width; | ||
| 226 | framebuffer_height = framebuffer.height; | ||
| 227 | |||
| 228 | const VAddr framebuffer_addr{framebuffer.address + framebuffer.offset}; | ||
| 229 | screen_info.was_accelerated = | ||
| 230 | rasterizer.AccelerateDisplay(framebuffer, framebuffer_addr, framebuffer.stride); | ||
| 231 | if (screen_info.was_accelerated) { | ||
| 232 | return; | ||
| 233 | } | ||
| 234 | |||
| 235 | // Reset the screen info's display texture to its own permanent texture | ||
| 236 | screen_info.display_texture = screen_info.texture.resource.handle; | ||
| 237 | |||
| 238 | // TODO(Rodrigo): Read this from HLE | ||
| 239 | constexpr u32 block_height_log2 = 4; | ||
| 240 | const auto pixel_format{ | ||
| 241 | VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||
| 242 | const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||
| 243 | const u64 size_in_bytes{Tegra::Texture::CalculateSize( | ||
| 244 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||
| 245 | const u8* const host_ptr{device_memory.GetPointer<u8>(framebuffer_addr)}; | ||
| 246 | const std::span<const u8> input_data(host_ptr, size_in_bytes); | ||
| 247 | Tegra::Texture::UnswizzleTexture(gl_framebuffer_data, input_data, bytes_per_pixel, | ||
| 248 | framebuffer.width, framebuffer.height, 1, block_height_log2, | ||
| 249 | 0); | ||
| 250 | |||
| 251 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); | ||
| 252 | glPixelStorei(GL_UNPACK_ROW_LENGTH, static_cast<GLint>(framebuffer.stride)); | ||
| 253 | |||
| 254 | // Update existing texture | ||
| 255 | // TODO: Test what happens on hardware when you change the framebuffer dimensions so that | ||
| 256 | // they differ from the LCD resolution. | ||
| 257 | // TODO: Applications could theoretically crash yuzu here by specifying too large | ||
| 258 | // framebuffer sizes. We should make sure that this cannot happen. | ||
| 259 | glTextureSubImage2D(screen_info.texture.resource.handle, 0, 0, 0, framebuffer.width, | ||
| 260 | framebuffer.height, screen_info.texture.gl_format, | ||
| 261 | screen_info.texture.gl_type, gl_framebuffer_data.data()); | ||
| 262 | |||
| 263 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||
| 264 | } | ||
| 265 | |||
| 266 | void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | ||
| 267 | const TextureInfo& texture) { | ||
| 268 | const u8 framebuffer_data[4] = {color_a, color_b, color_g, color_r}; | ||
| 269 | glClearTexImage(texture.resource.handle, 0, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer_data); | ||
| 270 | } | ||
| 271 | |||
| 272 | void RendererOpenGL::InitOpenGLObjects() { | ||
| 273 | // Create shader programs | ||
| 274 | fxaa_vertex = CreateProgram(HostShaders::FXAA_VERT, GL_VERTEX_SHADER); | ||
| 275 | fxaa_fragment = CreateProgram(HostShaders::FXAA_FRAG, GL_FRAGMENT_SHADER); | ||
| 276 | |||
| 277 | const auto replace_include = [](std::string& shader_source, std::string_view include_name, | ||
| 278 | std::string_view include_content) { | ||
| 279 | const std::string include_string = fmt::format("#include \"{}\"", include_name); | ||
| 280 | const std::size_t pos = shader_source.find(include_string); | ||
| 281 | ASSERT(pos != std::string::npos); | ||
| 282 | shader_source.replace(pos, include_string.size(), include_content); | ||
| 283 | }; | ||
| 284 | |||
| 285 | const auto SmaaShader = [&](std::string_view specialized_source, GLenum stage) { | ||
| 286 | std::string shader_source{specialized_source}; | ||
| 287 | replace_include(shader_source, "opengl_smaa.glsl", HostShaders::OPENGL_SMAA_GLSL); | ||
| 288 | return CreateProgram(shader_source, stage); | ||
| 289 | }; | ||
| 290 | |||
| 291 | smaa_edge_detection_vert = SmaaShader(HostShaders::SMAA_EDGE_DETECTION_VERT, GL_VERTEX_SHADER); | ||
| 292 | smaa_edge_detection_frag = | ||
| 293 | SmaaShader(HostShaders::SMAA_EDGE_DETECTION_FRAG, GL_FRAGMENT_SHADER); | ||
| 294 | smaa_blending_weight_calculation_vert = | ||
| 295 | SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_VERT, GL_VERTEX_SHADER); | ||
| 296 | smaa_blending_weight_calculation_frag = | ||
| 297 | SmaaShader(HostShaders::SMAA_BLENDING_WEIGHT_CALCULATION_FRAG, GL_FRAGMENT_SHADER); | ||
| 298 | smaa_neighborhood_blending_vert = | ||
| 299 | SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_VERT, GL_VERTEX_SHADER); | ||
| 300 | smaa_neighborhood_blending_frag = | ||
| 301 | SmaaShader(HostShaders::SMAA_NEIGHBORHOOD_BLENDING_FRAG, GL_FRAGMENT_SHADER); | ||
| 302 | |||
| 303 | present_vertex = CreateProgram(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER); | ||
| 304 | present_bilinear_fragment = CreateProgram(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER); | ||
| 305 | present_bicubic_fragment = CreateProgram(HostShaders::PRESENT_BICUBIC_FRAG, GL_FRAGMENT_SHADER); | ||
| 306 | present_gaussian_fragment = | ||
| 307 | CreateProgram(HostShaders::PRESENT_GAUSSIAN_FRAG, GL_FRAGMENT_SHADER); | ||
| 308 | present_scaleforce_fragment = | ||
| 309 | CreateProgram(fmt::format("#version 460\n{}", HostShaders::OPENGL_PRESENT_SCALEFORCE_FRAG), | ||
| 310 | GL_FRAGMENT_SHADER); | ||
| 311 | |||
| 312 | std::string fsr_source{HostShaders::OPENGL_FIDELITYFX_FSR_FRAG}; | ||
| 313 | replace_include(fsr_source, "ffx_a.h", HostShaders::FFX_A_H); | ||
| 314 | replace_include(fsr_source, "ffx_fsr1.h", HostShaders::FFX_FSR1_H); | ||
| 315 | |||
| 316 | std::string fsr_easu_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_EASU_FRAG}; | ||
| 317 | std::string fsr_rcas_frag_source{HostShaders::OPENGL_FIDELITYFX_FSR_RCAS_FRAG}; | ||
| 318 | replace_include(fsr_easu_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||
| 319 | replace_include(fsr_rcas_frag_source, "opengl_fidelityfx_fsr.frag", fsr_source); | ||
| 320 | |||
| 321 | fsr = std::make_unique<FSR>(HostShaders::FULL_SCREEN_TRIANGLE_VERT, fsr_easu_frag_source, | ||
| 322 | fsr_rcas_frag_source); | ||
| 323 | |||
| 324 | // Generate presentation sampler | ||
| 325 | present_sampler.Create(); | ||
| 326 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||
| 327 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||
| 328 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 329 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 330 | glSamplerParameteri(present_sampler.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | ||
| 331 | |||
| 332 | present_sampler_nn.Create(); | ||
| 333 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | ||
| 334 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | ||
| 335 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); | ||
| 336 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); | ||
| 337 | glSamplerParameteri(present_sampler_nn.handle, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); | ||
| 338 | |||
| 339 | // Generate VBO handle for drawing | ||
| 340 | vertex_buffer.Create(); | ||
| 341 | |||
| 342 | // Attach vertex data to VAO | ||
| 343 | glNamedBufferData(vertex_buffer.handle, sizeof(ScreenRectVertex) * 4, nullptr, GL_STREAM_DRAW); | ||
| 344 | |||
| 345 | // Allocate textures for the screen | ||
| 346 | screen_info.texture.resource.Create(GL_TEXTURE_2D); | ||
| 347 | |||
| 348 | const GLuint texture = screen_info.texture.resource.handle; | ||
| 349 | glTextureStorage2D(texture, 1, GL_RGBA8, 1, 1); | ||
| 350 | |||
| 351 | screen_info.display_texture = screen_info.texture.resource.handle; | ||
| 352 | |||
| 353 | // Clear screen to black | ||
| 354 | LoadColorToActiveGLTexture(0, 0, 0, 0, screen_info.texture); | ||
| 355 | |||
| 356 | aa_framebuffer.Create(); | ||
| 357 | |||
| 358 | smaa_area_tex.Create(GL_TEXTURE_2D); | ||
| 359 | glTextureStorage2D(smaa_area_tex.handle, 1, GL_RG8, AREATEX_WIDTH, AREATEX_HEIGHT); | ||
| 360 | glTextureSubImage2D(smaa_area_tex.handle, 0, 0, 0, AREATEX_WIDTH, AREATEX_HEIGHT, GL_RG, | ||
| 361 | GL_UNSIGNED_BYTE, areaTexBytes); | ||
| 362 | smaa_search_tex.Create(GL_TEXTURE_2D); | ||
| 363 | glTextureStorage2D(smaa_search_tex.handle, 1, GL_R8, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT); | ||
| 364 | glTextureSubImage2D(smaa_search_tex.handle, 0, 0, 0, SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT, GL_RED, | ||
| 365 | GL_UNSIGNED_BYTE, searchTexBytes); | ||
| 366 | } | ||
| 367 | |||
| 368 | void RendererOpenGL::AddTelemetryFields() { | 147 | void RendererOpenGL::AddTelemetryFields() { |
| 369 | const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; | 148 | const char* const gl_version{reinterpret_cast<char const*>(glGetString(GL_VERSION))}; |
| 370 | const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; | 149 | const char* const gpu_vendor{reinterpret_cast<char const*>(glGetString(GL_VENDOR))}; |
| @@ -380,328 +159,7 @@ void RendererOpenGL::AddTelemetryFields() { | |||
| 380 | telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); | 159 | telemetry_session.AddField(user_system, "GPU_OpenGL_Version", std::string(gl_version)); |
| 381 | } | 160 | } |
| 382 | 161 | ||
| 383 | void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture, | 162 | void RendererOpenGL::RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 384 | const Tegra::FramebufferConfig& framebuffer) { | ||
| 385 | texture.width = framebuffer.width; | ||
| 386 | texture.height = framebuffer.height; | ||
| 387 | texture.pixel_format = framebuffer.pixel_format; | ||
| 388 | |||
| 389 | const auto pixel_format{ | ||
| 390 | VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)}; | ||
| 391 | const u32 bytes_per_pixel{VideoCore::Surface::BytesPerBlock(pixel_format)}; | ||
| 392 | gl_framebuffer_data.resize(texture.width * texture.height * bytes_per_pixel); | ||
| 393 | |||
| 394 | GLint internal_format; | ||
| 395 | switch (framebuffer.pixel_format) { | ||
| 396 | case Service::android::PixelFormat::Rgba8888: | ||
| 397 | internal_format = GL_RGBA8; | ||
| 398 | texture.gl_format = GL_RGBA; | ||
| 399 | texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 400 | break; | ||
| 401 | case Service::android::PixelFormat::Rgb565: | ||
| 402 | internal_format = GL_RGB565; | ||
| 403 | texture.gl_format = GL_RGB; | ||
| 404 | texture.gl_type = GL_UNSIGNED_SHORT_5_6_5; | ||
| 405 | break; | ||
| 406 | default: | ||
| 407 | internal_format = GL_RGBA8; | ||
| 408 | texture.gl_format = GL_RGBA; | ||
| 409 | texture.gl_type = GL_UNSIGNED_INT_8_8_8_8_REV; | ||
| 410 | // UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||
| 411 | // static_cast<u32>(framebuffer.pixel_format)); | ||
| 412 | break; | ||
| 413 | } | ||
| 414 | |||
| 415 | texture.resource.Release(); | ||
| 416 | texture.resource.Create(GL_TEXTURE_2D); | ||
| 417 | glTextureStorage2D(texture.resource.handle, 1, internal_format, texture.width, texture.height); | ||
| 418 | aa_texture.Release(); | ||
| 419 | aa_texture.Create(GL_TEXTURE_2D); | ||
| 420 | glTextureStorage2D(aa_texture.handle, 1, GL_RGBA16F, | ||
| 421 | Settings::values.resolution_info.ScaleUp(screen_info.texture.width), | ||
| 422 | Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); | ||
| 423 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, aa_texture.handle, 0); | ||
| 424 | smaa_edges_tex.Release(); | ||
| 425 | smaa_edges_tex.Create(GL_TEXTURE_2D); | ||
| 426 | glTextureStorage2D(smaa_edges_tex.handle, 1, GL_RG16F, | ||
| 427 | Settings::values.resolution_info.ScaleUp(screen_info.texture.width), | ||
| 428 | Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); | ||
| 429 | smaa_blend_tex.Release(); | ||
| 430 | smaa_blend_tex.Create(GL_TEXTURE_2D); | ||
| 431 | glTextureStorage2D(smaa_blend_tex.handle, 1, GL_RGBA16F, | ||
| 432 | Settings::values.resolution_info.ScaleUp(screen_info.texture.width), | ||
| 433 | Settings::values.resolution_info.ScaleUp(screen_info.texture.height)); | ||
| 434 | } | ||
| 435 | |||
| 436 | void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) { | ||
| 437 | // TODO: Signal state tracker about these changes | ||
| 438 | state_tracker.NotifyScreenDrawVertexArray(); | ||
| 439 | state_tracker.NotifyPolygonModes(); | ||
| 440 | state_tracker.NotifyViewport0(); | ||
| 441 | state_tracker.NotifyScissor0(); | ||
| 442 | state_tracker.NotifyColorMask(0); | ||
| 443 | state_tracker.NotifyBlend0(); | ||
| 444 | state_tracker.NotifyFramebuffer(); | ||
| 445 | state_tracker.NotifyFrontFace(); | ||
| 446 | state_tracker.NotifyCullTest(); | ||
| 447 | state_tracker.NotifyDepthTest(); | ||
| 448 | state_tracker.NotifyStencilTest(); | ||
| 449 | state_tracker.NotifyPolygonOffset(); | ||
| 450 | state_tracker.NotifyRasterizeEnable(); | ||
| 451 | state_tracker.NotifyFramebufferSRGB(); | ||
| 452 | state_tracker.NotifyLogicOp(); | ||
| 453 | state_tracker.NotifyClipControl(); | ||
| 454 | state_tracker.NotifyAlphaTest(); | ||
| 455 | |||
| 456 | state_tracker.ClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE); | ||
| 457 | |||
| 458 | glEnable(GL_CULL_FACE); | ||
| 459 | glDisable(GL_COLOR_LOGIC_OP); | ||
| 460 | glDisable(GL_DEPTH_TEST); | ||
| 461 | glDisable(GL_STENCIL_TEST); | ||
| 462 | glDisable(GL_POLYGON_OFFSET_FILL); | ||
| 463 | glDisable(GL_RASTERIZER_DISCARD); | ||
| 464 | glDisable(GL_ALPHA_TEST); | ||
| 465 | glDisablei(GL_BLEND, 0); | ||
| 466 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | ||
| 467 | glCullFace(GL_BACK); | ||
| 468 | glFrontFace(GL_CW); | ||
| 469 | glColorMaski(0, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); | ||
| 470 | glDepthRangeIndexed(0, 0.0, 0.0); | ||
| 471 | |||
| 472 | glBindTextureUnit(0, screen_info.display_texture); | ||
| 473 | |||
| 474 | auto anti_aliasing = Settings::values.anti_aliasing.GetValue(); | ||
| 475 | if (anti_aliasing >= Settings::AntiAliasing::MaxEnum) { | ||
| 476 | LOG_ERROR(Render_OpenGL, "Invalid antialiasing option selected {}", anti_aliasing); | ||
| 477 | anti_aliasing = Settings::AntiAliasing::None; | ||
| 478 | Settings::values.anti_aliasing.SetValue(anti_aliasing); | ||
| 479 | } | ||
| 480 | |||
| 481 | if (anti_aliasing != Settings::AntiAliasing::None) { | ||
| 482 | glEnablei(GL_SCISSOR_TEST, 0); | ||
| 483 | auto viewport_width = screen_info.texture.width; | ||
| 484 | auto scissor_width = framebuffer_crop_rect.GetWidth(); | ||
| 485 | if (scissor_width <= 0) { | ||
| 486 | scissor_width = viewport_width; | ||
| 487 | } | ||
| 488 | auto viewport_height = screen_info.texture.height; | ||
| 489 | auto scissor_height = framebuffer_crop_rect.GetHeight(); | ||
| 490 | if (scissor_height <= 0) { | ||
| 491 | scissor_height = viewport_height; | ||
| 492 | } | ||
| 493 | if (screen_info.was_accelerated) { | ||
| 494 | viewport_width = Settings::values.resolution_info.ScaleUp(viewport_width); | ||
| 495 | scissor_width = Settings::values.resolution_info.ScaleUp(scissor_width); | ||
| 496 | viewport_height = Settings::values.resolution_info.ScaleUp(viewport_height); | ||
| 497 | scissor_height = Settings::values.resolution_info.ScaleUp(scissor_height); | ||
| 498 | } | ||
| 499 | glScissorIndexed(0, 0, 0, scissor_width, scissor_height); | ||
| 500 | glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(viewport_width), | ||
| 501 | static_cast<GLfloat>(viewport_height)); | ||
| 502 | |||
| 503 | glBindSampler(0, present_sampler.handle); | ||
| 504 | GLint old_read_fb; | ||
| 505 | GLint old_draw_fb; | ||
| 506 | glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &old_read_fb); | ||
| 507 | glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &old_draw_fb); | ||
| 508 | |||
| 509 | switch (anti_aliasing) { | ||
| 510 | case Settings::AntiAliasing::Fxaa: { | ||
| 511 | program_manager.BindPresentPrograms(fxaa_vertex.handle, fxaa_fragment.handle); | ||
| 512 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||
| 513 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
| 514 | } break; | ||
| 515 | case Settings::AntiAliasing::Smaa: { | ||
| 516 | glClearColor(0, 0, 0, 0); | ||
| 517 | glFrontFace(GL_CCW); | ||
| 518 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, aa_framebuffer.handle); | ||
| 519 | glBindSampler(1, present_sampler.handle); | ||
| 520 | glBindSampler(2, present_sampler.handle); | ||
| 521 | |||
| 522 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 523 | smaa_edges_tex.handle, 0); | ||
| 524 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 525 | program_manager.BindPresentPrograms(smaa_edge_detection_vert.handle, | ||
| 526 | smaa_edge_detection_frag.handle); | ||
| 527 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 528 | |||
| 529 | glBindTextureUnit(0, smaa_edges_tex.handle); | ||
| 530 | glBindTextureUnit(1, smaa_area_tex.handle); | ||
| 531 | glBindTextureUnit(2, smaa_search_tex.handle); | ||
| 532 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 533 | smaa_blend_tex.handle, 0); | ||
| 534 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 535 | program_manager.BindPresentPrograms(smaa_blending_weight_calculation_vert.handle, | ||
| 536 | smaa_blending_weight_calculation_frag.handle); | ||
| 537 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 538 | |||
| 539 | glBindTextureUnit(0, screen_info.display_texture); | ||
| 540 | glBindTextureUnit(1, smaa_blend_tex.handle); | ||
| 541 | glNamedFramebufferTexture(aa_framebuffer.handle, GL_COLOR_ATTACHMENT0, | ||
| 542 | aa_texture.handle, 0); | ||
| 543 | program_manager.BindPresentPrograms(smaa_neighborhood_blending_vert.handle, | ||
| 544 | smaa_neighborhood_blending_frag.handle); | ||
| 545 | glDrawArrays(GL_TRIANGLES, 0, 3); | ||
| 546 | glFrontFace(GL_CW); | ||
| 547 | } break; | ||
| 548 | default: | ||
| 549 | UNREACHABLE(); | ||
| 550 | } | ||
| 551 | |||
| 552 | glBindFramebuffer(GL_READ_FRAMEBUFFER, old_read_fb); | ||
| 553 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, old_draw_fb); | ||
| 554 | |||
| 555 | glBindTextureUnit(0, aa_texture.handle); | ||
| 556 | } | ||
| 557 | glDisablei(GL_SCISSOR_TEST, 0); | ||
| 558 | |||
| 559 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 560 | if (!fsr->AreBuffersInitialized()) { | ||
| 561 | fsr->InitBuffers(); | ||
| 562 | } | ||
| 563 | |||
| 564 | auto crop_rect = framebuffer_crop_rect; | ||
| 565 | if (crop_rect.GetWidth() == 0) { | ||
| 566 | crop_rect.right = framebuffer_width; | ||
| 567 | } | ||
| 568 | if (crop_rect.GetHeight() == 0) { | ||
| 569 | crop_rect.bottom = framebuffer_height; | ||
| 570 | } | ||
| 571 | crop_rect = crop_rect.Scale(Settings::values.resolution_info.up_factor); | ||
| 572 | const auto fsr_input_width = Settings::values.resolution_info.ScaleUp(framebuffer_width); | ||
| 573 | const auto fsr_input_height = Settings::values.resolution_info.ScaleUp(framebuffer_height); | ||
| 574 | glBindSampler(0, present_sampler.handle); | ||
| 575 | fsr->Draw(program_manager, layout.screen, fsr_input_width, fsr_input_height, crop_rect); | ||
| 576 | } else { | ||
| 577 | if (fsr->AreBuffersInitialized()) { | ||
| 578 | fsr->ReleaseBuffers(); | ||
| 579 | } | ||
| 580 | } | ||
| 581 | |||
| 582 | const std::array ortho_matrix = | ||
| 583 | MakeOrthographicMatrix(static_cast<float>(layout.width), static_cast<float>(layout.height)); | ||
| 584 | |||
| 585 | const auto fragment_handle = [this]() { | ||
| 586 | switch (Settings::values.scaling_filter.GetValue()) { | ||
| 587 | case Settings::ScalingFilter::NearestNeighbor: | ||
| 588 | case Settings::ScalingFilter::Bilinear: | ||
| 589 | return present_bilinear_fragment.handle; | ||
| 590 | case Settings::ScalingFilter::Bicubic: | ||
| 591 | return present_bicubic_fragment.handle; | ||
| 592 | case Settings::ScalingFilter::Gaussian: | ||
| 593 | return present_gaussian_fragment.handle; | ||
| 594 | case Settings::ScalingFilter::ScaleForce: | ||
| 595 | return present_scaleforce_fragment.handle; | ||
| 596 | case Settings::ScalingFilter::Fsr: | ||
| 597 | return fsr->GetPresentFragmentProgram().handle; | ||
| 598 | default: | ||
| 599 | return present_bilinear_fragment.handle; | ||
| 600 | } | ||
| 601 | }(); | ||
| 602 | program_manager.BindPresentPrograms(present_vertex.handle, fragment_handle); | ||
| 603 | glProgramUniformMatrix3x2fv(present_vertex.handle, ModelViewMatrixLocation, 1, GL_FALSE, | ||
| 604 | ortho_matrix.data()); | ||
| 605 | |||
| 606 | const auto& texcoords = screen_info.display_texcoords; | ||
| 607 | auto left = texcoords.left; | ||
| 608 | auto right = texcoords.right; | ||
| 609 | if (framebuffer_transform_flags != Service::android::BufferTransformFlags::Unset) { | ||
| 610 | if (framebuffer_transform_flags == Service::android::BufferTransformFlags::FlipV) { | ||
| 611 | // Flip the framebuffer vertically | ||
| 612 | left = texcoords.right; | ||
| 613 | right = texcoords.left; | ||
| 614 | } else { | ||
| 615 | // Other transformations are unsupported | ||
| 616 | LOG_CRITICAL(Render_OpenGL, "Unsupported framebuffer_transform_flags={}", | ||
| 617 | framebuffer_transform_flags); | ||
| 618 | UNIMPLEMENTED(); | ||
| 619 | } | ||
| 620 | } | ||
| 621 | |||
| 622 | ASSERT_MSG(framebuffer_crop_rect.left == 0, "Unimplemented"); | ||
| 623 | |||
| 624 | f32 left_start{}; | ||
| 625 | if (framebuffer_crop_rect.Top() > 0) { | ||
| 626 | left_start = static_cast<f32>(framebuffer_crop_rect.Top()) / | ||
| 627 | static_cast<f32>(framebuffer_crop_rect.Bottom()); | ||
| 628 | } | ||
| 629 | f32 scale_u = static_cast<f32>(framebuffer_width) / static_cast<f32>(screen_info.texture.width); | ||
| 630 | f32 scale_v = | ||
| 631 | static_cast<f32>(framebuffer_height) / static_cast<f32>(screen_info.texture.height); | ||
| 632 | |||
| 633 | if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::Fsr) { | ||
| 634 | // Scale the output by the crop width/height. This is commonly used with 1280x720 rendering | ||
| 635 | // (e.g. handheld mode) on a 1920x1080 framebuffer. | ||
| 636 | if (framebuffer_crop_rect.GetWidth() > 0) { | ||
| 637 | scale_u = static_cast<f32>(framebuffer_crop_rect.GetWidth()) / | ||
| 638 | static_cast<f32>(screen_info.texture.width); | ||
| 639 | } | ||
| 640 | if (framebuffer_crop_rect.GetHeight() > 0) { | ||
| 641 | scale_v = static_cast<f32>(framebuffer_crop_rect.GetHeight()) / | ||
| 642 | static_cast<f32>(screen_info.texture.height); | ||
| 643 | } | ||
| 644 | } | ||
| 645 | if (Settings::values.anti_aliasing.GetValue() == Settings::AntiAliasing::Fxaa && | ||
| 646 | !screen_info.was_accelerated) { | ||
| 647 | scale_u /= Settings::values.resolution_info.up_factor; | ||
| 648 | scale_v /= Settings::values.resolution_info.up_factor; | ||
| 649 | } | ||
| 650 | |||
| 651 | const auto& screen = layout.screen; | ||
| 652 | const std::array vertices = { | ||
| 653 | ScreenRectVertex(screen.left, screen.top, texcoords.top * scale_u, | ||
| 654 | left_start + left * scale_v), | ||
| 655 | ScreenRectVertex(screen.right, screen.top, texcoords.bottom * scale_u, | ||
| 656 | left_start + left * scale_v), | ||
| 657 | ScreenRectVertex(screen.left, screen.bottom, texcoords.top * scale_u, | ||
| 658 | left_start + right * scale_v), | ||
| 659 | ScreenRectVertex(screen.right, screen.bottom, texcoords.bottom * scale_u, | ||
| 660 | left_start + right * scale_v), | ||
| 661 | }; | ||
| 662 | glNamedBufferSubData(vertex_buffer.handle, 0, sizeof(vertices), std::data(vertices)); | ||
| 663 | |||
| 664 | glDisable(GL_FRAMEBUFFER_SRGB); | ||
| 665 | glViewportIndexedf(0, 0.0f, 0.0f, static_cast<GLfloat>(layout.width), | ||
| 666 | static_cast<GLfloat>(layout.height)); | ||
| 667 | |||
| 668 | glEnableVertexAttribArray(PositionLocation); | ||
| 669 | glEnableVertexAttribArray(TexCoordLocation); | ||
| 670 | glVertexAttribDivisor(PositionLocation, 0); | ||
| 671 | glVertexAttribDivisor(TexCoordLocation, 0); | ||
| 672 | glVertexAttribFormat(PositionLocation, 2, GL_FLOAT, GL_FALSE, | ||
| 673 | offsetof(ScreenRectVertex, position)); | ||
| 674 | glVertexAttribFormat(TexCoordLocation, 2, GL_FLOAT, GL_FALSE, | ||
| 675 | offsetof(ScreenRectVertex, tex_coord)); | ||
| 676 | glVertexAttribBinding(PositionLocation, 0); | ||
| 677 | glVertexAttribBinding(TexCoordLocation, 0); | ||
| 678 | if (device.HasVertexBufferUnifiedMemory()) { | ||
| 679 | glBindVertexBuffer(0, 0, 0, sizeof(ScreenRectVertex)); | ||
| 680 | glBufferAddressRangeNV(GL_VERTEX_ATTRIB_ARRAY_ADDRESS_NV, 0, vertex_buffer_address, | ||
| 681 | sizeof(vertices)); | ||
| 682 | } else { | ||
| 683 | glBindVertexBuffer(0, vertex_buffer.handle, 0, sizeof(ScreenRectVertex)); | ||
| 684 | } | ||
| 685 | |||
| 686 | if (Settings::values.scaling_filter.GetValue() != Settings::ScalingFilter::NearestNeighbor) { | ||
| 687 | glBindSampler(0, present_sampler.handle); | ||
| 688 | } else { | ||
| 689 | glBindSampler(0, present_sampler_nn.handle); | ||
| 690 | } | ||
| 691 | |||
| 692 | // Update background color before drawing | ||
| 693 | glClearColor(Settings::values.bg_red.GetValue() / 255.0f, | ||
| 694 | Settings::values.bg_green.GetValue() / 255.0f, | ||
| 695 | Settings::values.bg_blue.GetValue() / 255.0f, 1.0f); | ||
| 696 | |||
| 697 | glClear(GL_COLOR_BUFFER_BIT); | ||
| 698 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); | ||
| 699 | |||
| 700 | // TODO | ||
| 701 | // program_manager.RestoreGuestPipeline(); | ||
| 702 | } | ||
| 703 | |||
| 704 | void RendererOpenGL::RenderScreenshot() { | ||
| 705 | if (!renderer_settings.screenshot_requested) { | 163 | if (!renderer_settings.screenshot_requested) { |
| 706 | return; | 164 | return; |
| 707 | } | 165 | } |
| @@ -723,7 +181,7 @@ void RendererOpenGL::RenderScreenshot() { | |||
| 723 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); | 181 | glRenderbufferStorage(GL_RENDERBUFFER, GL_SRGB8, layout.width, layout.height); |
| 724 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); | 182 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer); |
| 725 | 183 | ||
| 726 | DrawScreen(layout); | 184 | blit_screen->DrawScreen(framebuffers, layout); |
| 727 | 185 | ||
| 728 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); | 186 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); |
| 729 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); | 187 | 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 @@ | |||
| 10 | 10 | ||
| 11 | #include "video_core/renderer_base.h" | 11 | #include "video_core/renderer_base.h" |
| 12 | #include "video_core/renderer_opengl/gl_device.h" | 12 | #include "video_core/renderer_opengl/gl_device.h" |
| 13 | #include "video_core/renderer_opengl/gl_fsr.h" | ||
| 14 | #include "video_core/renderer_opengl/gl_rasterizer.h" | 13 | #include "video_core/renderer_opengl/gl_rasterizer.h" |
| 15 | #include "video_core/renderer_opengl/gl_resource_manager.h" | 14 | #include "video_core/renderer_opengl/gl_resource_manager.h" |
| 16 | #include "video_core/renderer_opengl/gl_shader_manager.h" | 15 | #include "video_core/renderer_opengl/gl_shader_manager.h" |
| @@ -25,37 +24,13 @@ namespace Core::Frontend { | |||
| 25 | class EmuWindow; | 24 | class EmuWindow; |
| 26 | } | 25 | } |
| 27 | 26 | ||
| 28 | namespace Core::Memory { | ||
| 29 | class Memory; | ||
| 30 | } | ||
| 31 | |||
| 32 | namespace Layout { | ||
| 33 | struct FramebufferLayout; | ||
| 34 | } | ||
| 35 | |||
| 36 | namespace Tegra { | 27 | namespace Tegra { |
| 37 | class GPU; | 28 | class GPU; |
| 38 | } | 29 | } |
| 39 | 30 | ||
| 40 | namespace OpenGL { | 31 | namespace OpenGL { |
| 41 | 32 | ||
| 42 | /// Structure used for storing information about the textures for the Switch screen | 33 | class BlitScreen; |
| 43 | struct TextureInfo { | ||
| 44 | OGLTexture resource; | ||
| 45 | GLsizei width; | ||
| 46 | GLsizei height; | ||
| 47 | GLenum gl_format; | ||
| 48 | GLenum gl_type; | ||
| 49 | Service::android::PixelFormat pixel_format; | ||
| 50 | }; | ||
| 51 | |||
| 52 | /// Structure used for storing information about the display target for the Switch screen | ||
| 53 | struct ScreenInfo { | ||
| 54 | GLuint display_texture{}; | ||
| 55 | bool was_accelerated = false; | ||
| 56 | const Common::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f}; | ||
| 57 | TextureInfo texture; | ||
| 58 | }; | ||
| 59 | 34 | ||
| 60 | class RendererOpenGL final : public VideoCore::RendererBase { | 35 | class RendererOpenGL final : public VideoCore::RendererBase { |
| 61 | public: | 36 | public: |
| @@ -65,7 +40,7 @@ public: | |||
| 65 | std::unique_ptr<Core::Frontend::GraphicsContext> context_); | 40 | std::unique_ptr<Core::Frontend::GraphicsContext> context_); |
| 66 | ~RendererOpenGL() override; | 41 | ~RendererOpenGL() override; |
| 67 | 42 | ||
| 68 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 43 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; |
| 69 | 44 | ||
| 70 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 45 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 71 | return &rasterizer; | 46 | return &rasterizer; |
| @@ -76,28 +51,8 @@ public: | |||
| 76 | } | 51 | } |
| 77 | 52 | ||
| 78 | private: | 53 | private: |
| 79 | /// Initializes the OpenGL state and creates persistent objects. | ||
| 80 | void InitOpenGLObjects(); | ||
| 81 | |||
| 82 | void AddTelemetryFields(); | 54 | void AddTelemetryFields(); |
| 83 | 55 | void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); | |
| 84 | void ConfigureFramebufferTexture(TextureInfo& texture, | ||
| 85 | const Tegra::FramebufferConfig& framebuffer); | ||
| 86 | |||
| 87 | /// Draws the emulated screens to the emulator window. | ||
| 88 | void DrawScreen(const Layout::FramebufferLayout& layout); | ||
| 89 | |||
| 90 | void RenderScreenshot(); | ||
| 91 | |||
| 92 | /// Loads framebuffer from emulated memory into the active OpenGL texture. | ||
| 93 | void LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer); | ||
| 94 | |||
| 95 | /// Fills active OpenGL texture with the given RGB color.Since the color is solid, the texture | ||
| 96 | /// can be 1x1 but will stretch across whatever it's rendered on. | ||
| 97 | void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, u8 color_a, | ||
| 98 | const TextureInfo& texture); | ||
| 99 | |||
| 100 | void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer); | ||
| 101 | 56 | ||
| 102 | Core::TelemetrySession& telemetry_session; | 57 | Core::TelemetrySession& telemetry_session; |
| 103 | Core::Frontend::EmuWindow& emu_window; | 58 | Core::Frontend::EmuWindow& emu_window; |
| @@ -108,49 +63,9 @@ private: | |||
| 108 | StateTracker state_tracker; | 63 | StateTracker state_tracker; |
| 109 | ProgramManager program_manager; | 64 | ProgramManager program_manager; |
| 110 | RasterizerOpenGL rasterizer; | 65 | RasterizerOpenGL rasterizer; |
| 111 | |||
| 112 | // OpenGL object IDs | ||
| 113 | OGLSampler present_sampler; | ||
| 114 | OGLSampler present_sampler_nn; | ||
| 115 | OGLBuffer vertex_buffer; | ||
| 116 | OGLProgram fxaa_vertex; | ||
| 117 | OGLProgram fxaa_fragment; | ||
| 118 | OGLProgram present_vertex; | ||
| 119 | OGLProgram present_bilinear_fragment; | ||
| 120 | OGLProgram present_bicubic_fragment; | ||
| 121 | OGLProgram present_gaussian_fragment; | ||
| 122 | OGLProgram present_scaleforce_fragment; | ||
| 123 | OGLFramebuffer screenshot_framebuffer; | 66 | OGLFramebuffer screenshot_framebuffer; |
| 124 | 67 | ||
| 125 | // GPU address of the vertex buffer | 68 | std::unique_ptr<BlitScreen> blit_screen; |
| 126 | GLuint64EXT vertex_buffer_address = 0; | ||
| 127 | |||
| 128 | /// Display information for Switch screen | ||
| 129 | ScreenInfo screen_info; | ||
| 130 | OGLTexture aa_texture; | ||
| 131 | OGLFramebuffer aa_framebuffer; | ||
| 132 | |||
| 133 | OGLProgram smaa_edge_detection_vert; | ||
| 134 | OGLProgram smaa_blending_weight_calculation_vert; | ||
| 135 | OGLProgram smaa_neighborhood_blending_vert; | ||
| 136 | OGLProgram smaa_edge_detection_frag; | ||
| 137 | OGLProgram smaa_blending_weight_calculation_frag; | ||
| 138 | OGLProgram smaa_neighborhood_blending_frag; | ||
| 139 | OGLTexture smaa_area_tex; | ||
| 140 | OGLTexture smaa_search_tex; | ||
| 141 | OGLTexture smaa_edges_tex; | ||
| 142 | OGLTexture smaa_blend_tex; | ||
| 143 | |||
| 144 | std::unique_ptr<FSR> fsr; | ||
| 145 | |||
| 146 | /// OpenGL framebuffer data | ||
| 147 | std::vector<u8> gl_framebuffer_data; | ||
| 148 | |||
| 149 | /// Used for transforming the framebuffer orientation | ||
| 150 | Service::android::BufferTransformFlags framebuffer_transform_flags{}; | ||
| 151 | Common::Rectangle<int> framebuffer_crop_rect; | ||
| 152 | u32 framebuffer_width; | ||
| 153 | u32 framebuffer_height; | ||
| 154 | }; | 69 | }; |
| 155 | 70 | ||
| 156 | } // namespace OpenGL | 71 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 7 | |||
| 8 | namespace Vulkan { | ||
| 9 | |||
| 10 | class Scheduler; | ||
| 11 | |||
| 12 | class AntiAliasPass { | ||
| 13 | public: | ||
| 14 | virtual ~AntiAliasPass() = default; | ||
| 15 | virtual void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, | ||
| 16 | VkImageView* inout_image_view) = 0; | ||
| 17 | }; | ||
| 18 | |||
| 19 | class NoAA final : public AntiAliasPass { | ||
| 20 | public: | ||
| 21 | void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, | ||
| 22 | VkImageView* inout_image_view) override {} | ||
| 23 | }; | ||
| 24 | |||
| 25 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/common_types.h" | ||
| 5 | |||
| 6 | #include "video_core/host_shaders/present_bicubic_frag_spv.h" | ||
| 7 | #include "video_core/host_shaders/present_gaussian_frag_spv.h" | ||
| 8 | #include "video_core/host_shaders/vulkan_present_frag_spv.h" | ||
| 9 | #include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" | ||
| 10 | #include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" | ||
| 11 | #include "video_core/renderer_vulkan/present/filters.h" | ||
| 12 | #include "video_core/renderer_vulkan/present/util.h" | ||
| 13 | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||
| 14 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 15 | |||
| 16 | namespace Vulkan { | ||
| 17 | |||
| 18 | namespace { | ||
| 19 | |||
| 20 | vk::ShaderModule SelectScaleForceShader(const Device& device) { | ||
| 21 | if (device.IsFloat16Supported()) { | ||
| 22 | return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV); | ||
| 23 | } else { | ||
| 24 | return BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | } // Anonymous namespace | ||
| 29 | |||
| 30 | std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format) { | ||
| 31 | return std::make_unique<WindowAdaptPass>(device, frame_format, | ||
| 32 | CreateNearestNeighborSampler(device), | ||
| 33 | BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); | ||
| 34 | } | ||
| 35 | |||
| 36 | std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format) { | ||
| 37 | return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||
| 38 | BuildShader(device, VULKAN_PRESENT_FRAG_SPV)); | ||
| 39 | } | ||
| 40 | |||
| 41 | std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format) { | ||
| 42 | return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||
| 43 | BuildShader(device, PRESENT_BICUBIC_FRAG_SPV)); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format) { | ||
| 47 | return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||
| 48 | BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV)); | ||
| 49 | } | ||
| 50 | |||
| 51 | std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format) { | ||
| 52 | return std::make_unique<WindowAdaptPass>(device, frame_format, CreateBilinearSampler(device), | ||
| 53 | SelectScaleForceShader(device)); | ||
| 54 | } | ||
| 55 | |||
| 56 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "video_core/renderer_vulkan/present/window_adapt_pass.h" | ||
| 7 | |||
| 8 | namespace Vulkan { | ||
| 9 | |||
| 10 | class MemoryAllocator; | ||
| 11 | |||
| 12 | std::unique_ptr<WindowAdaptPass> MakeNearestNeighbor(const Device& device, VkFormat frame_format); | ||
| 13 | std::unique_ptr<WindowAdaptPass> MakeBilinear(const Device& device, VkFormat frame_format); | ||
| 14 | std::unique_ptr<WindowAdaptPass> MakeBicubic(const Device& device, VkFormat frame_format); | ||
| 15 | std::unique_ptr<WindowAdaptPass> MakeGaussian(const Device& device, VkFormat frame_format); | ||
| 16 | std::unique_ptr<WindowAdaptPass> MakeScaleForce(const Device& device, VkFormat frame_format); | ||
| 17 | |||
| 18 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/common_types.h" | ||
| 5 | #include "common/div_ceil.h" | ||
| 6 | #include "common/settings.h" | ||
| 7 | |||
| 8 | #include "video_core/fsr.h" | ||
| 9 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_frag_spv.h" | ||
| 10 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_frag_spv.h" | ||
| 11 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_frag_spv.h" | ||
| 12 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_frag_spv.h" | ||
| 13 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_vert_spv.h" | ||
| 14 | #include "video_core/renderer_vulkan/present/fsr.h" | ||
| 15 | #include "video_core/renderer_vulkan/present/util.h" | ||
| 16 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 17 | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||
| 18 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 19 | |||
| 20 | namespace Vulkan { | ||
| 21 | using namespace FSR; | ||
| 22 | |||
| 23 | using PushConstants = std::array<u32, 4 * 4>; | ||
| 24 | |||
| 25 | FSR::FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, | ||
| 26 | VkExtent2D extent) | ||
| 27 | : m_device{device}, m_memory_allocator{memory_allocator}, | ||
| 28 | m_image_count{image_count}, m_extent{extent} { | ||
| 29 | |||
| 30 | CreateImages(); | ||
| 31 | CreateRenderPasses(); | ||
| 32 | CreateSampler(); | ||
| 33 | CreateShaders(); | ||
| 34 | CreateDescriptorPool(); | ||
| 35 | CreateDescriptorSetLayout(); | ||
| 36 | CreateDescriptorSets(); | ||
| 37 | CreatePipelineLayouts(); | ||
| 38 | CreatePipelines(); | ||
| 39 | } | ||
| 40 | |||
| 41 | void FSR::CreateImages() { | ||
| 42 | m_dynamic_images.resize(m_image_count); | ||
| 43 | for (auto& images : m_dynamic_images) { | ||
| 44 | images.images[Easu] = | ||
| 45 | CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 46 | images.images[Rcas] = | ||
| 47 | CreateWrappedImage(m_memory_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 48 | images.image_views[Easu] = | ||
| 49 | CreateWrappedImageView(m_device, images.images[Easu], VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 50 | images.image_views[Rcas] = | ||
| 51 | CreateWrappedImageView(m_device, images.images[Rcas], VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 55 | void FSR::CreateRenderPasses() { | ||
| 56 | m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 57 | |||
| 58 | for (auto& images : m_dynamic_images) { | ||
| 59 | images.framebuffers[Easu] = | ||
| 60 | CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Easu], m_extent); | ||
| 61 | images.framebuffers[Rcas] = | ||
| 62 | CreateWrappedFramebuffer(m_device, m_renderpass, images.image_views[Rcas], m_extent); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | void FSR::CreateSampler() { | ||
| 67 | m_sampler = CreateBilinearSampler(m_device); | ||
| 68 | } | ||
| 69 | |||
| 70 | void FSR::CreateShaders() { | ||
| 71 | m_vert_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_VERT_SPV); | ||
| 72 | |||
| 73 | if (m_device.IsFloat16Supported()) { | ||
| 74 | m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP16_FRAG_SPV); | ||
| 75 | m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_FRAG_SPV); | ||
| 76 | } else { | ||
| 77 | m_easu_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_EASU_FP32_FRAG_SPV); | ||
| 78 | m_rcas_shader = BuildShader(m_device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_FRAG_SPV); | ||
| 79 | } | ||
| 80 | } | ||
| 81 | |||
| 82 | void FSR::CreateDescriptorPool() { | ||
| 83 | // EASU: 1 descriptor | ||
| 84 | // RCAS: 1 descriptor | ||
| 85 | // 2 descriptors, 2 descriptor sets per invocation | ||
| 86 | m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, 2 * m_image_count); | ||
| 87 | } | ||
| 88 | |||
| 89 | void FSR::CreateDescriptorSetLayout() { | ||
| 90 | m_descriptor_set_layout = | ||
| 91 | CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||
| 92 | } | ||
| 93 | |||
| 94 | void FSR::CreateDescriptorSets() { | ||
| 95 | std::vector<VkDescriptorSetLayout> layouts(MaxFsrStage, *m_descriptor_set_layout); | ||
| 96 | |||
| 97 | for (auto& images : m_dynamic_images) { | ||
| 98 | images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | void FSR::CreatePipelineLayouts() { | ||
| 103 | const VkPushConstantRange range{ | ||
| 104 | .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 105 | .offset = 0, | ||
| 106 | .size = sizeof(PushConstants), | ||
| 107 | }; | ||
| 108 | VkPipelineLayoutCreateInfo ci{ | ||
| 109 | .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||
| 110 | .pNext = nullptr, | ||
| 111 | .flags = 0, | ||
| 112 | .setLayoutCount = 1, | ||
| 113 | .pSetLayouts = m_descriptor_set_layout.address(), | ||
| 114 | .pushConstantRangeCount = 1, | ||
| 115 | .pPushConstantRanges = &range, | ||
| 116 | }; | ||
| 117 | |||
| 118 | m_pipeline_layout = m_device.GetLogical().CreatePipelineLayout(ci); | ||
| 119 | } | ||
| 120 | |||
| 121 | void FSR::CreatePipelines() { | ||
| 122 | m_easu_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, | ||
| 123 | std::tie(m_vert_shader, m_easu_shader)); | ||
| 124 | m_rcas_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, | ||
| 125 | std::tie(m_vert_shader, m_rcas_shader)); | ||
| 126 | } | ||
| 127 | |||
| 128 | void FSR::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { | ||
| 129 | Images& images = m_dynamic_images[image_index]; | ||
| 130 | std::vector<VkDescriptorImageInfo> image_infos; | ||
| 131 | std::vector<VkWriteDescriptorSet> updates; | ||
| 132 | image_infos.reserve(2); | ||
| 133 | |||
| 134 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, | ||
| 135 | images.descriptor_sets[Easu], 0)); | ||
| 136 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Easu], | ||
| 137 | images.descriptor_sets[Rcas], 0)); | ||
| 138 | |||
| 139 | m_device.GetLogical().UpdateDescriptorSets(updates, {}); | ||
| 140 | } | ||
| 141 | |||
| 142 | void FSR::UploadImages(Scheduler& scheduler) { | ||
| 143 | if (m_images_ready) { | ||
| 144 | return; | ||
| 145 | } | ||
| 146 | |||
| 147 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { | ||
| 148 | for (auto& image : m_dynamic_images) { | ||
| 149 | ClearColorImage(cmdbuf, *image.images[Easu]); | ||
| 150 | ClearColorImage(cmdbuf, *image.images[Rcas]); | ||
| 151 | } | ||
| 152 | }); | ||
| 153 | scheduler.Finish(); | ||
| 154 | |||
| 155 | m_images_ready = true; | ||
| 156 | } | ||
| 157 | |||
| 158 | VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, | ||
| 159 | VkImageView source_image_view, VkExtent2D input_image_extent, | ||
| 160 | const Common::Rectangle<f32>& crop_rect) { | ||
| 161 | Images& images = m_dynamic_images[image_index]; | ||
| 162 | |||
| 163 | VkImage easu_image = *images.images[Easu]; | ||
| 164 | VkImage rcas_image = *images.images[Rcas]; | ||
| 165 | VkDescriptorSet easu_descriptor_set = images.descriptor_sets[Easu]; | ||
| 166 | VkDescriptorSet rcas_descriptor_set = images.descriptor_sets[Rcas]; | ||
| 167 | VkFramebuffer easu_framebuffer = *images.framebuffers[Easu]; | ||
| 168 | VkFramebuffer rcas_framebuffer = *images.framebuffers[Rcas]; | ||
| 169 | VkPipeline easu_pipeline = *m_easu_pipeline; | ||
| 170 | VkPipeline rcas_pipeline = *m_rcas_pipeline; | ||
| 171 | VkPipelineLayout pipeline_layout = *m_pipeline_layout; | ||
| 172 | VkRenderPass renderpass = *m_renderpass; | ||
| 173 | VkExtent2D extent = m_extent; | ||
| 174 | |||
| 175 | const f32 input_image_width = static_cast<f32>(input_image_extent.width); | ||
| 176 | const f32 input_image_height = static_cast<f32>(input_image_extent.height); | ||
| 177 | const f32 output_image_width = static_cast<f32>(extent.width); | ||
| 178 | const f32 output_image_height = static_cast<f32>(extent.height); | ||
| 179 | const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; | ||
| 180 | const f32 viewport_x = crop_rect.left * input_image_width; | ||
| 181 | const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; | ||
| 182 | const f32 viewport_y = crop_rect.top * input_image_height; | ||
| 183 | |||
| 184 | PushConstants easu_con{}; | ||
| 185 | PushConstants rcas_con{}; | ||
| 186 | FsrEasuConOffset(easu_con.data() + 0, easu_con.data() + 4, easu_con.data() + 8, | ||
| 187 | easu_con.data() + 12, viewport_width, viewport_height, input_image_width, | ||
| 188 | input_image_height, output_image_width, output_image_height, viewport_x, | ||
| 189 | viewport_y); | ||
| 190 | |||
| 191 | const float sharpening = | ||
| 192 | static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; | ||
| 193 | FsrRcasCon(rcas_con.data(), sharpening); | ||
| 194 | |||
| 195 | UploadImages(scheduler); | ||
| 196 | UpdateDescriptorSets(source_image_view, image_index); | ||
| 197 | |||
| 198 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 199 | scheduler.Record([=](vk::CommandBuffer cmdbuf) { | ||
| 200 | TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 201 | TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 202 | BeginRenderPass(cmdbuf, renderpass, easu_framebuffer, extent); | ||
| 203 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, easu_pipeline); | ||
| 204 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, | ||
| 205 | easu_descriptor_set, {}); | ||
| 206 | cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, easu_con); | ||
| 207 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 208 | cmdbuf.EndRenderPass(); | ||
| 209 | |||
| 210 | TransitionImageLayout(cmdbuf, easu_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 211 | TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 212 | BeginRenderPass(cmdbuf, renderpass, rcas_framebuffer, extent); | ||
| 213 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, rcas_pipeline); | ||
| 214 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, | ||
| 215 | rcas_descriptor_set, {}); | ||
| 216 | cmdbuf.PushConstants(pipeline_layout, VK_SHADER_STAGE_FRAGMENT_BIT, rcas_con); | ||
| 217 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 218 | cmdbuf.EndRenderPass(); | ||
| 219 | |||
| 220 | TransitionImageLayout(cmdbuf, rcas_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 221 | }); | ||
| 222 | |||
| 223 | return *images.image_views[Rcas]; | ||
| 224 | } | ||
| 225 | |||
| 226 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/math_util.h" | ||
| 7 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 8 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 9 | |||
| 10 | namespace Vulkan { | ||
| 11 | |||
| 12 | class Device; | ||
| 13 | class Scheduler; | ||
| 14 | |||
| 15 | class FSR { | ||
| 16 | public: | ||
| 17 | explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, | ||
| 18 | VkExtent2D extent); | ||
| 19 | VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, | ||
| 20 | VkImageView source_image_view, VkExtent2D input_image_extent, | ||
| 21 | const Common::Rectangle<f32>& crop_rect); | ||
| 22 | |||
| 23 | private: | ||
| 24 | void CreateImages(); | ||
| 25 | void CreateRenderPasses(); | ||
| 26 | void CreateSampler(); | ||
| 27 | void CreateShaders(); | ||
| 28 | void CreateDescriptorPool(); | ||
| 29 | void CreateDescriptorSetLayout(); | ||
| 30 | void CreateDescriptorSets(); | ||
| 31 | void CreatePipelineLayouts(); | ||
| 32 | void CreatePipelines(); | ||
| 33 | |||
| 34 | void UploadImages(Scheduler& scheduler); | ||
| 35 | void UpdateDescriptorSets(VkImageView image_view, size_t image_index); | ||
| 36 | |||
| 37 | const Device& m_device; | ||
| 38 | MemoryAllocator& m_memory_allocator; | ||
| 39 | const size_t m_image_count; | ||
| 40 | const VkExtent2D m_extent; | ||
| 41 | |||
| 42 | enum FsrStage { | ||
| 43 | Easu, | ||
| 44 | Rcas, | ||
| 45 | MaxFsrStage, | ||
| 46 | }; | ||
| 47 | |||
| 48 | vk::DescriptorPool m_descriptor_pool; | ||
| 49 | vk::DescriptorSetLayout m_descriptor_set_layout; | ||
| 50 | vk::PipelineLayout m_pipeline_layout; | ||
| 51 | vk::ShaderModule m_vert_shader; | ||
| 52 | vk::ShaderModule m_easu_shader; | ||
| 53 | vk::ShaderModule m_rcas_shader; | ||
| 54 | vk::Pipeline m_easu_pipeline; | ||
| 55 | vk::Pipeline m_rcas_pipeline; | ||
| 56 | vk::RenderPass m_renderpass; | ||
| 57 | vk::Sampler m_sampler; | ||
| 58 | |||
| 59 | struct Images { | ||
| 60 | vk::DescriptorSets descriptor_sets; | ||
| 61 | std::array<vk::Image, MaxFsrStage> images; | ||
| 62 | std::array<vk::ImageView, MaxFsrStage> image_views; | ||
| 63 | std::array<vk::Framebuffer, MaxFsrStage> framebuffers; | ||
| 64 | }; | ||
| 65 | std::vector<Images> m_dynamic_images; | ||
| 66 | bool m_images_ready{}; | ||
| 67 | }; | ||
| 68 | |||
| 69 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/common_types.h" | ||
| 5 | |||
| 6 | #include "video_core/host_shaders/fxaa_frag_spv.h" | ||
| 7 | #include "video_core/host_shaders/fxaa_vert_spv.h" | ||
| 8 | #include "video_core/renderer_vulkan/present/fxaa.h" | ||
| 9 | #include "video_core/renderer_vulkan/present/util.h" | ||
| 10 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 11 | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||
| 12 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 13 | |||
| 14 | namespace Vulkan { | ||
| 15 | |||
| 16 | FXAA::FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) | ||
| 17 | : m_device(device), m_allocator(allocator), m_extent(extent), | ||
| 18 | m_image_count(static_cast<u32>(image_count)) { | ||
| 19 | CreateImages(); | ||
| 20 | CreateRenderPasses(); | ||
| 21 | CreateSampler(); | ||
| 22 | CreateShaders(); | ||
| 23 | CreateDescriptorPool(); | ||
| 24 | CreateDescriptorSetLayouts(); | ||
| 25 | CreateDescriptorSets(); | ||
| 26 | CreatePipelineLayouts(); | ||
| 27 | CreatePipelines(); | ||
| 28 | } | ||
| 29 | |||
| 30 | FXAA::~FXAA() = default; | ||
| 31 | |||
| 32 | void FXAA::CreateImages() { | ||
| 33 | for (u32 i = 0; i < m_image_count; i++) { | ||
| 34 | Image& image = m_dynamic_images.emplace_back(); | ||
| 35 | |||
| 36 | image.image = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 37 | image.image_view = | ||
| 38 | CreateWrappedImageView(m_device, image.image, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 39 | } | ||
| 40 | } | ||
| 41 | |||
| 42 | void FXAA::CreateRenderPasses() { | ||
| 43 | m_renderpass = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 44 | |||
| 45 | for (auto& image : m_dynamic_images) { | ||
| 46 | image.framebuffer = | ||
| 47 | CreateWrappedFramebuffer(m_device, m_renderpass, image.image_view, m_extent); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | |||
| 51 | void FXAA::CreateSampler() { | ||
| 52 | m_sampler = CreateWrappedSampler(m_device); | ||
| 53 | } | ||
| 54 | |||
| 55 | void FXAA::CreateShaders() { | ||
| 56 | m_vertex_shader = CreateWrappedShaderModule(m_device, FXAA_VERT_SPV); | ||
| 57 | m_fragment_shader = CreateWrappedShaderModule(m_device, FXAA_FRAG_SPV); | ||
| 58 | } | ||
| 59 | |||
| 60 | void FXAA::CreateDescriptorPool() { | ||
| 61 | // 2 descriptors, 1 descriptor set per image | ||
| 62 | m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 2 * m_image_count, m_image_count); | ||
| 63 | } | ||
| 64 | |||
| 65 | void FXAA::CreateDescriptorSetLayouts() { | ||
| 66 | m_descriptor_set_layout = | ||
| 67 | CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 68 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||
| 69 | } | ||
| 70 | |||
| 71 | void FXAA::CreateDescriptorSets() { | ||
| 72 | VkDescriptorSetLayout layout = *m_descriptor_set_layout; | ||
| 73 | |||
| 74 | for (auto& images : m_dynamic_images) { | ||
| 75 | images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, {layout}); | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | void FXAA::CreatePipelineLayouts() { | ||
| 80 | m_pipeline_layout = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layout); | ||
| 81 | } | ||
| 82 | |||
| 83 | void FXAA::CreatePipelines() { | ||
| 84 | m_pipeline = CreateWrappedPipeline(m_device, m_renderpass, m_pipeline_layout, | ||
| 85 | std::tie(m_vertex_shader, m_fragment_shader)); | ||
| 86 | } | ||
| 87 | |||
| 88 | void FXAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { | ||
| 89 | Image& image = m_dynamic_images[image_index]; | ||
| 90 | std::vector<VkDescriptorImageInfo> image_infos; | ||
| 91 | std::vector<VkWriteDescriptorSet> updates; | ||
| 92 | image_infos.reserve(2); | ||
| 93 | |||
| 94 | updates.push_back( | ||
| 95 | CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 0)); | ||
| 96 | updates.push_back( | ||
| 97 | CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, image.descriptor_sets[0], 1)); | ||
| 98 | |||
| 99 | m_device.GetLogical().UpdateDescriptorSets(updates, {}); | ||
| 100 | } | ||
| 101 | |||
| 102 | void FXAA::UploadImages(Scheduler& scheduler) { | ||
| 103 | if (m_images_ready) { | ||
| 104 | return; | ||
| 105 | } | ||
| 106 | |||
| 107 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { | ||
| 108 | for (auto& image : m_dynamic_images) { | ||
| 109 | ClearColorImage(cmdbuf, *image.image); | ||
| 110 | } | ||
| 111 | }); | ||
| 112 | scheduler.Finish(); | ||
| 113 | |||
| 114 | m_images_ready = true; | ||
| 115 | } | ||
| 116 | |||
| 117 | void FXAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, | ||
| 118 | VkImageView* inout_image_view) { | ||
| 119 | const Image& image{m_dynamic_images[image_index]}; | ||
| 120 | const VkImage input_image{*inout_image}; | ||
| 121 | const VkImage output_image{*image.image}; | ||
| 122 | const VkDescriptorSet descriptor_set{image.descriptor_sets[0]}; | ||
| 123 | const VkFramebuffer framebuffer{*image.framebuffer}; | ||
| 124 | const VkRenderPass renderpass{*m_renderpass}; | ||
| 125 | const VkPipeline pipeline{*m_pipeline}; | ||
| 126 | const VkPipelineLayout layout{*m_pipeline_layout}; | ||
| 127 | const VkExtent2D extent{m_extent}; | ||
| 128 | |||
| 129 | UploadImages(scheduler); | ||
| 130 | UpdateDescriptorSets(*inout_image_view, image_index); | ||
| 131 | |||
| 132 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 133 | scheduler.Record([=](vk::CommandBuffer cmdbuf) { | ||
| 134 | TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 135 | TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 136 | BeginRenderPass(cmdbuf, renderpass, framebuffer, extent); | ||
| 137 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); | ||
| 138 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptor_set, {}); | ||
| 139 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 140 | cmdbuf.EndRenderPass(); | ||
| 141 | TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 142 | }); | ||
| 143 | |||
| 144 | *inout_image = *image.image; | ||
| 145 | *inout_image_view = *image.image_view; | ||
| 146 | } | ||
| 147 | |||
| 148 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "video_core/renderer_vulkan/present/anti_alias_pass.h" | ||
| 7 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 8 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 9 | |||
| 10 | namespace Vulkan { | ||
| 11 | |||
| 12 | class Device; | ||
| 13 | class Scheduler; | ||
| 14 | class StagingBufferPool; | ||
| 15 | |||
| 16 | class FXAA final : public AntiAliasPass { | ||
| 17 | public: | ||
| 18 | explicit FXAA(const Device& device, MemoryAllocator& allocator, size_t image_count, | ||
| 19 | VkExtent2D extent); | ||
| 20 | ~FXAA() override; | ||
| 21 | |||
| 22 | void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, | ||
| 23 | VkImageView* inout_image_view) override; | ||
| 24 | |||
| 25 | private: | ||
| 26 | void CreateImages(); | ||
| 27 | void CreateRenderPasses(); | ||
| 28 | void CreateSampler(); | ||
| 29 | void CreateShaders(); | ||
| 30 | void CreateDescriptorPool(); | ||
| 31 | void CreateDescriptorSetLayouts(); | ||
| 32 | void CreateDescriptorSets(); | ||
| 33 | void CreatePipelineLayouts(); | ||
| 34 | void CreatePipelines(); | ||
| 35 | void UpdateDescriptorSets(VkImageView image_view, size_t image_index); | ||
| 36 | void UploadImages(Scheduler& scheduler); | ||
| 37 | |||
| 38 | const Device& m_device; | ||
| 39 | MemoryAllocator& m_allocator; | ||
| 40 | const VkExtent2D m_extent; | ||
| 41 | const u32 m_image_count; | ||
| 42 | |||
| 43 | vk::ShaderModule m_vertex_shader{}; | ||
| 44 | vk::ShaderModule m_fragment_shader{}; | ||
| 45 | vk::DescriptorPool m_descriptor_pool{}; | ||
| 46 | vk::DescriptorSetLayout m_descriptor_set_layout{}; | ||
| 47 | vk::PipelineLayout m_pipeline_layout{}; | ||
| 48 | vk::Pipeline m_pipeline{}; | ||
| 49 | vk::RenderPass m_renderpass{}; | ||
| 50 | |||
| 51 | struct Image { | ||
| 52 | vk::DescriptorSets descriptor_sets{}; | ||
| 53 | vk::Framebuffer framebuffer{}; | ||
| 54 | vk::Image image{}; | ||
| 55 | vk::ImageView image_view{}; | ||
| 56 | }; | ||
| 57 | std::vector<Image> m_dynamic_images{}; | ||
| 58 | bool m_images_ready{}; | ||
| 59 | |||
| 60 | vk::Sampler m_sampler{}; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | ||
| 5 | |||
| 6 | #include "common/settings.h" | ||
| 7 | #include "video_core/framebuffer_config.h" | ||
| 8 | #include "video_core/renderer_vulkan/present/fsr.h" | ||
| 9 | #include "video_core/renderer_vulkan/present/fxaa.h" | ||
| 10 | #include "video_core/renderer_vulkan/present/layer.h" | ||
| 11 | #include "video_core/renderer_vulkan/present/present_push_constants.h" | ||
| 12 | #include "video_core/renderer_vulkan/present/smaa.h" | ||
| 13 | #include "video_core/renderer_vulkan/present/util.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | ||
| 15 | #include "video_core/textures/decoders.h" | ||
| 16 | |||
| 17 | namespace Vulkan { | ||
| 18 | |||
| 19 | namespace { | ||
| 20 | |||
| 21 | u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { | ||
| 22 | using namespace VideoCore::Surface; | ||
| 23 | return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); | ||
| 24 | } | ||
| 25 | |||
| 26 | std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { | ||
| 27 | return static_cast<std::size_t>(framebuffer.stride) * | ||
| 28 | static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); | ||
| 29 | } | ||
| 30 | |||
| 31 | VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | ||
| 32 | switch (framebuffer.pixel_format) { | ||
| 33 | case Service::android::PixelFormat::Rgba8888: | ||
| 34 | case Service::android::PixelFormat::Rgbx8888: | ||
| 35 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||
| 36 | case Service::android::PixelFormat::Rgb565: | ||
| 37 | return VK_FORMAT_R5G6B5_UNORM_PACK16; | ||
| 38 | case Service::android::PixelFormat::Bgra8888: | ||
| 39 | return VK_FORMAT_B8G8R8A8_UNORM; | ||
| 40 | default: | ||
| 41 | UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | ||
| 42 | static_cast<u32>(framebuffer.pixel_format)); | ||
| 43 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||
| 44 | } | ||
| 45 | } | ||
| 46 | |||
| 47 | } // Anonymous namespace | ||
| 48 | |||
| 49 | Layer::Layer(const Device& device_, MemoryAllocator& memory_allocator_, Scheduler& scheduler_, | ||
| 50 | Tegra::MaxwellDeviceMemoryManager& device_memory_, size_t image_count_, | ||
| 51 | VkExtent2D output_size, VkDescriptorSetLayout layout) | ||
| 52 | : device(device_), memory_allocator(memory_allocator_), scheduler(scheduler_), | ||
| 53 | device_memory(device_memory_), image_count(image_count_) { | ||
| 54 | CreateDescriptorPool(); | ||
| 55 | CreateDescriptorSets(layout); | ||
| 56 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 57 | CreateFSR(output_size); | ||
| 58 | } | ||
| 59 | } | ||
| 60 | |||
| 61 | Layer::~Layer() { | ||
| 62 | ReleaseRawImages(); | ||
| 63 | } | ||
| 64 | |||
| 65 | void Layer::ConfigureDraw(PresentPushConstants* out_push_constants, | ||
| 66 | VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, | ||
| 67 | VkSampler sampler, size_t image_index, | ||
| 68 | const Tegra::FramebufferConfig& framebuffer, | ||
| 69 | const Layout::FramebufferLayout& layout) { | ||
| 70 | const auto texture_info = rasterizer.AccelerateDisplay( | ||
| 71 | framebuffer, framebuffer.address + framebuffer.offset, framebuffer.stride); | ||
| 72 | const u32 texture_width = texture_info ? texture_info->width : framebuffer.width; | ||
| 73 | const u32 texture_height = texture_info ? texture_info->height : framebuffer.height; | ||
| 74 | const u32 scaled_width = texture_info ? texture_info->scaled_width : texture_width; | ||
| 75 | const u32 scaled_height = texture_info ? texture_info->scaled_height : texture_height; | ||
| 76 | const bool use_accelerated = texture_info.has_value(); | ||
| 77 | |||
| 78 | RefreshResources(framebuffer); | ||
| 79 | SetAntiAliasPass(); | ||
| 80 | |||
| 81 | // Finish any pending renderpass | ||
| 82 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 83 | scheduler.Wait(resource_ticks[image_index]); | ||
| 84 | SCOPE_EXIT({ resource_ticks[image_index] = scheduler.CurrentTick(); }); | ||
| 85 | |||
| 86 | if (!use_accelerated) { | ||
| 87 | UpdateRawImage(framebuffer, image_index); | ||
| 88 | } | ||
| 89 | |||
| 90 | VkImage source_image = texture_info ? texture_info->image : *raw_images[image_index]; | ||
| 91 | VkImageView source_image_view = | ||
| 92 | texture_info ? texture_info->image_view : *raw_image_views[image_index]; | ||
| 93 | |||
| 94 | anti_alias->Draw(scheduler, image_index, &source_image, &source_image_view); | ||
| 95 | |||
| 96 | auto crop_rect = Tegra::NormalizeCrop(framebuffer, texture_width, texture_height); | ||
| 97 | const VkExtent2D render_extent{ | ||
| 98 | .width = scaled_width, | ||
| 99 | .height = scaled_height, | ||
| 100 | }; | ||
| 101 | |||
| 102 | if (fsr) { | ||
| 103 | source_image_view = fsr->Draw(scheduler, image_index, source_image, source_image_view, | ||
| 104 | render_extent, crop_rect); | ||
| 105 | crop_rect = {0, 0, 1, 1}; | ||
| 106 | } | ||
| 107 | |||
| 108 | SetMatrixData(*out_push_constants, layout); | ||
| 109 | SetVertexData(*out_push_constants, layout, crop_rect); | ||
| 110 | |||
| 111 | UpdateDescriptorSet(source_image_view, sampler, image_index); | ||
| 112 | *out_descriptor_set = descriptor_sets[image_index]; | ||
| 113 | } | ||
| 114 | |||
| 115 | void Layer::CreateDescriptorPool() { | ||
| 116 | descriptor_pool = CreateWrappedDescriptorPool(device, image_count, image_count); | ||
| 117 | } | ||
| 118 | |||
| 119 | void Layer::CreateDescriptorSets(VkDescriptorSetLayout layout) { | ||
| 120 | const std::vector layouts(image_count, layout); | ||
| 121 | descriptor_sets = CreateWrappedDescriptorSets(descriptor_pool, layouts); | ||
| 122 | } | ||
| 123 | |||
| 124 | void Layer::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { | ||
| 125 | const VkBufferCreateInfo ci{ | ||
| 126 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||
| 127 | .pNext = nullptr, | ||
| 128 | .flags = 0, | ||
| 129 | .size = CalculateBufferSize(framebuffer), | ||
| 130 | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | | ||
| 131 | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, | ||
| 132 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 133 | .queueFamilyIndexCount = 0, | ||
| 134 | .pQueueFamilyIndices = nullptr, | ||
| 135 | }; | ||
| 136 | |||
| 137 | buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); | ||
| 138 | } | ||
| 139 | |||
| 140 | void Layer::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { | ||
| 141 | const auto format = GetFormat(framebuffer); | ||
| 142 | resource_ticks.resize(image_count); | ||
| 143 | raw_images.resize(image_count); | ||
| 144 | raw_image_views.resize(image_count); | ||
| 145 | |||
| 146 | for (size_t i = 0; i < image_count; ++i) { | ||
| 147 | raw_images[i] = | ||
| 148 | CreateWrappedImage(memory_allocator, {framebuffer.width, framebuffer.height}, format); | ||
| 149 | raw_image_views[i] = CreateWrappedImageView(device, raw_images[i], format); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | |||
| 153 | void Layer::CreateFSR(VkExtent2D output_size) { | ||
| 154 | fsr = std::make_unique<FSR>(device, memory_allocator, image_count, output_size); | ||
| 155 | } | ||
| 156 | |||
| 157 | void Layer::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { | ||
| 158 | if (framebuffer.width == raw_width && framebuffer.height == raw_height && | ||
| 159 | framebuffer.pixel_format == pixel_format && !raw_images.empty()) { | ||
| 160 | return; | ||
| 161 | } | ||
| 162 | |||
| 163 | raw_width = framebuffer.width; | ||
| 164 | raw_height = framebuffer.height; | ||
| 165 | pixel_format = framebuffer.pixel_format; | ||
| 166 | anti_alias.reset(); | ||
| 167 | |||
| 168 | ReleaseRawImages(); | ||
| 169 | CreateStagingBuffer(framebuffer); | ||
| 170 | CreateRawImages(framebuffer); | ||
| 171 | } | ||
| 172 | |||
| 173 | void Layer::SetAntiAliasPass() { | ||
| 174 | if (anti_alias && anti_alias_setting == Settings::values.anti_aliasing.GetValue()) { | ||
| 175 | return; | ||
| 176 | } | ||
| 177 | |||
| 178 | anti_alias_setting = Settings::values.anti_aliasing.GetValue(); | ||
| 179 | |||
| 180 | const VkExtent2D render_area{ | ||
| 181 | .width = Settings::values.resolution_info.ScaleUp(raw_width), | ||
| 182 | .height = Settings::values.resolution_info.ScaleUp(raw_height), | ||
| 183 | }; | ||
| 184 | |||
| 185 | switch (anti_alias_setting) { | ||
| 186 | case Settings::AntiAliasing::Fxaa: | ||
| 187 | anti_alias = std::make_unique<FXAA>(device, memory_allocator, image_count, render_area); | ||
| 188 | break; | ||
| 189 | case Settings::AntiAliasing::Smaa: | ||
| 190 | anti_alias = std::make_unique<SMAA>(device, memory_allocator, image_count, render_area); | ||
| 191 | break; | ||
| 192 | default: | ||
| 193 | anti_alias = std::make_unique<NoAA>(); | ||
| 194 | break; | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | void Layer::ReleaseRawImages() { | ||
| 199 | for (const u64 tick : resource_ticks) { | ||
| 200 | scheduler.Wait(tick); | ||
| 201 | } | ||
| 202 | raw_images.clear(); | ||
| 203 | buffer.reset(); | ||
| 204 | } | ||
| 205 | |||
| 206 | u64 Layer::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const { | ||
| 207 | return GetSizeInBytes(framebuffer) * image_count; | ||
| 208 | } | ||
| 209 | |||
| 210 | u64 Layer::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, | ||
| 211 | size_t image_index) const { | ||
| 212 | return GetSizeInBytes(framebuffer) * image_index; | ||
| 213 | } | ||
| 214 | |||
| 215 | void Layer::SetMatrixData(PresentPushConstants& data, | ||
| 216 | const Layout::FramebufferLayout& layout) const { | ||
| 217 | data.modelview_matrix = | ||
| 218 | MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); | ||
| 219 | } | ||
| 220 | |||
| 221 | void Layer::SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, | ||
| 222 | const Common::Rectangle<f32>& crop) const { | ||
| 223 | // Map the coordinates to the screen. | ||
| 224 | const auto& screen = layout.screen; | ||
| 225 | const auto x = static_cast<f32>(screen.left); | ||
| 226 | const auto y = static_cast<f32>(screen.top); | ||
| 227 | const auto w = static_cast<f32>(screen.GetWidth()); | ||
| 228 | const auto h = static_cast<f32>(screen.GetHeight()); | ||
| 229 | |||
| 230 | data.vertices[0] = ScreenRectVertex(x, y, crop.left, crop.top); | ||
| 231 | data.vertices[1] = ScreenRectVertex(x + w, y, crop.right, crop.top); | ||
| 232 | data.vertices[2] = ScreenRectVertex(x, y + h, crop.left, crop.bottom); | ||
| 233 | data.vertices[3] = ScreenRectVertex(x + w, y + h, crop.right, crop.bottom); | ||
| 234 | } | ||
| 235 | |||
| 236 | void Layer::UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index) { | ||
| 237 | const VkDescriptorImageInfo image_info{ | ||
| 238 | .sampler = sampler, | ||
| 239 | .imageView = image_view, | ||
| 240 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 241 | }; | ||
| 242 | |||
| 243 | const VkWriteDescriptorSet sampler_write{ | ||
| 244 | .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||
| 245 | .pNext = nullptr, | ||
| 246 | .dstSet = descriptor_sets[image_index], | ||
| 247 | .dstBinding = 0, | ||
| 248 | .dstArrayElement = 0, | ||
| 249 | .descriptorCount = 1, | ||
| 250 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 251 | .pImageInfo = &image_info, | ||
| 252 | .pBufferInfo = nullptr, | ||
| 253 | .pTexelBufferView = nullptr, | ||
| 254 | }; | ||
| 255 | |||
| 256 | device.GetLogical().UpdateDescriptorSets(std::array{sampler_write}, {}); | ||
| 257 | } | ||
| 258 | |||
| 259 | void Layer::UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index) { | ||
| 260 | const std::span<u8> mapped_span = buffer.Mapped(); | ||
| 261 | |||
| 262 | const u64 image_offset = GetRawImageOffset(framebuffer, image_index); | ||
| 263 | |||
| 264 | const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset; | ||
| 265 | const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr); | ||
| 266 | |||
| 267 | // TODO(Rodrigo): Read this from HLE | ||
| 268 | constexpr u32 block_height_log2 = 4; | ||
| 269 | const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); | ||
| 270 | const u64 linear_size{GetSizeInBytes(framebuffer)}; | ||
| 271 | const u64 tiled_size{Tegra::Texture::CalculateSize( | ||
| 272 | true, bytes_per_pixel, framebuffer.stride, framebuffer.height, 1, block_height_log2, 0)}; | ||
| 273 | Tegra::Texture::UnswizzleTexture( | ||
| 274 | mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), | ||
| 275 | bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); | ||
| 276 | |||
| 277 | const VkBufferImageCopy copy{ | ||
| 278 | .bufferOffset = image_offset, | ||
| 279 | .bufferRowLength = 0, | ||
| 280 | .bufferImageHeight = 0, | ||
| 281 | .imageSubresource = | ||
| 282 | { | ||
| 283 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 284 | .mipLevel = 0, | ||
| 285 | .baseArrayLayer = 0, | ||
| 286 | .layerCount = 1, | ||
| 287 | }, | ||
| 288 | .imageOffset = {.x = 0, .y = 0, .z = 0}, | ||
| 289 | .imageExtent = | ||
| 290 | { | ||
| 291 | .width = framebuffer.width, | ||
| 292 | .height = framebuffer.height, | ||
| 293 | .depth = 1, | ||
| 294 | }, | ||
| 295 | }; | ||
| 296 | scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { | ||
| 297 | const VkImage image = *raw_images[index]; | ||
| 298 | const VkImageMemoryBarrier base_barrier{ | ||
| 299 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 300 | .pNext = nullptr, | ||
| 301 | .srcAccessMask = 0, | ||
| 302 | .dstAccessMask = 0, | ||
| 303 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 304 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 305 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 306 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 307 | .image = image, | ||
| 308 | .subresourceRange{ | ||
| 309 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 310 | .baseMipLevel = 0, | ||
| 311 | .levelCount = 1, | ||
| 312 | .baseArrayLayer = 0, | ||
| 313 | .layerCount = 1, | ||
| 314 | }, | ||
| 315 | }; | ||
| 316 | VkImageMemoryBarrier read_barrier = base_barrier; | ||
| 317 | read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | ||
| 318 | read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||
| 319 | read_barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | ||
| 320 | |||
| 321 | VkImageMemoryBarrier write_barrier = base_barrier; | ||
| 322 | write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | ||
| 323 | write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||
| 324 | write_barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; | ||
| 325 | |||
| 326 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, | ||
| 327 | read_barrier); | ||
| 328 | cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, copy); | ||
| 329 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, | ||
| 330 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | | ||
| 331 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||
| 332 | 0, write_barrier); | ||
| 333 | }); | ||
| 334 | } | ||
| 335 | |||
| 336 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/math_util.h" | ||
| 7 | #include "video_core/host1x/gpu_device_memory_manager.h" | ||
| 8 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 9 | |||
| 10 | namespace Layout { | ||
| 11 | struct FramebufferLayout; | ||
| 12 | } | ||
| 13 | |||
| 14 | namespace Tegra { | ||
| 15 | struct FramebufferConfig; | ||
| 16 | } | ||
| 17 | |||
| 18 | namespace Service::android { | ||
| 19 | enum class PixelFormat : u32; | ||
| 20 | } | ||
| 21 | |||
| 22 | namespace Settings { | ||
| 23 | enum class AntiAliasing : u32; | ||
| 24 | } | ||
| 25 | |||
| 26 | namespace Vulkan { | ||
| 27 | |||
| 28 | class AntiAliasPass; | ||
| 29 | class Device; | ||
| 30 | class FSR; | ||
| 31 | class MemoryAllocator; | ||
| 32 | struct PresentPushConstants; | ||
| 33 | class RasterizerVulkan; | ||
| 34 | class Scheduler; | ||
| 35 | |||
| 36 | class Layer final { | ||
| 37 | public: | ||
| 38 | explicit Layer(const Device& device, MemoryAllocator& memory_allocator, Scheduler& scheduler, | ||
| 39 | Tegra::MaxwellDeviceMemoryManager& device_memory, size_t image_count, | ||
| 40 | VkExtent2D output_size, VkDescriptorSetLayout layout); | ||
| 41 | ~Layer(); | ||
| 42 | |||
| 43 | void ConfigureDraw(PresentPushConstants* out_push_constants, | ||
| 44 | VkDescriptorSet* out_descriptor_set, RasterizerVulkan& rasterizer, | ||
| 45 | VkSampler sampler, size_t image_index, | ||
| 46 | const Tegra::FramebufferConfig& framebuffer, | ||
| 47 | const Layout::FramebufferLayout& layout); | ||
| 48 | |||
| 49 | private: | ||
| 50 | void CreateDescriptorPool(); | ||
| 51 | void CreateDescriptorSets(VkDescriptorSetLayout layout); | ||
| 52 | void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); | ||
| 53 | void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); | ||
| 54 | void CreateFSR(VkExtent2D output_size); | ||
| 55 | |||
| 56 | void RefreshResources(const Tegra::FramebufferConfig& framebuffer); | ||
| 57 | void SetAntiAliasPass(); | ||
| 58 | void ReleaseRawImages(); | ||
| 59 | |||
| 60 | u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; | ||
| 61 | u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer, size_t image_index) const; | ||
| 62 | |||
| 63 | void SetMatrixData(PresentPushConstants& data, const Layout::FramebufferLayout& layout) const; | ||
| 64 | void SetVertexData(PresentPushConstants& data, const Layout::FramebufferLayout& layout, | ||
| 65 | const Common::Rectangle<f32>& crop) const; | ||
| 66 | void UpdateDescriptorSet(VkImageView image_view, VkSampler sampler, size_t image_index); | ||
| 67 | void UpdateRawImage(const Tegra::FramebufferConfig& framebuffer, size_t image_index); | ||
| 68 | |||
| 69 | private: | ||
| 70 | const Device& device; | ||
| 71 | MemoryAllocator& memory_allocator; | ||
| 72 | Scheduler& scheduler; | ||
| 73 | Tegra::MaxwellDeviceMemoryManager& device_memory; | ||
| 74 | const size_t image_count{}; | ||
| 75 | vk::DescriptorPool descriptor_pool{}; | ||
| 76 | vk::DescriptorSets descriptor_sets{}; | ||
| 77 | |||
| 78 | vk::Buffer buffer{}; | ||
| 79 | std::vector<vk::Image> raw_images{}; | ||
| 80 | std::vector<vk::ImageView> raw_image_views{}; | ||
| 81 | u32 raw_width{}; | ||
| 82 | u32 raw_height{}; | ||
| 83 | Service::android::PixelFormat pixel_format{}; | ||
| 84 | |||
| 85 | Settings::AntiAliasing anti_alias_setting{}; | ||
| 86 | std::unique_ptr<AntiAliasPass> anti_alias{}; | ||
| 87 | |||
| 88 | std::unique_ptr<FSR> fsr{}; | ||
| 89 | std::vector<u64> resource_ticks{}; | ||
| 90 | }; | ||
| 91 | |||
| 92 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/common_types.h" | ||
| 7 | |||
| 8 | namespace Vulkan { | ||
| 9 | |||
| 10 | struct ScreenRectVertex { | ||
| 11 | ScreenRectVertex() = default; | ||
| 12 | explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} | ||
| 13 | |||
| 14 | std::array<f32, 2> position; | ||
| 15 | std::array<f32, 2> tex_coord; | ||
| 16 | }; | ||
| 17 | |||
| 18 | static inline std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { | ||
| 19 | // clang-format off | ||
| 20 | return { 2.f / width, 0.f, 0.f, 0.f, | ||
| 21 | 0.f, 2.f / height, 0.f, 0.f, | ||
| 22 | 0.f, 0.f, 1.f, 0.f, | ||
| 23 | -1.f, -1.f, 0.f, 1.f}; | ||
| 24 | // clang-format on | ||
| 25 | } | ||
| 26 | |||
| 27 | struct PresentPushConstants { | ||
| 28 | std::array<f32, 4 * 4> modelview_matrix; | ||
| 29 | std::array<ScreenRectVertex, 4> vertices; | ||
| 30 | }; | ||
| 31 | |||
| 32 | static_assert(sizeof(PresentPushConstants) <= 128, "Push constants are too large"); | ||
| 33 | |||
| 34 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include <list> | ||
| 5 | |||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/polyfill_ranges.h" | ||
| 8 | |||
| 9 | #include "video_core/renderer_vulkan/present/smaa.h" | ||
| 10 | #include "video_core/renderer_vulkan/present/util.h" | ||
| 11 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 12 | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||
| 13 | #include "video_core/smaa_area_tex.h" | ||
| 14 | #include "video_core/smaa_search_tex.h" | ||
| 15 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 16 | |||
| 17 | #include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h" | ||
| 18 | #include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h" | ||
| 19 | #include "video_core/host_shaders/smaa_edge_detection_frag_spv.h" | ||
| 20 | #include "video_core/host_shaders/smaa_edge_detection_vert_spv.h" | ||
| 21 | #include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h" | ||
| 22 | #include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h" | ||
| 23 | |||
| 24 | namespace Vulkan { | ||
| 25 | |||
| 26 | SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) | ||
| 27 | : m_device(device), m_allocator(allocator), m_extent(extent), | ||
| 28 | m_image_count(static_cast<u32>(image_count)) { | ||
| 29 | CreateImages(); | ||
| 30 | CreateRenderPasses(); | ||
| 31 | CreateSampler(); | ||
| 32 | CreateShaders(); | ||
| 33 | CreateDescriptorPool(); | ||
| 34 | CreateDescriptorSetLayouts(); | ||
| 35 | CreateDescriptorSets(); | ||
| 36 | CreatePipelineLayouts(); | ||
| 37 | CreatePipelines(); | ||
| 38 | } | ||
| 39 | |||
| 40 | SMAA::~SMAA() = default; | ||
| 41 | |||
| 42 | void SMAA::CreateImages() { | ||
| 43 | static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; | ||
| 44 | static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; | ||
| 45 | |||
| 46 | m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM); | ||
| 47 | m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM); | ||
| 48 | |||
| 49 | m_static_image_views[Area] = | ||
| 50 | CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); | ||
| 51 | m_static_image_views[Search] = | ||
| 52 | CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM); | ||
| 53 | |||
| 54 | for (u32 i = 0; i < m_image_count; i++) { | ||
| 55 | Images& images = m_dynamic_images.emplace_back(); | ||
| 56 | |||
| 57 | images.images[Blend] = | ||
| 58 | CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 59 | images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); | ||
| 60 | images.images[Output] = | ||
| 61 | CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 62 | |||
| 63 | images.image_views[Blend] = | ||
| 64 | CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 65 | images.image_views[Edges] = | ||
| 66 | CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); | ||
| 67 | images.image_views[Output] = | ||
| 68 | CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | void SMAA::CreateRenderPasses() { | ||
| 73 | m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT); | ||
| 74 | m_renderpasses[BlendingWeightCalculation] = | ||
| 75 | CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 76 | m_renderpasses[NeighborhoodBlending] = | ||
| 77 | CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 78 | |||
| 79 | for (auto& images : m_dynamic_images) { | ||
| 80 | images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( | ||
| 81 | m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); | ||
| 82 | |||
| 83 | images.framebuffers[BlendingWeightCalculation] = | ||
| 84 | CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation], | ||
| 85 | images.image_views[Blend], m_extent); | ||
| 86 | |||
| 87 | images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( | ||
| 88 | m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); | ||
| 89 | } | ||
| 90 | } | ||
| 91 | |||
| 92 | void SMAA::CreateSampler() { | ||
| 93 | m_sampler = CreateWrappedSampler(m_device); | ||
| 94 | } | ||
| 95 | |||
| 96 | void SMAA::CreateShaders() { | ||
| 97 | // These match the order of the SMAAStage enum | ||
| 98 | static constexpr std::array vert_shader_sources{ | ||
| 99 | ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV), | ||
| 100 | ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV), | ||
| 101 | ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV), | ||
| 102 | }; | ||
| 103 | static constexpr std::array frag_shader_sources{ | ||
| 104 | ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV), | ||
| 105 | ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV), | ||
| 106 | ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV), | ||
| 107 | }; | ||
| 108 | |||
| 109 | for (size_t i = 0; i < MaxSMAAStage; i++) { | ||
| 110 | m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]); | ||
| 111 | m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | void SMAA::CreateDescriptorPool() { | ||
| 116 | // Edge detection: 1 descriptor | ||
| 117 | // Blending weight calculation: 3 descriptors | ||
| 118 | // Neighborhood blending: 2 descriptors | ||
| 119 | |||
| 120 | // 6 descriptors, 3 descriptor sets per image | ||
| 121 | m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count); | ||
| 122 | } | ||
| 123 | |||
| 124 | void SMAA::CreateDescriptorSetLayouts() { | ||
| 125 | m_descriptor_set_layouts[EdgeDetection] = | ||
| 126 | CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||
| 127 | m_descriptor_set_layouts[BlendingWeightCalculation] = | ||
| 128 | CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 129 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 130 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||
| 131 | m_descriptor_set_layouts[NeighborhoodBlending] = | ||
| 132 | CreateWrappedDescriptorSetLayout(m_device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 133 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||
| 134 | } | ||
| 135 | |||
| 136 | void SMAA::CreateDescriptorSets() { | ||
| 137 | std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size()); | ||
| 138 | std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), | ||
| 139 | [](auto& layout) { return *layout; }); | ||
| 140 | |||
| 141 | for (auto& images : m_dynamic_images) { | ||
| 142 | images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); | ||
| 143 | } | ||
| 144 | } | ||
| 145 | |||
| 146 | void SMAA::CreatePipelineLayouts() { | ||
| 147 | for (size_t i = 0; i < MaxSMAAStage; i++) { | ||
| 148 | m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]); | ||
| 149 | } | ||
| 150 | } | ||
| 151 | |||
| 152 | void SMAA::CreatePipelines() { | ||
| 153 | for (size_t i = 0; i < MaxSMAAStage; i++) { | ||
| 154 | m_pipelines[i] = | ||
| 155 | CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i], | ||
| 156 | std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
| 160 | void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { | ||
| 161 | Images& images = m_dynamic_images[image_index]; | ||
| 162 | std::vector<VkDescriptorImageInfo> image_infos; | ||
| 163 | std::vector<VkWriteDescriptorSet> updates; | ||
| 164 | image_infos.reserve(6); | ||
| 165 | |||
| 166 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, | ||
| 167 | images.descriptor_sets[EdgeDetection], 0)); | ||
| 168 | |||
| 169 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges], | ||
| 170 | images.descriptor_sets[BlendingWeightCalculation], | ||
| 171 | 0)); | ||
| 172 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area], | ||
| 173 | images.descriptor_sets[BlendingWeightCalculation], | ||
| 174 | 1)); | ||
| 175 | updates.push_back( | ||
| 176 | CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search], | ||
| 177 | images.descriptor_sets[BlendingWeightCalculation], 2)); | ||
| 178 | |||
| 179 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, | ||
| 180 | images.descriptor_sets[NeighborhoodBlending], 0)); | ||
| 181 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], | ||
| 182 | images.descriptor_sets[NeighborhoodBlending], 1)); | ||
| 183 | |||
| 184 | m_device.GetLogical().UpdateDescriptorSets(updates, {}); | ||
| 185 | } | ||
| 186 | |||
| 187 | void SMAA::UploadImages(Scheduler& scheduler) { | ||
| 188 | if (m_images_ready) { | ||
| 189 | return; | ||
| 190 | } | ||
| 191 | |||
| 192 | static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; | ||
| 193 | static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; | ||
| 194 | |||
| 195 | UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent, | ||
| 196 | VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes)); | ||
| 197 | UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, | ||
| 198 | VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); | ||
| 199 | |||
| 200 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { | ||
| 201 | for (auto& images : m_dynamic_images) { | ||
| 202 | for (size_t i = 0; i < MaxDynamicImage; i++) { | ||
| 203 | ClearColorImage(cmdbuf, *images.images[i]); | ||
| 204 | } | ||
| 205 | } | ||
| 206 | }); | ||
| 207 | scheduler.Finish(); | ||
| 208 | |||
| 209 | m_images_ready = true; | ||
| 210 | } | ||
| 211 | |||
| 212 | void SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, | ||
| 213 | VkImageView* inout_image_view) { | ||
| 214 | Images& images = m_dynamic_images[image_index]; | ||
| 215 | |||
| 216 | VkImage input_image = *inout_image; | ||
| 217 | VkImage output_image = *images.images[Output]; | ||
| 218 | VkImage edges_image = *images.images[Edges]; | ||
| 219 | VkImage blend_image = *images.images[Blend]; | ||
| 220 | |||
| 221 | VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection]; | ||
| 222 | VkDescriptorSet blending_weight_calculation_descriptor_set = | ||
| 223 | images.descriptor_sets[BlendingWeightCalculation]; | ||
| 224 | VkDescriptorSet neighborhood_blending_descriptor_set = | ||
| 225 | images.descriptor_sets[NeighborhoodBlending]; | ||
| 226 | |||
| 227 | VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection]; | ||
| 228 | VkFramebuffer blending_weight_calculation_framebuffer = | ||
| 229 | *images.framebuffers[BlendingWeightCalculation]; | ||
| 230 | VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; | ||
| 231 | |||
| 232 | UploadImages(scheduler); | ||
| 233 | UpdateDescriptorSets(*inout_image_view, image_index); | ||
| 234 | |||
| 235 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 236 | scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { | ||
| 237 | TransitionImageLayout(cmdbuf, input_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 238 | TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 239 | BeginRenderPass(cmdbuf, *m_renderpasses[EdgeDetection], edge_detection_framebuffer, | ||
| 240 | m_extent); | ||
| 241 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]); | ||
| 242 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 243 | *m_pipeline_layouts[EdgeDetection], 0, | ||
| 244 | edge_detection_descriptor_set, {}); | ||
| 245 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 246 | cmdbuf.EndRenderPass(); | ||
| 247 | |||
| 248 | TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 249 | TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 250 | BeginRenderPass(cmdbuf, *m_renderpasses[BlendingWeightCalculation], | ||
| 251 | blending_weight_calculation_framebuffer, m_extent); | ||
| 252 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 253 | *m_pipelines[BlendingWeightCalculation]); | ||
| 254 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 255 | *m_pipeline_layouts[BlendingWeightCalculation], 0, | ||
| 256 | blending_weight_calculation_descriptor_set, {}); | ||
| 257 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 258 | cmdbuf.EndRenderPass(); | ||
| 259 | |||
| 260 | TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 261 | TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 262 | BeginRenderPass(cmdbuf, *m_renderpasses[NeighborhoodBlending], | ||
| 263 | neighborhood_blending_framebuffer, m_extent); | ||
| 264 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]); | ||
| 265 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 266 | *m_pipeline_layouts[NeighborhoodBlending], 0, | ||
| 267 | neighborhood_blending_descriptor_set, {}); | ||
| 268 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 269 | cmdbuf.EndRenderPass(); | ||
| 270 | TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 271 | }); | ||
| 272 | |||
| 273 | *inout_image = *images.images[Output]; | ||
| 274 | *inout_image_view = *images.image_views[Output]; | ||
| 275 | } | ||
| 276 | |||
| 277 | } // 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 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include "video_core/renderer_vulkan/present/anti_alias_pass.h" | ||
| 7 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | 8 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" |
| 8 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 9 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 9 | 10 | ||
| @@ -13,12 +14,14 @@ class Device; | |||
| 13 | class Scheduler; | 14 | class Scheduler; |
| 14 | class StagingBufferPool; | 15 | class StagingBufferPool; |
| 15 | 16 | ||
| 16 | class SMAA { | 17 | class SMAA final : public AntiAliasPass { |
| 17 | public: | 18 | public: |
| 18 | explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, | 19 | explicit SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, |
| 19 | VkExtent2D extent); | 20 | VkExtent2D extent); |
| 20 | VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, | 21 | ~SMAA() override; |
| 21 | VkImageView source_image_view); | 22 | |
| 23 | void Draw(Scheduler& scheduler, size_t image_index, VkImage* inout_image, | ||
| 24 | VkImageView* inout_image_view) override; | ||
| 22 | 25 | ||
| 23 | private: | 26 | private: |
| 24 | enum SMAAStage { | 27 | 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <list> | ||
| 5 | |||
| 6 | #include "common/assert.h" | 4 | #include "common/assert.h" |
| 7 | #include "common/polyfill_ranges.h" | 5 | #include "common/polyfill_ranges.h" |
| 8 | 6 | #include "video_core/renderer_vulkan/present/util.h" | |
| 9 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 10 | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||
| 11 | #include "video_core/renderer_vulkan/vk_smaa.h" | ||
| 12 | #include "video_core/smaa_area_tex.h" | ||
| 13 | #include "video_core/smaa_search_tex.h" | ||
| 14 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 15 | |||
| 16 | #include "video_core/host_shaders/smaa_blending_weight_calculation_frag_spv.h" | ||
| 17 | #include "video_core/host_shaders/smaa_blending_weight_calculation_vert_spv.h" | ||
| 18 | #include "video_core/host_shaders/smaa_edge_detection_frag_spv.h" | ||
| 19 | #include "video_core/host_shaders/smaa_edge_detection_vert_spv.h" | ||
| 20 | #include "video_core/host_shaders/smaa_neighborhood_blending_frag_spv.h" | ||
| 21 | #include "video_core/host_shaders/smaa_neighborhood_blending_vert_spv.h" | ||
| 22 | 7 | ||
| 23 | namespace Vulkan { | 8 | namespace Vulkan { |
| 24 | namespace { | ||
| 25 | 9 | ||
| 26 | #define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) | 10 | vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage) { |
| 11 | const VkBufferCreateInfo dst_buffer_info{ | ||
| 12 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||
| 13 | .pNext = nullptr, | ||
| 14 | .flags = 0, | ||
| 15 | .size = size, | ||
| 16 | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT, | ||
| 17 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 18 | .queueFamilyIndexCount = 0, | ||
| 19 | .pQueueFamilyIndices = nullptr, | ||
| 20 | }; | ||
| 21 | return allocator.CreateBuffer(dst_buffer_info, usage); | ||
| 22 | } | ||
| 27 | 23 | ||
| 28 | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { | 24 | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format) { |
| 29 | const VkImageCreateInfo image_ci{ | 25 | const VkImageCreateInfo image_ci{ |
| @@ -48,7 +44,7 @@ vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, | |||
| 48 | } | 44 | } |
| 49 | 45 | ||
| 50 | void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, | 46 | void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, |
| 51 | VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL) { | 47 | VkImageLayout source_layout) { |
| 52 | constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | | 48 | constexpr VkFlags flags{VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | |
| 53 | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; | 49 | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_SHADER_READ_BIT}; |
| 54 | const VkImageMemoryBarrier barrier{ | 50 | const VkImageMemoryBarrier barrier{ |
| @@ -75,7 +71,7 @@ void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayo | |||
| 75 | 71 | ||
| 76 | void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, | 72 | void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, |
| 77 | vk::Image& image, VkExtent2D dimensions, VkFormat format, | 73 | vk::Image& image, VkExtent2D dimensions, VkFormat format, |
| 78 | std::span<const u8> initial_contents = {}) { | 74 | std::span<const u8> initial_contents) { |
| 79 | const VkBufferCreateInfo upload_ci = { | 75 | const VkBufferCreateInfo upload_ci = { |
| 80 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | 76 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, |
| 81 | .pNext = nullptr, | 77 | .pNext = nullptr, |
| @@ -114,6 +110,70 @@ void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& sc | |||
| 114 | scheduler.Finish(); | 110 | scheduler.Finish(); |
| 115 | } | 111 | } |
| 116 | 112 | ||
| 113 | void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, | ||
| 114 | VkExtent3D extent) { | ||
| 115 | const VkImageMemoryBarrier read_barrier{ | ||
| 116 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 117 | .pNext = nullptr, | ||
| 118 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 119 | .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, | ||
| 120 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 121 | .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 122 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 123 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 124 | .image = image, | ||
| 125 | .subresourceRange{ | ||
| 126 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 127 | .baseMipLevel = 0, | ||
| 128 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 129 | .baseArrayLayer = 0, | ||
| 130 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 131 | }, | ||
| 132 | }; | ||
| 133 | const VkImageMemoryBarrier image_write_barrier{ | ||
| 134 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 135 | .pNext = nullptr, | ||
| 136 | .srcAccessMask = 0, | ||
| 137 | .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 138 | .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 139 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 140 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 141 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 142 | .image = image, | ||
| 143 | .subresourceRange{ | ||
| 144 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 145 | .baseMipLevel = 0, | ||
| 146 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 147 | .baseArrayLayer = 0, | ||
| 148 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 149 | }, | ||
| 150 | }; | ||
| 151 | static constexpr VkMemoryBarrier memory_write_barrier{ | ||
| 152 | .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, | ||
| 153 | .pNext = nullptr, | ||
| 154 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 155 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 156 | }; | ||
| 157 | const VkBufferImageCopy copy{ | ||
| 158 | .bufferOffset = 0, | ||
| 159 | .bufferRowLength = 0, | ||
| 160 | .bufferImageHeight = 0, | ||
| 161 | .imageSubresource{ | ||
| 162 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 163 | .mipLevel = 0, | ||
| 164 | .baseArrayLayer = 0, | ||
| 165 | .layerCount = 1, | ||
| 166 | }, | ||
| 167 | .imageOffset{.x = 0, .y = 0, .z = 0}, | ||
| 168 | .imageExtent{extent}, | ||
| 169 | }; | ||
| 170 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, | ||
| 171 | read_barrier); | ||
| 172 | cmdbuf.CopyImageToBuffer(image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, buffer, copy); | ||
| 173 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, 0, | ||
| 174 | memory_write_barrier, nullptr, image_write_barrier); | ||
| 175 | } | ||
| 176 | |||
| 117 | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { | 177 | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format) { |
| 118 | return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | 178 | return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ |
| 119 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 179 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
| @@ -131,16 +191,18 @@ vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkF | |||
| 131 | }); | 191 | }); |
| 132 | } | 192 | } |
| 133 | 193 | ||
| 134 | vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format) { | 194 | vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, |
| 195 | VkImageLayout initial_layout) { | ||
| 135 | const VkAttachmentDescription attachment{ | 196 | const VkAttachmentDescription attachment{ |
| 136 | .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, | 197 | .flags = VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT, |
| 137 | .format = format, | 198 | .format = format, |
| 138 | .samples = VK_SAMPLE_COUNT_1_BIT, | 199 | .samples = VK_SAMPLE_COUNT_1_BIT, |
| 139 | .loadOp = VK_ATTACHMENT_LOAD_OP_LOAD, | 200 | .loadOp = initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_ATTACHMENT_LOAD_OP_DONT_CARE |
| 201 | : VK_ATTACHMENT_LOAD_OP_LOAD, | ||
| 140 | .storeOp = VK_ATTACHMENT_STORE_OP_STORE, | 202 | .storeOp = VK_ATTACHMENT_STORE_OP_STORE, |
| 141 | .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, | 203 | .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD, |
| 142 | .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, | 204 | .stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE, |
| 143 | .initialLayout = VK_IMAGE_LAYOUT_GENERAL, | 205 | .initialLayout = initial_layout, |
| 144 | .finalLayout = VK_IMAGE_LAYOUT_GENERAL, | 206 | .finalLayout = VK_IMAGE_LAYOUT_GENERAL, |
| 145 | }; | 207 | }; |
| 146 | 208 | ||
| @@ -200,13 +262,13 @@ vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& r | |||
| 200 | }); | 262 | }); |
| 201 | } | 263 | } |
| 202 | 264 | ||
| 203 | vk::Sampler CreateWrappedSampler(const Device& device) { | 265 | vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter) { |
| 204 | return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ | 266 | return device.GetLogical().CreateSampler(VkSamplerCreateInfo{ |
| 205 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | 267 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, |
| 206 | .pNext = nullptr, | 268 | .pNext = nullptr, |
| 207 | .flags = 0, | 269 | .flags = 0, |
| 208 | .magFilter = VK_FILTER_LINEAR, | 270 | .magFilter = filter, |
| 209 | .minFilter = VK_FILTER_LINEAR, | 271 | .minFilter = filter, |
| 210 | .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, | 272 | .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, |
| 211 | .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | 273 | .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, |
| 212 | .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | 274 | .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, |
| @@ -233,30 +295,34 @@ vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const | |||
| 233 | }); | 295 | }); |
| 234 | } | 296 | } |
| 235 | 297 | ||
| 236 | vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, u32 max_descriptors, | 298 | vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors, |
| 237 | u32 max_sets) { | 299 | size_t max_sets, |
| 238 | const VkDescriptorPoolSize pool_size{ | 300 | std::initializer_list<VkDescriptorType> types) { |
| 239 | .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | 301 | std::vector<VkDescriptorPoolSize> pool_sizes(types.size()); |
| 240 | .descriptorCount = static_cast<u32>(max_descriptors), | 302 | for (u32 i = 0; i < types.size(); i++) { |
| 241 | }; | 303 | pool_sizes[i] = VkDescriptorPoolSize{ |
| 304 | .type = std::data(types)[i], | ||
| 305 | .descriptorCount = static_cast<u32>(max_descriptors), | ||
| 306 | }; | ||
| 307 | } | ||
| 242 | 308 | ||
| 243 | return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{ | 309 | return device.GetLogical().CreateDescriptorPool(VkDescriptorPoolCreateInfo{ |
| 244 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | 310 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, |
| 245 | .pNext = nullptr, | 311 | .pNext = nullptr, |
| 246 | .flags = 0, | 312 | .flags = 0, |
| 247 | .maxSets = max_sets, | 313 | .maxSets = static_cast<u32>(max_sets), |
| 248 | .poolSizeCount = 1, | 314 | .poolSizeCount = static_cast<u32>(pool_sizes.size()), |
| 249 | .pPoolSizes = &pool_size, | 315 | .pPoolSizes = pool_sizes.data(), |
| 250 | }); | 316 | }); |
| 251 | } | 317 | } |
| 252 | 318 | ||
| 253 | vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout(const Device& device, | 319 | vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout( |
| 254 | u32 max_sampler_bindings) { | 320 | const Device& device, std::initializer_list<VkDescriptorType> types) { |
| 255 | std::vector<VkDescriptorSetLayoutBinding> bindings(max_sampler_bindings); | 321 | std::vector<VkDescriptorSetLayoutBinding> bindings(types.size()); |
| 256 | for (u32 i = 0; i < max_sampler_bindings; i++) { | 322 | for (size_t i = 0; i < types.size(); i++) { |
| 257 | bindings[i] = { | 323 | bindings[i] = { |
| 258 | .binding = i, | 324 | .binding = static_cast<u32>(i), |
| 259 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | 325 | .descriptorType = std::data(types)[i], |
| 260 | .descriptorCount = 1, | 326 | .descriptorCount = 1, |
| 261 | .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, | 327 | .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, |
| 262 | .pImmutableSamplers = nullptr, | 328 | .pImmutableSamplers = nullptr, |
| @@ -298,7 +364,8 @@ vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, | |||
| 298 | 364 | ||
| 299 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | 365 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, |
| 300 | vk::PipelineLayout& layout, | 366 | vk::PipelineLayout& layout, |
| 301 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders) { | 367 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, |
| 368 | bool enable_blending) { | ||
| 302 | const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ | 369 | const std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages{{ |
| 303 | { | 370 | { |
| 304 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | 371 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, |
| @@ -376,7 +443,7 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | |||
| 376 | .alphaToOneEnable = VK_FALSE, | 443 | .alphaToOneEnable = VK_FALSE, |
| 377 | }; | 444 | }; |
| 378 | 445 | ||
| 379 | constexpr VkPipelineColorBlendAttachmentState color_blend_attachment{ | 446 | constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_disabled{ |
| 380 | .blendEnable = VK_FALSE, | 447 | .blendEnable = VK_FALSE, |
| 381 | .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, | 448 | .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, |
| 382 | .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, | 449 | .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, |
| @@ -388,6 +455,18 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | |||
| 388 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | 455 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, |
| 389 | }; | 456 | }; |
| 390 | 457 | ||
| 458 | constexpr VkPipelineColorBlendAttachmentState color_blend_attachment_enabled{ | ||
| 459 | .blendEnable = VK_TRUE, | ||
| 460 | .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, | ||
| 461 | .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, | ||
| 462 | .colorBlendOp = VK_BLEND_OP_ADD, | ||
| 463 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, | ||
| 464 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 465 | .alphaBlendOp = VK_BLEND_OP_ADD, | ||
| 466 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||
| 467 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||
| 468 | }; | ||
| 469 | |||
| 391 | const VkPipelineColorBlendStateCreateInfo color_blend_ci{ | 470 | const VkPipelineColorBlendStateCreateInfo color_blend_ci{ |
| 392 | .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | 471 | .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, |
| 393 | .pNext = nullptr, | 472 | .pNext = nullptr, |
| @@ -395,7 +474,8 @@ vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderp | |||
| 395 | .logicOpEnable = VK_FALSE, | 474 | .logicOpEnable = VK_FALSE, |
| 396 | .logicOp = VK_LOGIC_OP_COPY, | 475 | .logicOp = VK_LOGIC_OP_COPY, |
| 397 | .attachmentCount = 1, | 476 | .attachmentCount = 1, |
| 398 | .pAttachments = &color_blend_attachment, | 477 | .pAttachments = |
| 478 | enable_blending ? &color_blend_attachment_enabled : &color_blend_attachment_disabled, | ||
| 399 | .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, | 479 | .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, |
| 400 | }; | 480 | }; |
| 401 | 481 | ||
| @@ -459,6 +539,56 @@ VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo> | |||
| 459 | }; | 539 | }; |
| 460 | } | 540 | } |
| 461 | 541 | ||
| 542 | vk::Sampler CreateBilinearSampler(const Device& device) { | ||
| 543 | const VkSamplerCreateInfo ci{ | ||
| 544 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | ||
| 545 | .pNext = nullptr, | ||
| 546 | .flags = 0, | ||
| 547 | .magFilter = VK_FILTER_LINEAR, | ||
| 548 | .minFilter = VK_FILTER_LINEAR, | ||
| 549 | .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, | ||
| 550 | .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 551 | .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 552 | .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 553 | .mipLodBias = 0.0f, | ||
| 554 | .anisotropyEnable = VK_FALSE, | ||
| 555 | .maxAnisotropy = 0.0f, | ||
| 556 | .compareEnable = VK_FALSE, | ||
| 557 | .compareOp = VK_COMPARE_OP_NEVER, | ||
| 558 | .minLod = 0.0f, | ||
| 559 | .maxLod = 0.0f, | ||
| 560 | .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, | ||
| 561 | .unnormalizedCoordinates = VK_FALSE, | ||
| 562 | }; | ||
| 563 | |||
| 564 | return device.GetLogical().CreateSampler(ci); | ||
| 565 | } | ||
| 566 | |||
| 567 | vk::Sampler CreateNearestNeighborSampler(const Device& device) { | ||
| 568 | const VkSamplerCreateInfo ci_nn{ | ||
| 569 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | ||
| 570 | .pNext = nullptr, | ||
| 571 | .flags = 0, | ||
| 572 | .magFilter = VK_FILTER_NEAREST, | ||
| 573 | .minFilter = VK_FILTER_NEAREST, | ||
| 574 | .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, | ||
| 575 | .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 576 | .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 577 | .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 578 | .mipLodBias = 0.0f, | ||
| 579 | .anisotropyEnable = VK_FALSE, | ||
| 580 | .maxAnisotropy = 0.0f, | ||
| 581 | .compareEnable = VK_FALSE, | ||
| 582 | .compareOp = VK_COMPARE_OP_NEVER, | ||
| 583 | .minLod = 0.0f, | ||
| 584 | .maxLod = 0.0f, | ||
| 585 | .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, | ||
| 586 | .unnormalizedCoordinates = VK_FALSE, | ||
| 587 | }; | ||
| 588 | |||
| 589 | return device.GetLogical().CreateSampler(ci_nn); | ||
| 590 | } | ||
| 591 | |||
| 462 | void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { | 592 | void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { |
| 463 | static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{ | 593 | static constexpr std::array<VkImageSubresourceRange, 1> subresources{{{ |
| 464 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | 594 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, |
| @@ -471,12 +601,12 @@ void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image) { | |||
| 471 | cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources); | 601 | cmdbuf.ClearColorImage(image, VK_IMAGE_LAYOUT_GENERAL, {}, subresources); |
| 472 | } | 602 | } |
| 473 | 603 | ||
| 474 | void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, | 604 | void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, |
| 475 | VkFramebuffer framebuffer, VkExtent2D extent) { | 605 | VkExtent2D extent) { |
| 476 | const VkRenderPassBeginInfo renderpass_bi{ | 606 | const VkRenderPassBeginInfo renderpass_bi{ |
| 477 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | 607 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, |
| 478 | .pNext = nullptr, | 608 | .pNext = nullptr, |
| 479 | .renderPass = *render_pass, | 609 | .renderPass = render_pass, |
| 480 | .framebuffer = framebuffer, | 610 | .framebuffer = framebuffer, |
| 481 | .renderArea{ | 611 | .renderArea{ |
| 482 | .offset{}, | 612 | .offset{}, |
| @@ -503,248 +633,4 @@ void BeginRenderPass(vk::CommandBuffer& cmdbuf, vk::RenderPass& render_pass, | |||
| 503 | cmdbuf.SetScissor(0, scissor); | 633 | cmdbuf.SetScissor(0, scissor); |
| 504 | } | 634 | } |
| 505 | 635 | ||
| 506 | } // Anonymous namespace | ||
| 507 | |||
| 508 | SMAA::SMAA(const Device& device, MemoryAllocator& allocator, size_t image_count, VkExtent2D extent) | ||
| 509 | : m_device(device), m_allocator(allocator), m_extent(extent), | ||
| 510 | m_image_count(static_cast<u32>(image_count)) { | ||
| 511 | CreateImages(); | ||
| 512 | CreateRenderPasses(); | ||
| 513 | CreateSampler(); | ||
| 514 | CreateShaders(); | ||
| 515 | CreateDescriptorPool(); | ||
| 516 | CreateDescriptorSetLayouts(); | ||
| 517 | CreateDescriptorSets(); | ||
| 518 | CreatePipelineLayouts(); | ||
| 519 | CreatePipelines(); | ||
| 520 | } | ||
| 521 | |||
| 522 | void SMAA::CreateImages() { | ||
| 523 | static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; | ||
| 524 | static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; | ||
| 525 | |||
| 526 | m_static_images[Area] = CreateWrappedImage(m_allocator, area_extent, VK_FORMAT_R8G8_UNORM); | ||
| 527 | m_static_images[Search] = CreateWrappedImage(m_allocator, search_extent, VK_FORMAT_R8_UNORM); | ||
| 528 | |||
| 529 | m_static_image_views[Area] = | ||
| 530 | CreateWrappedImageView(m_device, m_static_images[Area], VK_FORMAT_R8G8_UNORM); | ||
| 531 | m_static_image_views[Search] = | ||
| 532 | CreateWrappedImageView(m_device, m_static_images[Search], VK_FORMAT_R8_UNORM); | ||
| 533 | |||
| 534 | for (u32 i = 0; i < m_image_count; i++) { | ||
| 535 | Images& images = m_dynamic_images.emplace_back(); | ||
| 536 | |||
| 537 | images.images[Blend] = | ||
| 538 | CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 539 | images.images[Edges] = CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16_SFLOAT); | ||
| 540 | images.images[Output] = | ||
| 541 | CreateWrappedImage(m_allocator, m_extent, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 542 | |||
| 543 | images.image_views[Blend] = | ||
| 544 | CreateWrappedImageView(m_device, images.images[Blend], VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 545 | images.image_views[Edges] = | ||
| 546 | CreateWrappedImageView(m_device, images.images[Edges], VK_FORMAT_R16G16_SFLOAT); | ||
| 547 | images.image_views[Output] = | ||
| 548 | CreateWrappedImageView(m_device, images.images[Output], VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 549 | } | ||
| 550 | } | ||
| 551 | |||
| 552 | void SMAA::CreateRenderPasses() { | ||
| 553 | m_renderpasses[EdgeDetection] = CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16_SFLOAT); | ||
| 554 | m_renderpasses[BlendingWeightCalculation] = | ||
| 555 | CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 556 | m_renderpasses[NeighborhoodBlending] = | ||
| 557 | CreateWrappedRenderPass(m_device, VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 558 | |||
| 559 | for (auto& images : m_dynamic_images) { | ||
| 560 | images.framebuffers[EdgeDetection] = CreateWrappedFramebuffer( | ||
| 561 | m_device, m_renderpasses[EdgeDetection], images.image_views[Edges], m_extent); | ||
| 562 | |||
| 563 | images.framebuffers[BlendingWeightCalculation] = | ||
| 564 | CreateWrappedFramebuffer(m_device, m_renderpasses[BlendingWeightCalculation], | ||
| 565 | images.image_views[Blend], m_extent); | ||
| 566 | |||
| 567 | images.framebuffers[NeighborhoodBlending] = CreateWrappedFramebuffer( | ||
| 568 | m_device, m_renderpasses[NeighborhoodBlending], images.image_views[Output], m_extent); | ||
| 569 | } | ||
| 570 | } | ||
| 571 | |||
| 572 | void SMAA::CreateSampler() { | ||
| 573 | m_sampler = CreateWrappedSampler(m_device); | ||
| 574 | } | ||
| 575 | |||
| 576 | void SMAA::CreateShaders() { | ||
| 577 | // These match the order of the SMAAStage enum | ||
| 578 | static constexpr std::array vert_shader_sources{ | ||
| 579 | ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_VERT_SPV), | ||
| 580 | ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_VERT_SPV), | ||
| 581 | ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_VERT_SPV), | ||
| 582 | }; | ||
| 583 | static constexpr std::array frag_shader_sources{ | ||
| 584 | ARRAY_TO_SPAN(SMAA_EDGE_DETECTION_FRAG_SPV), | ||
| 585 | ARRAY_TO_SPAN(SMAA_BLENDING_WEIGHT_CALCULATION_FRAG_SPV), | ||
| 586 | ARRAY_TO_SPAN(SMAA_NEIGHBORHOOD_BLENDING_FRAG_SPV), | ||
| 587 | }; | ||
| 588 | |||
| 589 | for (size_t i = 0; i < MaxSMAAStage; i++) { | ||
| 590 | m_vertex_shaders[i] = CreateWrappedShaderModule(m_device, vert_shader_sources[i]); | ||
| 591 | m_fragment_shaders[i] = CreateWrappedShaderModule(m_device, frag_shader_sources[i]); | ||
| 592 | } | ||
| 593 | } | ||
| 594 | |||
| 595 | void SMAA::CreateDescriptorPool() { | ||
| 596 | // Edge detection: 1 descriptor | ||
| 597 | // Blending weight calculation: 3 descriptors | ||
| 598 | // Neighborhood blending: 2 descriptors | ||
| 599 | |||
| 600 | // 6 descriptors, 3 descriptor sets per image | ||
| 601 | m_descriptor_pool = CreateWrappedDescriptorPool(m_device, 6 * m_image_count, 3 * m_image_count); | ||
| 602 | } | ||
| 603 | |||
| 604 | void SMAA::CreateDescriptorSetLayouts() { | ||
| 605 | m_descriptor_set_layouts[EdgeDetection] = CreateWrappedDescriptorSetLayout(m_device, 1); | ||
| 606 | m_descriptor_set_layouts[BlendingWeightCalculation] = | ||
| 607 | CreateWrappedDescriptorSetLayout(m_device, 3); | ||
| 608 | m_descriptor_set_layouts[NeighborhoodBlending] = CreateWrappedDescriptorSetLayout(m_device, 2); | ||
| 609 | } | ||
| 610 | |||
| 611 | void SMAA::CreateDescriptorSets() { | ||
| 612 | std::vector<VkDescriptorSetLayout> layouts(m_descriptor_set_layouts.size()); | ||
| 613 | std::ranges::transform(m_descriptor_set_layouts, layouts.begin(), | ||
| 614 | [](auto& layout) { return *layout; }); | ||
| 615 | |||
| 616 | for (auto& images : m_dynamic_images) { | ||
| 617 | images.descriptor_sets = CreateWrappedDescriptorSets(m_descriptor_pool, layouts); | ||
| 618 | } | ||
| 619 | } | ||
| 620 | |||
| 621 | void SMAA::CreatePipelineLayouts() { | ||
| 622 | for (size_t i = 0; i < MaxSMAAStage; i++) { | ||
| 623 | m_pipeline_layouts[i] = CreateWrappedPipelineLayout(m_device, m_descriptor_set_layouts[i]); | ||
| 624 | } | ||
| 625 | } | ||
| 626 | |||
| 627 | void SMAA::CreatePipelines() { | ||
| 628 | for (size_t i = 0; i < MaxSMAAStage; i++) { | ||
| 629 | m_pipelines[i] = | ||
| 630 | CreateWrappedPipeline(m_device, m_renderpasses[i], m_pipeline_layouts[i], | ||
| 631 | std::tie(m_vertex_shaders[i], m_fragment_shaders[i])); | ||
| 632 | } | ||
| 633 | } | ||
| 634 | |||
| 635 | void SMAA::UpdateDescriptorSets(VkImageView image_view, size_t image_index) { | ||
| 636 | Images& images = m_dynamic_images[image_index]; | ||
| 637 | std::vector<VkDescriptorImageInfo> image_infos; | ||
| 638 | std::vector<VkWriteDescriptorSet> updates; | ||
| 639 | image_infos.reserve(6); | ||
| 640 | |||
| 641 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, | ||
| 642 | images.descriptor_sets[EdgeDetection], 0)); | ||
| 643 | |||
| 644 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Edges], | ||
| 645 | images.descriptor_sets[BlendingWeightCalculation], | ||
| 646 | 0)); | ||
| 647 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Area], | ||
| 648 | images.descriptor_sets[BlendingWeightCalculation], | ||
| 649 | 1)); | ||
| 650 | updates.push_back( | ||
| 651 | CreateWriteDescriptorSet(image_infos, *m_sampler, *m_static_image_views[Search], | ||
| 652 | images.descriptor_sets[BlendingWeightCalculation], 2)); | ||
| 653 | |||
| 654 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, image_view, | ||
| 655 | images.descriptor_sets[NeighborhoodBlending], 0)); | ||
| 656 | updates.push_back(CreateWriteDescriptorSet(image_infos, *m_sampler, *images.image_views[Blend], | ||
| 657 | images.descriptor_sets[NeighborhoodBlending], 1)); | ||
| 658 | |||
| 659 | m_device.GetLogical().UpdateDescriptorSets(updates, {}); | ||
| 660 | } | ||
| 661 | |||
| 662 | void SMAA::UploadImages(Scheduler& scheduler) { | ||
| 663 | if (m_images_ready) { | ||
| 664 | return; | ||
| 665 | } | ||
| 666 | |||
| 667 | static constexpr VkExtent2D area_extent{AREATEX_WIDTH, AREATEX_HEIGHT}; | ||
| 668 | static constexpr VkExtent2D search_extent{SEARCHTEX_WIDTH, SEARCHTEX_HEIGHT}; | ||
| 669 | |||
| 670 | UploadImage(m_device, m_allocator, scheduler, m_static_images[Area], area_extent, | ||
| 671 | VK_FORMAT_R8G8_UNORM, ARRAY_TO_SPAN(areaTexBytes)); | ||
| 672 | UploadImage(m_device, m_allocator, scheduler, m_static_images[Search], search_extent, | ||
| 673 | VK_FORMAT_R8_UNORM, ARRAY_TO_SPAN(searchTexBytes)); | ||
| 674 | |||
| 675 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { | ||
| 676 | for (auto& images : m_dynamic_images) { | ||
| 677 | for (size_t i = 0; i < MaxDynamicImage; i++) { | ||
| 678 | ClearColorImage(cmdbuf, *images.images[i]); | ||
| 679 | } | ||
| 680 | } | ||
| 681 | }); | ||
| 682 | scheduler.Finish(); | ||
| 683 | |||
| 684 | m_images_ready = true; | ||
| 685 | } | ||
| 686 | |||
| 687 | VkImageView SMAA::Draw(Scheduler& scheduler, size_t image_index, VkImage source_image, | ||
| 688 | VkImageView source_image_view) { | ||
| 689 | Images& images = m_dynamic_images[image_index]; | ||
| 690 | |||
| 691 | VkImage output_image = *images.images[Output]; | ||
| 692 | VkImage edges_image = *images.images[Edges]; | ||
| 693 | VkImage blend_image = *images.images[Blend]; | ||
| 694 | |||
| 695 | VkDescriptorSet edge_detection_descriptor_set = images.descriptor_sets[EdgeDetection]; | ||
| 696 | VkDescriptorSet blending_weight_calculation_descriptor_set = | ||
| 697 | images.descriptor_sets[BlendingWeightCalculation]; | ||
| 698 | VkDescriptorSet neighborhood_blending_descriptor_set = | ||
| 699 | images.descriptor_sets[NeighborhoodBlending]; | ||
| 700 | |||
| 701 | VkFramebuffer edge_detection_framebuffer = *images.framebuffers[EdgeDetection]; | ||
| 702 | VkFramebuffer blending_weight_calculation_framebuffer = | ||
| 703 | *images.framebuffers[BlendingWeightCalculation]; | ||
| 704 | VkFramebuffer neighborhood_blending_framebuffer = *images.framebuffers[NeighborhoodBlending]; | ||
| 705 | |||
| 706 | UploadImages(scheduler); | ||
| 707 | UpdateDescriptorSets(source_image_view, image_index); | ||
| 708 | |||
| 709 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 710 | scheduler.Record([=, this](vk::CommandBuffer cmdbuf) { | ||
| 711 | TransitionImageLayout(cmdbuf, source_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 712 | TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 713 | BeginRenderPass(cmdbuf, m_renderpasses[EdgeDetection], edge_detection_framebuffer, | ||
| 714 | m_extent); | ||
| 715 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[EdgeDetection]); | ||
| 716 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 717 | *m_pipeline_layouts[EdgeDetection], 0, | ||
| 718 | edge_detection_descriptor_set, {}); | ||
| 719 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 720 | cmdbuf.EndRenderPass(); | ||
| 721 | |||
| 722 | TransitionImageLayout(cmdbuf, edges_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 723 | TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 724 | BeginRenderPass(cmdbuf, m_renderpasses[BlendingWeightCalculation], | ||
| 725 | blending_weight_calculation_framebuffer, m_extent); | ||
| 726 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 727 | *m_pipelines[BlendingWeightCalculation]); | ||
| 728 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 729 | *m_pipeline_layouts[BlendingWeightCalculation], 0, | ||
| 730 | blending_weight_calculation_descriptor_set, {}); | ||
| 731 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 732 | cmdbuf.EndRenderPass(); | ||
| 733 | |||
| 734 | TransitionImageLayout(cmdbuf, blend_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 735 | TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 736 | BeginRenderPass(cmdbuf, m_renderpasses[NeighborhoodBlending], | ||
| 737 | neighborhood_blending_framebuffer, m_extent); | ||
| 738 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipelines[NeighborhoodBlending]); | ||
| 739 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 740 | *m_pipeline_layouts[NeighborhoodBlending], 0, | ||
| 741 | neighborhood_blending_descriptor_set, {}); | ||
| 742 | cmdbuf.Draw(3, 1, 0, 0); | ||
| 743 | cmdbuf.EndRenderPass(); | ||
| 744 | TransitionImageLayout(cmdbuf, output_image, VK_IMAGE_LAYOUT_GENERAL); | ||
| 745 | }); | ||
| 746 | |||
| 747 | return *images.image_views[Output]; | ||
| 748 | } | ||
| 749 | |||
| 750 | } // namespace Vulkan | 636 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 7 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 8 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 9 | |||
| 10 | namespace Vulkan { | ||
| 11 | |||
| 12 | #define ARRAY_TO_SPAN(a) std::span(a, (sizeof(a) / sizeof(a[0]))) | ||
| 13 | |||
| 14 | vk::Buffer CreateWrappedBuffer(MemoryAllocator& allocator, VkDeviceSize size, MemoryUsage usage); | ||
| 15 | |||
| 16 | vk::Image CreateWrappedImage(MemoryAllocator& allocator, VkExtent2D dimensions, VkFormat format); | ||
| 17 | void TransitionImageLayout(vk::CommandBuffer& cmdbuf, VkImage image, VkImageLayout target_layout, | ||
| 18 | VkImageLayout source_layout = VK_IMAGE_LAYOUT_GENERAL); | ||
| 19 | void UploadImage(const Device& device, MemoryAllocator& allocator, Scheduler& scheduler, | ||
| 20 | vk::Image& image, VkExtent2D dimensions, VkFormat format, | ||
| 21 | std::span<const u8> initial_contents = {}); | ||
| 22 | void DownloadColorImage(vk::CommandBuffer& cmdbuf, VkImage image, VkBuffer buffer, | ||
| 23 | VkExtent3D extent); | ||
| 24 | void ClearColorImage(vk::CommandBuffer& cmdbuf, VkImage image); | ||
| 25 | |||
| 26 | vk::ImageView CreateWrappedImageView(const Device& device, vk::Image& image, VkFormat format); | ||
| 27 | vk::RenderPass CreateWrappedRenderPass(const Device& device, VkFormat format, | ||
| 28 | VkImageLayout initial_layout = VK_IMAGE_LAYOUT_GENERAL); | ||
| 29 | vk::Framebuffer CreateWrappedFramebuffer(const Device& device, vk::RenderPass& render_pass, | ||
| 30 | vk::ImageView& dest_image, VkExtent2D extent); | ||
| 31 | vk::Sampler CreateWrappedSampler(const Device& device, VkFilter filter = VK_FILTER_LINEAR); | ||
| 32 | vk::ShaderModule CreateWrappedShaderModule(const Device& device, std::span<const u32> code); | ||
| 33 | vk::DescriptorPool CreateWrappedDescriptorPool(const Device& device, size_t max_descriptors, | ||
| 34 | size_t max_sets, | ||
| 35 | std::initializer_list<VkDescriptorType> types = { | ||
| 36 | VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||
| 37 | vk::DescriptorSetLayout CreateWrappedDescriptorSetLayout( | ||
| 38 | const Device& device, std::initializer_list<VkDescriptorType> types); | ||
| 39 | vk::DescriptorSets CreateWrappedDescriptorSets(vk::DescriptorPool& pool, | ||
| 40 | vk::Span<VkDescriptorSetLayout> layouts); | ||
| 41 | vk::PipelineLayout CreateWrappedPipelineLayout(const Device& device, | ||
| 42 | vk::DescriptorSetLayout& layout); | ||
| 43 | vk::Pipeline CreateWrappedPipeline(const Device& device, vk::RenderPass& renderpass, | ||
| 44 | vk::PipelineLayout& layout, | ||
| 45 | std::tuple<vk::ShaderModule&, vk::ShaderModule&> shaders, | ||
| 46 | bool enable_blending = false); | ||
| 47 | VkWriteDescriptorSet CreateWriteDescriptorSet(std::vector<VkDescriptorImageInfo>& images, | ||
| 48 | VkSampler sampler, VkImageView view, | ||
| 49 | VkDescriptorSet set, u32 binding); | ||
| 50 | vk::Sampler CreateBilinearSampler(const Device& device); | ||
| 51 | vk::Sampler CreateNearestNeighborSampler(const Device& device); | ||
| 52 | |||
| 53 | void BeginRenderPass(vk::CommandBuffer& cmdbuf, VkRenderPass render_pass, VkFramebuffer framebuffer, | ||
| 54 | VkExtent2D extent); | ||
| 55 | |||
| 56 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "core/frontend/framebuffer_layout.h" | ||
| 5 | #include "video_core/framebuffer_config.h" | ||
| 6 | #include "video_core/host_shaders/vulkan_present_vert_spv.h" | ||
| 7 | #include "video_core/renderer_vulkan/present/layer.h" | ||
| 8 | #include "video_core/renderer_vulkan/present/present_push_constants.h" | ||
| 9 | #include "video_core/renderer_vulkan/present/util.h" | ||
| 10 | #include "video_core/renderer_vulkan/present/window_adapt_pass.h" | ||
| 11 | #include "video_core/renderer_vulkan/vk_present_manager.h" | ||
| 12 | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||
| 13 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 14 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 15 | |||
| 16 | namespace Vulkan { | ||
| 17 | |||
| 18 | WindowAdaptPass::WindowAdaptPass(const Device& device_, VkFormat frame_format, | ||
| 19 | vk::Sampler&& sampler_, vk::ShaderModule&& fragment_shader_) | ||
| 20 | : device(device_), sampler(std::move(sampler_)), fragment_shader(std::move(fragment_shader_)) { | ||
| 21 | CreateDescriptorSetLayout(); | ||
| 22 | CreatePipelineLayout(); | ||
| 23 | CreateVertexShader(); | ||
| 24 | CreateRenderPass(frame_format); | ||
| 25 | CreatePipeline(); | ||
| 26 | } | ||
| 27 | |||
| 28 | WindowAdaptPass::~WindowAdaptPass() = default; | ||
| 29 | |||
| 30 | void WindowAdaptPass::Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, | ||
| 31 | std::list<Layer>& layers, | ||
| 32 | std::span<const Tegra::FramebufferConfig> configs, | ||
| 33 | const Layout::FramebufferLayout& layout, Frame* dst) { | ||
| 34 | |||
| 35 | const VkFramebuffer host_framebuffer{*dst->framebuffer}; | ||
| 36 | const VkRenderPass renderpass{*render_pass}; | ||
| 37 | const VkPipeline graphics_pipeline{*pipeline}; | ||
| 38 | const VkPipelineLayout graphics_pipeline_layout{*pipeline_layout}; | ||
| 39 | const VkExtent2D render_area{ | ||
| 40 | .width = dst->width, | ||
| 41 | .height = dst->height, | ||
| 42 | }; | ||
| 43 | |||
| 44 | const size_t layer_count = configs.size(); | ||
| 45 | std::vector<PresentPushConstants> push_constants(layer_count); | ||
| 46 | std::vector<VkDescriptorSet> descriptor_sets(layer_count); | ||
| 47 | |||
| 48 | auto layer_it = layers.begin(); | ||
| 49 | for (size_t i = 0; i < layer_count; i++) { | ||
| 50 | layer_it->ConfigureDraw(&push_constants[i], &descriptor_sets[i], rasterizer, *sampler, | ||
| 51 | image_index, configs[i], layout); | ||
| 52 | layer_it++; | ||
| 53 | } | ||
| 54 | |||
| 55 | scheduler.Record([=](vk::CommandBuffer cmdbuf) { | ||
| 56 | const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; | ||
| 57 | const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; | ||
| 58 | const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; | ||
| 59 | const VkClearAttachment clear_attachment{ | ||
| 60 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 61 | .colorAttachment = 0, | ||
| 62 | .clearValue = | ||
| 63 | { | ||
| 64 | .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, | ||
| 65 | }, | ||
| 66 | }; | ||
| 67 | const VkClearRect clear_rect{ | ||
| 68 | .rect = | ||
| 69 | { | ||
| 70 | .offset = {0, 0}, | ||
| 71 | .extent = render_area, | ||
| 72 | }, | ||
| 73 | .baseArrayLayer = 0, | ||
| 74 | .layerCount = 1, | ||
| 75 | }; | ||
| 76 | |||
| 77 | BeginRenderPass(cmdbuf, renderpass, host_framebuffer, render_area); | ||
| 78 | cmdbuf.ClearAttachments({clear_attachment}, {clear_rect}); | ||
| 79 | |||
| 80 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); | ||
| 81 | for (size_t i = 0; i < layer_count; i++) { | ||
| 82 | cmdbuf.PushConstants(graphics_pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT, | ||
| 83 | push_constants[i]); | ||
| 84 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline_layout, 0, | ||
| 85 | descriptor_sets[i], {}); | ||
| 86 | cmdbuf.Draw(4, 1, 0, 0); | ||
| 87 | } | ||
| 88 | |||
| 89 | cmdbuf.EndRenderPass(); | ||
| 90 | }); | ||
| 91 | } | ||
| 92 | |||
| 93 | VkDescriptorSetLayout WindowAdaptPass::GetDescriptorSetLayout() { | ||
| 94 | return *descriptor_set_layout; | ||
| 95 | } | ||
| 96 | |||
| 97 | VkRenderPass WindowAdaptPass::GetRenderPass() { | ||
| 98 | return *render_pass; | ||
| 99 | } | ||
| 100 | |||
| 101 | void WindowAdaptPass::CreateDescriptorSetLayout() { | ||
| 102 | descriptor_set_layout = | ||
| 103 | CreateWrappedDescriptorSetLayout(device, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER}); | ||
| 104 | } | ||
| 105 | |||
| 106 | void WindowAdaptPass::CreatePipelineLayout() { | ||
| 107 | const VkPushConstantRange range{ | ||
| 108 | .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 109 | .offset = 0, | ||
| 110 | .size = sizeof(PresentPushConstants), | ||
| 111 | }; | ||
| 112 | |||
| 113 | pipeline_layout = device.GetLogical().CreatePipelineLayout(VkPipelineLayoutCreateInfo{ | ||
| 114 | .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||
| 115 | .pNext = nullptr, | ||
| 116 | .flags = 0, | ||
| 117 | .setLayoutCount = 1, | ||
| 118 | .pSetLayouts = descriptor_set_layout.address(), | ||
| 119 | .pushConstantRangeCount = 1, | ||
| 120 | .pPushConstantRanges = &range, | ||
| 121 | }); | ||
| 122 | } | ||
| 123 | |||
| 124 | void WindowAdaptPass::CreateVertexShader() { | ||
| 125 | vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); | ||
| 126 | } | ||
| 127 | |||
| 128 | void WindowAdaptPass::CreateRenderPass(VkFormat frame_format) { | ||
| 129 | render_pass = CreateWrappedRenderPass(device, frame_format, VK_IMAGE_LAYOUT_UNDEFINED); | ||
| 130 | } | ||
| 131 | |||
| 132 | void WindowAdaptPass::CreatePipeline() { | ||
| 133 | pipeline = CreateWrappedPipeline(device, render_pass, pipeline_layout, | ||
| 134 | std::tie(vertex_shader, fragment_shader), false); | ||
| 135 | } | ||
| 136 | |||
| 137 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2024 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include <list> | ||
| 7 | |||
| 8 | #include "common/math_util.h" | ||
| 9 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 10 | |||
| 11 | namespace Layout { | ||
| 12 | struct FramebufferLayout; | ||
| 13 | } | ||
| 14 | |||
| 15 | namespace Tegra { | ||
| 16 | struct FramebufferConfig; | ||
| 17 | } | ||
| 18 | |||
| 19 | namespace Vulkan { | ||
| 20 | |||
| 21 | class Device; | ||
| 22 | struct Frame; | ||
| 23 | class Layer; | ||
| 24 | class Scheduler; | ||
| 25 | class RasterizerVulkan; | ||
| 26 | |||
| 27 | class WindowAdaptPass final { | ||
| 28 | public: | ||
| 29 | explicit WindowAdaptPass(const Device& device, VkFormat frame_format, vk::Sampler&& sampler, | ||
| 30 | vk::ShaderModule&& fragment_shader); | ||
| 31 | ~WindowAdaptPass(); | ||
| 32 | |||
| 33 | void Draw(RasterizerVulkan& rasterizer, Scheduler& scheduler, size_t image_index, | ||
| 34 | std::list<Layer>& layers, std::span<const Tegra::FramebufferConfig> configs, | ||
| 35 | const Layout::FramebufferLayout& layout, Frame* dst); | ||
| 36 | |||
| 37 | VkDescriptorSetLayout GetDescriptorSetLayout(); | ||
| 38 | VkRenderPass GetRenderPass(); | ||
| 39 | |||
| 40 | private: | ||
| 41 | void CreateDescriptorSetLayout(); | ||
| 42 | void CreatePipelineLayout(); | ||
| 43 | void CreateVertexShader(); | ||
| 44 | void CreateRenderPass(VkFormat frame_format); | ||
| 45 | void CreatePipeline(); | ||
| 46 | |||
| 47 | private: | ||
| 48 | const Device& device; | ||
| 49 | vk::DescriptorSetLayout descriptor_set_layout; | ||
| 50 | vk::PipelineLayout pipeline_layout; | ||
| 51 | vk::Sampler sampler; | ||
| 52 | vk::ShaderModule vertex_shader; | ||
| 53 | vk::ShaderModule fragment_shader; | ||
| 54 | vk::RenderPass render_pass; | ||
| 55 | vk::Pipeline pipeline; | ||
| 56 | }; | ||
| 57 | |||
| 58 | } // 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 @@ | |||
| 20 | #include "core/frontend/graphics_context.h" | 20 | #include "core/frontend/graphics_context.h" |
| 21 | #include "core/telemetry_session.h" | 21 | #include "core/telemetry_session.h" |
| 22 | #include "video_core/gpu.h" | 22 | #include "video_core/gpu.h" |
| 23 | #include "video_core/renderer_vulkan/present/util.h" | ||
| 23 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | 24 | #include "video_core/renderer_vulkan/renderer_vulkan.h" |
| 24 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | 25 | #include "video_core/renderer_vulkan/vk_blit_screen.h" |
| 25 | #include "video_core/renderer_vulkan/vk_rasterizer.h" | 26 | #include "video_core/renderer_vulkan/vk_rasterizer.h" |
| 26 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 27 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 27 | #include "video_core/renderer_vulkan/vk_state_tracker.h" | 28 | #include "video_core/renderer_vulkan/vk_state_tracker.h" |
| 28 | #include "video_core/renderer_vulkan/vk_swapchain.h" | 29 | #include "video_core/renderer_vulkan/vk_swapchain.h" |
| 30 | #include "video_core/textures/decoders.h" | ||
| 29 | #include "video_core/vulkan_common/vulkan_debug_callback.h" | 31 | #include "video_core/vulkan_common/vulkan_debug_callback.h" |
| 30 | #include "video_core/vulkan_common/vulkan_device.h" | 32 | #include "video_core/vulkan_common/vulkan_device.h" |
| 31 | #include "video_core/vulkan_common/vulkan_instance.h" | 33 | #include "video_core/vulkan_common/vulkan_instance.h" |
| @@ -97,10 +99,10 @@ RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_, | |||
| 97 | render_window.GetFramebufferLayout().height), | 99 | render_window.GetFramebufferLayout().height), |
| 98 | present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, | 100 | present_manager(instance, render_window, device, memory_allocator, scheduler, swapchain, |
| 99 | surface), | 101 | surface), |
| 100 | blit_screen(device_memory, render_window, device, memory_allocator, swapchain, | 102 | blit_swapchain(device_memory, device, memory_allocator, present_manager, scheduler), |
| 101 | present_manager, scheduler, screen_info), | 103 | blit_screenshot(device_memory, device, memory_allocator, present_manager, scheduler), |
| 102 | rasterizer(render_window, gpu, device_memory, screen_info, device, memory_allocator, | 104 | rasterizer(render_window, gpu, device_memory, device, memory_allocator, state_tracker, |
| 103 | state_tracker, scheduler) { | 105 | scheduler) { |
| 104 | if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { | 106 | if (Settings::values.renderer_force_max_clock.GetValue() && device.ShouldBoostClocks()) { |
| 105 | turbo_mode.emplace(instance, dld); | 107 | turbo_mode.emplace(instance, dld); |
| 106 | scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); | 108 | scheduler.RegisterOnSubmit([this] { turbo_mode->QueueSubmitted(); }); |
| @@ -116,25 +118,22 @@ RendererVulkan::~RendererVulkan() { | |||
| 116 | void(device.GetLogical().WaitIdle()); | 118 | void(device.GetLogical().WaitIdle()); |
| 117 | } | 119 | } |
| 118 | 120 | ||
| 119 | void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) { | 121 | void RendererVulkan::Composite(std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 120 | if (!framebuffer) { | 122 | if (framebuffers.empty()) { |
| 121 | return; | 123 | return; |
| 122 | } | 124 | } |
| 125 | |||
| 123 | SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); | 126 | SCOPE_EXIT({ render_window.OnFrameDisplayed(); }); |
| 127 | |||
| 124 | if (!render_window.IsShown()) { | 128 | if (!render_window.IsShown()) { |
| 125 | return; | 129 | return; |
| 126 | } | 130 | } |
| 127 | // Update screen info if the framebuffer size has changed. | ||
| 128 | screen_info.width = framebuffer->width; | ||
| 129 | screen_info.height = framebuffer->height; | ||
| 130 | |||
| 131 | const DAddr framebuffer_addr = framebuffer->address + framebuffer->offset; | ||
| 132 | const bool use_accelerated = | ||
| 133 | rasterizer.AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride); | ||
| 134 | RenderScreenshot(*framebuffer, use_accelerated); | ||
| 135 | 131 | ||
| 132 | RenderScreenshot(framebuffers); | ||
| 136 | Frame* frame = present_manager.GetRenderFrame(); | 133 | Frame* frame = present_manager.GetRenderFrame(); |
| 137 | blit_screen.DrawToSwapchain(frame, *framebuffer, use_accelerated); | 134 | blit_swapchain.DrawToFrame(rasterizer, frame, framebuffers, |
| 135 | render_window.GetFramebufferLayout(), swapchain.GetImageCount(), | ||
| 136 | swapchain.GetImageViewFormat()); | ||
| 138 | scheduler.Flush(*frame->render_ready); | 137 | scheduler.Flush(*frame->render_ready); |
| 139 | present_manager.Present(frame); | 138 | present_manager.Present(frame); |
| 140 | 139 | ||
| @@ -168,143 +167,37 @@ void RendererVulkan::Report() const { | |||
| 168 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); | 167 | telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions); |
| 169 | } | 168 | } |
| 170 | 169 | ||
| 171 | void Vulkan::RendererVulkan::RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, | 170 | void Vulkan::RendererVulkan::RenderScreenshot( |
| 172 | bool use_accelerated) { | 171 | std::span<const Tegra::FramebufferConfig> framebuffers) { |
| 173 | if (!renderer_settings.screenshot_requested) { | 172 | if (!renderer_settings.screenshot_requested) { |
| 174 | return; | 173 | return; |
| 175 | } | 174 | } |
| 175 | |||
| 176 | constexpr VkFormat ScreenshotFormat{VK_FORMAT_B8G8R8A8_UNORM}; | ||
| 176 | const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; | 177 | const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout}; |
| 177 | vk::Image staging_image = memory_allocator.CreateImage(VkImageCreateInfo{ | ||
| 178 | .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | ||
| 179 | .pNext = nullptr, | ||
| 180 | .flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT, | ||
| 181 | .imageType = VK_IMAGE_TYPE_2D, | ||
| 182 | .format = VK_FORMAT_B8G8R8A8_UNORM, | ||
| 183 | .extent = | ||
| 184 | { | ||
| 185 | .width = layout.width, | ||
| 186 | .height = layout.height, | ||
| 187 | .depth = 1, | ||
| 188 | }, | ||
| 189 | .mipLevels = 1, | ||
| 190 | .arrayLayers = 1, | ||
| 191 | .samples = VK_SAMPLE_COUNT_1_BIT, | ||
| 192 | .tiling = VK_IMAGE_TILING_OPTIMAL, | ||
| 193 | .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | | ||
| 194 | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, | ||
| 195 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 196 | .queueFamilyIndexCount = 0, | ||
| 197 | .pQueueFamilyIndices = nullptr, | ||
| 198 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||
| 199 | }); | ||
| 200 | 178 | ||
| 201 | const vk::ImageView dst_view = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | 179 | auto frame = [&]() { |
| 202 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | 180 | Frame f{}; |
| 203 | .pNext = nullptr, | 181 | f.image = CreateWrappedImage(memory_allocator, VkExtent2D{layout.width, layout.height}, |
| 204 | .flags = 0, | 182 | ScreenshotFormat); |
| 205 | .image = *staging_image, | 183 | f.image_view = CreateWrappedImageView(device, f.image, ScreenshotFormat); |
| 206 | .viewType = VK_IMAGE_VIEW_TYPE_2D, | 184 | f.framebuffer = blit_screenshot.CreateFramebuffer(layout, *f.image_view, ScreenshotFormat); |
| 207 | .format = VK_FORMAT_B8G8R8A8_UNORM, | 185 | return f; |
| 208 | .components{ | 186 | }(); |
| 209 | .r = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 210 | .g = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 211 | .b = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 212 | .a = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 213 | }, | ||
| 214 | .subresourceRange{ | ||
| 215 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 216 | .baseMipLevel = 0, | ||
| 217 | .levelCount = 1, | ||
| 218 | .baseArrayLayer = 0, | ||
| 219 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 220 | }, | ||
| 221 | }); | ||
| 222 | const VkExtent2D render_area{.width = layout.width, .height = layout.height}; | ||
| 223 | const vk::Framebuffer screenshot_fb = blit_screen.CreateFramebuffer(*dst_view, render_area); | ||
| 224 | blit_screen.Draw(framebuffer, *screenshot_fb, layout, render_area, use_accelerated); | ||
| 225 | 187 | ||
| 226 | const auto buffer_size = static_cast<VkDeviceSize>(layout.width * layout.height * 4); | 188 | blit_screenshot.DrawToFrame(rasterizer, &frame, framebuffers, layout, 1, |
| 227 | const VkBufferCreateInfo dst_buffer_info{ | 189 | VK_FORMAT_B8G8R8A8_UNORM); |
| 228 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | 190 | |
| 229 | .pNext = nullptr, | 191 | const auto dst_buffer = CreateWrappedBuffer( |
| 230 | .flags = 0, | 192 | memory_allocator, static_cast<VkDeviceSize>(layout.width * layout.height * 4), |
| 231 | .size = buffer_size, | 193 | MemoryUsage::Download); |
| 232 | .usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT, | ||
| 233 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 234 | .queueFamilyIndexCount = 0, | ||
| 235 | .pQueueFamilyIndices = nullptr, | ||
| 236 | }; | ||
| 237 | const vk::Buffer dst_buffer = | ||
| 238 | memory_allocator.CreateBuffer(dst_buffer_info, MemoryUsage::Download); | ||
| 239 | 194 | ||
| 240 | scheduler.RequestOutsideRenderPassOperationContext(); | 195 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 241 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { | 196 | scheduler.Record([&](vk::CommandBuffer cmdbuf) { |
| 242 | const VkImageMemoryBarrier read_barrier{ | 197 | DownloadColorImage(cmdbuf, *frame.image, *dst_buffer, |
| 243 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | 198 | VkExtent3D{layout.width, layout.height, 1}); |
| 244 | .pNext = nullptr, | ||
| 245 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 246 | .dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT, | ||
| 247 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 248 | .newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 249 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 250 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 251 | .image = *staging_image, | ||
| 252 | .subresourceRange{ | ||
| 253 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 254 | .baseMipLevel = 0, | ||
| 255 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 256 | .baseArrayLayer = 0, | ||
| 257 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 258 | }, | ||
| 259 | }; | ||
| 260 | const VkImageMemoryBarrier image_write_barrier{ | ||
| 261 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 262 | .pNext = nullptr, | ||
| 263 | .srcAccessMask = 0, | ||
| 264 | .dstAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 265 | .oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, | ||
| 266 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 267 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 268 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 269 | .image = *staging_image, | ||
| 270 | .subresourceRange{ | ||
| 271 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 272 | .baseMipLevel = 0, | ||
| 273 | .levelCount = VK_REMAINING_MIP_LEVELS, | ||
| 274 | .baseArrayLayer = 0, | ||
| 275 | .layerCount = VK_REMAINING_ARRAY_LAYERS, | ||
| 276 | }, | ||
| 277 | }; | ||
| 278 | static constexpr VkMemoryBarrier memory_write_barrier{ | ||
| 279 | .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER, | ||
| 280 | .pNext = nullptr, | ||
| 281 | .srcAccessMask = VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 282 | .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT | VK_ACCESS_MEMORY_WRITE_BIT, | ||
| 283 | }; | ||
| 284 | const VkBufferImageCopy copy{ | ||
| 285 | .bufferOffset = 0, | ||
| 286 | .bufferRowLength = 0, | ||
| 287 | .bufferImageHeight = 0, | ||
| 288 | .imageSubresource{ | ||
| 289 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 290 | .mipLevel = 0, | ||
| 291 | .baseArrayLayer = 0, | ||
| 292 | .layerCount = 1, | ||
| 293 | }, | ||
| 294 | .imageOffset{.x = 0, .y = 0, .z = 0}, | ||
| 295 | .imageExtent{ | ||
| 296 | .width = layout.width, | ||
| 297 | .height = layout.height, | ||
| 298 | .depth = 1, | ||
| 299 | }, | ||
| 300 | }; | ||
| 301 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, | ||
| 302 | 0, read_barrier); | ||
| 303 | cmdbuf.CopyImageToBuffer(*staging_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, *dst_buffer, | ||
| 304 | copy); | ||
| 305 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 306 | 0, memory_write_barrier, nullptr, image_write_barrier); | ||
| 307 | }); | 199 | }); |
| 200 | |||
| 308 | // Ensure the copy is fully completed before saving the screenshot | 201 | // Ensure the copy is fully completed before saving the screenshot |
| 309 | scheduler.Finish(); | 202 | scheduler.Finish(); |
| 310 | 203 | ||
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: | |||
| 46 | std::unique_ptr<Core::Frontend::GraphicsContext> context_); | 46 | std::unique_ptr<Core::Frontend::GraphicsContext> context_); |
| 47 | ~RendererVulkan() override; | 47 | ~RendererVulkan() override; |
| 48 | 48 | ||
| 49 | void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override; | 49 | void Composite(std::span<const Tegra::FramebufferConfig> framebuffers) override; |
| 50 | 50 | ||
| 51 | VideoCore::RasterizerInterface* ReadRasterizer() override { | 51 | VideoCore::RasterizerInterface* ReadRasterizer() override { |
| 52 | return &rasterizer; | 52 | return &rasterizer; |
| @@ -59,7 +59,7 @@ public: | |||
| 59 | private: | 59 | private: |
| 60 | void Report() const; | 60 | void Report() const; |
| 61 | 61 | ||
| 62 | void RenderScreenshot(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated); | 62 | void RenderScreenshot(std::span<const Tegra::FramebufferConfig> framebuffers); |
| 63 | 63 | ||
| 64 | Core::TelemetrySession& telemetry_session; | 64 | Core::TelemetrySession& telemetry_session; |
| 65 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 65 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| @@ -72,15 +72,14 @@ private: | |||
| 72 | vk::DebugUtilsMessenger debug_messenger; | 72 | vk::DebugUtilsMessenger debug_messenger; |
| 73 | vk::SurfaceKHR surface; | 73 | vk::SurfaceKHR surface; |
| 74 | 74 | ||
| 75 | ScreenInfo screen_info; | ||
| 76 | |||
| 77 | Device device; | 75 | Device device; |
| 78 | MemoryAllocator memory_allocator; | 76 | MemoryAllocator memory_allocator; |
| 79 | StateTracker state_tracker; | 77 | StateTracker state_tracker; |
| 80 | Scheduler scheduler; | 78 | Scheduler scheduler; |
| 81 | Swapchain swapchain; | 79 | Swapchain swapchain; |
| 82 | PresentManager present_manager; | 80 | PresentManager present_manager; |
| 83 | BlitScreen blit_screen; | 81 | BlitScreen blit_swapchain; |
| 82 | BlitScreen blit_screenshot; | ||
| 84 | RasterizerVulkan rasterizer; | 83 | RasterizerVulkan rasterizer; |
| 85 | std::optional<TurboMode> turbo_mode; | 84 | std::optional<TurboMode> turbo_mode; |
| 86 | }; | 85 | }; |
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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include <algorithm> | 4 | #include "video_core/framebuffer_config.h" |
| 5 | #include <array> | 5 | #include "video_core/renderer_vulkan/present/filters.h" |
| 6 | #include <cstring> | 6 | #include "video_core/renderer_vulkan/present/layer.h" |
| 7 | #include <memory> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "common/assert.h" | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/math_util.h" | ||
| 13 | #include "common/polyfill_ranges.h" | ||
| 14 | #include "common/settings.h" | ||
| 15 | #include "core/core.h" | ||
| 16 | #include "core/frontend/emu_window.h" | ||
| 17 | #include "video_core/gpu.h" | ||
| 18 | #include "video_core/host1x/gpu_device_memory_manager.h" | ||
| 19 | #include "video_core/host_shaders/fxaa_frag_spv.h" | ||
| 20 | #include "video_core/host_shaders/fxaa_vert_spv.h" | ||
| 21 | #include "video_core/host_shaders/present_bicubic_frag_spv.h" | ||
| 22 | #include "video_core/host_shaders/present_gaussian_frag_spv.h" | ||
| 23 | #include "video_core/host_shaders/vulkan_present_frag_spv.h" | ||
| 24 | #include "video_core/host_shaders/vulkan_present_scaleforce_fp16_frag_spv.h" | ||
| 25 | #include "video_core/host_shaders/vulkan_present_scaleforce_fp32_frag_spv.h" | ||
| 26 | #include "video_core/host_shaders/vulkan_present_vert_spv.h" | ||
| 27 | #include "video_core/renderer_vulkan/renderer_vulkan.h" | ||
| 28 | #include "video_core/renderer_vulkan/vk_blit_screen.h" | 7 | #include "video_core/renderer_vulkan/vk_blit_screen.h" |
| 29 | #include "video_core/renderer_vulkan/vk_fsr.h" | 8 | #include "video_core/renderer_vulkan/vk_present_manager.h" |
| 30 | #include "video_core/renderer_vulkan/vk_scheduler.h" | 9 | #include "video_core/renderer_vulkan/vk_scheduler.h" |
| 31 | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||
| 32 | #include "video_core/renderer_vulkan/vk_smaa.h" | ||
| 33 | #include "video_core/renderer_vulkan/vk_swapchain.h" | ||
| 34 | #include "video_core/surface.h" | ||
| 35 | #include "video_core/textures/decoders.h" | ||
| 36 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 37 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 38 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 39 | 10 | ||
| 40 | namespace Vulkan { | 11 | namespace Vulkan { |
| 41 | 12 | ||
| 42 | namespace { | 13 | BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, const Device& device_, |
| 43 | 14 | MemoryAllocator& memory_allocator_, PresentManager& present_manager_, | |
| 44 | struct ScreenRectVertex { | 15 | Scheduler& scheduler_) |
| 45 | ScreenRectVertex() = default; | 16 | : device_memory{device_memory_}, device{device_}, memory_allocator{memory_allocator_}, |
| 46 | explicit ScreenRectVertex(f32 x, f32 y, f32 u, f32 v) : position{{x, y}}, tex_coord{{u, v}} {} | 17 | present_manager{present_manager_}, scheduler{scheduler_}, image_count{1}, |
| 47 | 18 | swapchain_view_format{VK_FORMAT_B8G8R8A8_UNORM} {} | |
| 48 | std::array<f32, 2> position; | ||
| 49 | std::array<f32, 2> tex_coord; | ||
| 50 | |||
| 51 | static VkVertexInputBindingDescription GetDescription() { | ||
| 52 | return { | ||
| 53 | .binding = 0, | ||
| 54 | .stride = sizeof(ScreenRectVertex), | ||
| 55 | .inputRate = VK_VERTEX_INPUT_RATE_VERTEX, | ||
| 56 | }; | ||
| 57 | } | ||
| 58 | |||
| 59 | static std::array<VkVertexInputAttributeDescription, 2> GetAttributes() { | ||
| 60 | return {{ | ||
| 61 | { | ||
| 62 | .location = 0, | ||
| 63 | .binding = 0, | ||
| 64 | .format = VK_FORMAT_R32G32_SFLOAT, | ||
| 65 | .offset = offsetof(ScreenRectVertex, position), | ||
| 66 | }, | ||
| 67 | { | ||
| 68 | .location = 1, | ||
| 69 | .binding = 0, | ||
| 70 | .format = VK_FORMAT_R32G32_SFLOAT, | ||
| 71 | .offset = offsetof(ScreenRectVertex, tex_coord), | ||
| 72 | }, | ||
| 73 | }}; | ||
| 74 | } | ||
| 75 | }; | ||
| 76 | 19 | ||
| 77 | std::array<f32, 4 * 4> MakeOrthographicMatrix(f32 width, f32 height) { | 20 | BlitScreen::~BlitScreen() = default; |
| 78 | // clang-format off | ||
| 79 | return { 2.f / width, 0.f, 0.f, 0.f, | ||
| 80 | 0.f, 2.f / height, 0.f, 0.f, | ||
| 81 | 0.f, 0.f, 1.f, 0.f, | ||
| 82 | -1.f, -1.f, 0.f, 1.f}; | ||
| 83 | // clang-format on | ||
| 84 | } | ||
| 85 | |||
| 86 | u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { | ||
| 87 | using namespace VideoCore::Surface; | ||
| 88 | return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)); | ||
| 89 | } | ||
| 90 | 21 | ||
| 91 | std::size_t GetSizeInBytes(const Tegra::FramebufferConfig& framebuffer) { | 22 | void BlitScreen::WaitIdle() { |
| 92 | return static_cast<std::size_t>(framebuffer.stride) * | 23 | present_manager.WaitPresent(); |
| 93 | static_cast<std::size_t>(framebuffer.height) * GetBytesPerPixel(framebuffer); | 24 | scheduler.Finish(); |
| 25 | device.GetLogical().WaitIdle(); | ||
| 94 | } | 26 | } |
| 95 | 27 | ||
| 96 | VkFormat GetFormat(const Tegra::FramebufferConfig& framebuffer) { | 28 | void BlitScreen::SetWindowAdaptPass() { |
| 97 | switch (framebuffer.pixel_format) { | 29 | layers.clear(); |
| 98 | case Service::android::PixelFormat::Rgba8888: | 30 | scaling_filter = Settings::values.scaling_filter.GetValue(); |
| 99 | case Service::android::PixelFormat::Rgbx8888: | 31 | |
| 100 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | 32 | switch (scaling_filter) { |
| 101 | case Service::android::PixelFormat::Rgb565: | 33 | case Settings::ScalingFilter::NearestNeighbor: |
| 102 | return VK_FORMAT_R5G6B5_UNORM_PACK16; | 34 | window_adapt = MakeNearestNeighbor(device, swapchain_view_format); |
| 103 | case Service::android::PixelFormat::Bgra8888: | 35 | break; |
| 104 | return VK_FORMAT_B8G8R8A8_UNORM; | 36 | case Settings::ScalingFilter::Bicubic: |
| 37 | window_adapt = MakeBicubic(device, swapchain_view_format); | ||
| 38 | break; | ||
| 39 | case Settings::ScalingFilter::Gaussian: | ||
| 40 | window_adapt = MakeGaussian(device, swapchain_view_format); | ||
| 41 | break; | ||
| 42 | case Settings::ScalingFilter::ScaleForce: | ||
| 43 | window_adapt = MakeScaleForce(device, swapchain_view_format); | ||
| 44 | break; | ||
| 45 | case Settings::ScalingFilter::Fsr: | ||
| 46 | case Settings::ScalingFilter::Bilinear: | ||
| 105 | default: | 47 | default: |
| 106 | UNIMPLEMENTED_MSG("Unknown framebuffer pixel format: {}", | 48 | window_adapt = MakeBilinear(device, swapchain_view_format); |
| 107 | static_cast<u32>(framebuffer.pixel_format)); | 49 | break; |
| 108 | return VK_FORMAT_A8B8G8R8_UNORM_PACK32; | ||
| 109 | } | 50 | } |
| 110 | } | 51 | } |
| 111 | 52 | ||
| 112 | } // Anonymous namespace | 53 | void BlitScreen::DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, |
| 113 | 54 | std::span<const Tegra::FramebufferConfig> framebuffers, | |
| 114 | struct BlitScreen::BufferData { | 55 | const Layout::FramebufferLayout& layout, |
| 115 | struct { | 56 | size_t current_swapchain_image_count, |
| 116 | std::array<f32, 4 * 4> modelview_matrix; | 57 | VkFormat current_swapchain_view_format) { |
| 117 | } uniform; | 58 | bool resource_update_required = false; |
| 118 | 59 | bool presentation_recreate_required = false; | |
| 119 | std::array<ScreenRectVertex, 4> vertices; | ||
| 120 | |||
| 121 | // Unaligned image data goes here | ||
| 122 | }; | ||
| 123 | |||
| 124 | BlitScreen::BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory_, | ||
| 125 | Core::Frontend::EmuWindow& render_window_, const Device& device_, | ||
| 126 | MemoryAllocator& memory_allocator_, Swapchain& swapchain_, | ||
| 127 | PresentManager& present_manager_, Scheduler& scheduler_, | ||
| 128 | const ScreenInfo& screen_info_) | ||
| 129 | : device_memory{device_memory_}, render_window{render_window_}, device{device_}, | ||
| 130 | memory_allocator{memory_allocator_}, swapchain{swapchain_}, present_manager{present_manager_}, | ||
| 131 | scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} { | ||
| 132 | resource_ticks.resize(image_count); | ||
| 133 | swapchain_view_format = swapchain.GetImageViewFormat(); | ||
| 134 | |||
| 135 | CreateStaticResources(); | ||
| 136 | CreateDynamicResources(); | ||
| 137 | } | ||
| 138 | |||
| 139 | BlitScreen::~BlitScreen() = default; | ||
| 140 | |||
| 141 | static Common::Rectangle<f32> NormalizeCrop(const Tegra::FramebufferConfig& framebuffer, | ||
| 142 | const ScreenInfo& screen_info) { | ||
| 143 | f32 left, top, right, bottom; | ||
| 144 | |||
| 145 | if (!framebuffer.crop_rect.IsEmpty()) { | ||
| 146 | // If crop rectangle is not empty, apply properties from rectangle. | ||
| 147 | left = static_cast<f32>(framebuffer.crop_rect.left); | ||
| 148 | top = static_cast<f32>(framebuffer.crop_rect.top); | ||
| 149 | right = static_cast<f32>(framebuffer.crop_rect.right); | ||
| 150 | bottom = static_cast<f32>(framebuffer.crop_rect.bottom); | ||
| 151 | } else { | ||
| 152 | // Otherwise, fall back to framebuffer dimensions. | ||
| 153 | left = 0; | ||
| 154 | top = 0; | ||
| 155 | right = static_cast<f32>(framebuffer.width); | ||
| 156 | bottom = static_cast<f32>(framebuffer.height); | ||
| 157 | } | ||
| 158 | |||
| 159 | // Apply transformation flags. | ||
| 160 | auto framebuffer_transform_flags = framebuffer.transform_flags; | ||
| 161 | 60 | ||
| 162 | if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipH)) { | 61 | // Recreate dynamic resources if the adapting filter changed |
| 163 | // Switch left and right. | 62 | if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue()) { |
| 164 | std::swap(left, right); | 63 | resource_update_required = true; |
| 165 | } | ||
| 166 | if (True(framebuffer_transform_flags & Service::android::BufferTransformFlags::FlipV)) { | ||
| 167 | // Switch top and bottom. | ||
| 168 | std::swap(top, bottom); | ||
| 169 | } | 64 | } |
| 170 | 65 | ||
| 171 | framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipH; | 66 | // Recreate dynamic resources if the image count changed |
| 172 | framebuffer_transform_flags &= ~Service::android::BufferTransformFlags::FlipV; | 67 | const size_t old_swapchain_image_count = |
| 173 | if (True(framebuffer_transform_flags)) { | 68 | std::exchange(image_count, current_swapchain_image_count); |
| 174 | UNIMPLEMENTED_MSG("Unsupported framebuffer_transform_flags={}", | 69 | if (old_swapchain_image_count != current_swapchain_image_count) { |
| 175 | static_cast<u32>(framebuffer_transform_flags)); | 70 | resource_update_required = true; |
| 176 | } | 71 | } |
| 177 | 72 | ||
| 178 | // Get the screen properties. | 73 | // Recreate the presentation frame if the format or dimensions of the window changed |
| 179 | const f32 screen_width = static_cast<f32>(screen_info.width); | 74 | const VkFormat old_swapchain_view_format = |
| 180 | const f32 screen_height = static_cast<f32>(screen_info.height); | 75 | std::exchange(swapchain_view_format, current_swapchain_view_format); |
| 181 | 76 | if (old_swapchain_view_format != current_swapchain_view_format || | |
| 182 | // Normalize coordinate space. | 77 | layout.width != frame->width || layout.height != frame->height) { |
| 183 | left /= screen_width; | 78 | resource_update_required = true; |
| 184 | top /= screen_height; | 79 | presentation_recreate_required = true; |
| 185 | right /= screen_width; | ||
| 186 | bottom /= screen_height; | ||
| 187 | |||
| 188 | return Common::Rectangle<f32>(left, top, right, bottom); | ||
| 189 | } | ||
| 190 | |||
| 191 | void BlitScreen::Recreate() { | ||
| 192 | present_manager.WaitPresent(); | ||
| 193 | scheduler.Finish(); | ||
| 194 | device.GetLogical().WaitIdle(); | ||
| 195 | CreateDynamicResources(); | ||
| 196 | } | ||
| 197 | |||
| 198 | void BlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, | ||
| 199 | const VkFramebuffer& host_framebuffer, const Layout::FramebufferLayout layout, | ||
| 200 | VkExtent2D render_area, bool use_accelerated) { | ||
| 201 | RefreshResources(framebuffer); | ||
| 202 | |||
| 203 | // Finish any pending renderpass | ||
| 204 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 205 | |||
| 206 | scheduler.Wait(resource_ticks[image_index]); | ||
| 207 | resource_ticks[image_index] = scheduler.CurrentTick(); | ||
| 208 | |||
| 209 | VkImage source_image = use_accelerated ? screen_info.image : *raw_images[image_index]; | ||
| 210 | VkImageView source_image_view = | ||
| 211 | use_accelerated ? screen_info.image_view : *raw_image_views[image_index]; | ||
| 212 | |||
| 213 | BufferData data; | ||
| 214 | SetUniformData(data, layout); | ||
| 215 | SetVertexData(data, framebuffer, layout); | ||
| 216 | |||
| 217 | const std::span<u8> mapped_span = buffer.Mapped(); | ||
| 218 | std::memcpy(mapped_span.data(), &data, sizeof(data)); | ||
| 219 | |||
| 220 | if (!use_accelerated) { | ||
| 221 | const u64 image_offset = GetRawImageOffset(framebuffer); | ||
| 222 | |||
| 223 | const DAddr framebuffer_addr = framebuffer.address + framebuffer.offset; | ||
| 224 | const u8* const host_ptr = device_memory.GetPointer<u8>(framebuffer_addr); | ||
| 225 | |||
| 226 | // TODO(Rodrigo): Read this from HLE | ||
| 227 | constexpr u32 block_height_log2 = 4; | ||
| 228 | const u32 bytes_per_pixel = GetBytesPerPixel(framebuffer); | ||
| 229 | const u64 linear_size{GetSizeInBytes(framebuffer)}; | ||
| 230 | const u64 tiled_size{Tegra::Texture::CalculateSize(true, bytes_per_pixel, | ||
| 231 | framebuffer.stride, framebuffer.height, | ||
| 232 | 1, block_height_log2, 0)}; | ||
| 233 | Tegra::Texture::UnswizzleTexture( | ||
| 234 | mapped_span.subspan(image_offset, linear_size), std::span(host_ptr, tiled_size), | ||
| 235 | bytes_per_pixel, framebuffer.width, framebuffer.height, 1, block_height_log2, 0); | ||
| 236 | |||
| 237 | const VkBufferImageCopy copy{ | ||
| 238 | .bufferOffset = image_offset, | ||
| 239 | .bufferRowLength = 0, | ||
| 240 | .bufferImageHeight = 0, | ||
| 241 | .imageSubresource = | ||
| 242 | { | ||
| 243 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 244 | .mipLevel = 0, | ||
| 245 | .baseArrayLayer = 0, | ||
| 246 | .layerCount = 1, | ||
| 247 | }, | ||
| 248 | .imageOffset = {.x = 0, .y = 0, .z = 0}, | ||
| 249 | .imageExtent = | ||
| 250 | { | ||
| 251 | .width = framebuffer.width, | ||
| 252 | .height = framebuffer.height, | ||
| 253 | .depth = 1, | ||
| 254 | }, | ||
| 255 | }; | ||
| 256 | scheduler.Record([this, copy, index = image_index](vk::CommandBuffer cmdbuf) { | ||
| 257 | const VkImage image = *raw_images[index]; | ||
| 258 | const VkImageMemoryBarrier base_barrier{ | ||
| 259 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 260 | .pNext = nullptr, | ||
| 261 | .srcAccessMask = 0, | ||
| 262 | .dstAccessMask = 0, | ||
| 263 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 264 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 265 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 266 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 267 | .image = image, | ||
| 268 | .subresourceRange{ | ||
| 269 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 270 | .baseMipLevel = 0, | ||
| 271 | .levelCount = 1, | ||
| 272 | .baseArrayLayer = 0, | ||
| 273 | .layerCount = 1, | ||
| 274 | }, | ||
| 275 | }; | ||
| 276 | VkImageMemoryBarrier read_barrier = base_barrier; | ||
| 277 | read_barrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT; | ||
| 278 | read_barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | ||
| 279 | read_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||
| 280 | |||
| 281 | VkImageMemoryBarrier write_barrier = base_barrier; | ||
| 282 | write_barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; | ||
| 283 | write_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||
| 284 | |||
| 285 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, | ||
| 286 | read_barrier); | ||
| 287 | cmdbuf.CopyBufferToImage(*buffer, image, VK_IMAGE_LAYOUT_GENERAL, copy); | ||
| 288 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_TRANSFER_BIT, | ||
| 289 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | | ||
| 290 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||
| 291 | 0, write_barrier); | ||
| 292 | }); | ||
| 293 | } | 80 | } |
| 294 | 81 | ||
| 295 | const auto anti_alias_pass = Settings::values.anti_aliasing.GetValue(); | 82 | // If we have a pending resource update, perform it |
| 296 | if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Fxaa) { | 83 | if (resource_update_required) { |
| 297 | UpdateAADescriptorSet(source_image_view, false); | 84 | // Wait for idle to ensure no resources are in use |
| 298 | const u32 up_scale = Settings::values.resolution_info.up_scale; | 85 | WaitIdle(); |
| 299 | const u32 down_shift = Settings::values.resolution_info.down_shift; | ||
| 300 | VkExtent2D size{ | ||
| 301 | .width = (up_scale * framebuffer.width) >> down_shift, | ||
| 302 | .height = (up_scale * framebuffer.height) >> down_shift, | ||
| 303 | }; | ||
| 304 | scheduler.Record([this, index = image_index, size, | ||
| 305 | anti_alias_pass](vk::CommandBuffer cmdbuf) { | ||
| 306 | const VkImageMemoryBarrier base_barrier{ | ||
| 307 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 308 | .pNext = nullptr, | ||
| 309 | .srcAccessMask = 0, | ||
| 310 | .dstAccessMask = 0, | ||
| 311 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 312 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 313 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 314 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 315 | .image = {}, | ||
| 316 | .subresourceRange = | ||
| 317 | { | ||
| 318 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 319 | .baseMipLevel = 0, | ||
| 320 | .levelCount = 1, | ||
| 321 | .baseArrayLayer = 0, | ||
| 322 | .layerCount = 1, | ||
| 323 | }, | ||
| 324 | }; | ||
| 325 | |||
| 326 | { | ||
| 327 | VkImageMemoryBarrier fsr_write_barrier = base_barrier; | ||
| 328 | fsr_write_barrier.image = *aa_image; | ||
| 329 | fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||
| 330 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 331 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, fsr_write_barrier); | ||
| 332 | } | ||
| 333 | 86 | ||
| 334 | const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; | 87 | // Update window adapt pass |
| 335 | const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; | 88 | SetWindowAdaptPass(); |
| 336 | const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; | ||
| 337 | const VkClearValue clear_color{ | ||
| 338 | .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, | ||
| 339 | }; | ||
| 340 | const VkRenderPassBeginInfo renderpass_bi{ | ||
| 341 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | ||
| 342 | .pNext = nullptr, | ||
| 343 | .renderPass = *aa_renderpass, | ||
| 344 | .framebuffer = *aa_framebuffer, | ||
| 345 | .renderArea = | ||
| 346 | { | ||
| 347 | .offset = {0, 0}, | ||
| 348 | .extent = size, | ||
| 349 | }, | ||
| 350 | .clearValueCount = 1, | ||
| 351 | .pClearValues = &clear_color, | ||
| 352 | }; | ||
| 353 | const VkViewport viewport{ | ||
| 354 | .x = 0.0f, | ||
| 355 | .y = 0.0f, | ||
| 356 | .width = static_cast<float>(size.width), | ||
| 357 | .height = static_cast<float>(size.height), | ||
| 358 | .minDepth = 0.0f, | ||
| 359 | .maxDepth = 1.0f, | ||
| 360 | }; | ||
| 361 | const VkRect2D scissor{ | ||
| 362 | .offset = {0, 0}, | ||
| 363 | .extent = size, | ||
| 364 | }; | ||
| 365 | cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); | ||
| 366 | switch (anti_alias_pass) { | ||
| 367 | case Settings::AntiAliasing::Fxaa: | ||
| 368 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline); | ||
| 369 | break; | ||
| 370 | default: | ||
| 371 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline); | ||
| 372 | break; | ||
| 373 | } | ||
| 374 | cmdbuf.SetViewport(0, viewport); | ||
| 375 | cmdbuf.SetScissor(0, scissor); | ||
| 376 | 89 | ||
| 377 | cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); | 90 | // Update frame format if needed |
| 378 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *aa_pipeline_layout, 0, | 91 | if (presentation_recreate_required) { |
| 379 | aa_descriptor_sets[index], {}); | 92 | present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, |
| 380 | cmdbuf.Draw(4, 1, 0, 0); | 93 | window_adapt->GetRenderPass()); |
| 381 | cmdbuf.EndRenderPass(); | ||
| 382 | |||
| 383 | { | ||
| 384 | VkImageMemoryBarrier blit_read_barrier = base_barrier; | ||
| 385 | blit_read_barrier.image = *aa_image; | ||
| 386 | blit_read_barrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; | ||
| 387 | blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||
| 388 | |||
| 389 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||
| 390 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, blit_read_barrier); | ||
| 391 | } | ||
| 392 | }); | ||
| 393 | source_image_view = *aa_image_view; | ||
| 394 | } | ||
| 395 | if (use_accelerated && anti_alias_pass == Settings::AntiAliasing::Smaa) { | ||
| 396 | if (!smaa) { | ||
| 397 | const u32 up_scale = Settings::values.resolution_info.up_scale; | ||
| 398 | const u32 down_shift = Settings::values.resolution_info.down_shift; | ||
| 399 | const VkExtent2D smaa_size{ | ||
| 400 | .width = (up_scale * framebuffer.width) >> down_shift, | ||
| 401 | .height = (up_scale * framebuffer.height) >> down_shift, | ||
| 402 | }; | ||
| 403 | CreateSMAA(smaa_size); | ||
| 404 | } | 94 | } |
| 405 | source_image_view = smaa->Draw(scheduler, image_index, source_image, source_image_view); | ||
| 406 | } | 95 | } |
| 407 | if (fsr) { | ||
| 408 | const auto crop_rect = NormalizeCrop(framebuffer, screen_info); | ||
| 409 | const VkExtent2D fsr_input_size{ | ||
| 410 | .width = Settings::values.resolution_info.ScaleUp(screen_info.width), | ||
| 411 | .height = Settings::values.resolution_info.ScaleUp(screen_info.height), | ||
| 412 | }; | ||
| 413 | VkImageView fsr_image_view = | ||
| 414 | fsr->Draw(scheduler, image_index, source_image_view, fsr_input_size, crop_rect); | ||
| 415 | UpdateDescriptorSet(fsr_image_view, true); | ||
| 416 | } else { | ||
| 417 | const bool is_nn = | ||
| 418 | Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::NearestNeighbor; | ||
| 419 | UpdateDescriptorSet(source_image_view, is_nn); | ||
| 420 | } | ||
| 421 | |||
| 422 | scheduler.Record([this, host_framebuffer, index = image_index, | ||
| 423 | size = render_area](vk::CommandBuffer cmdbuf) { | ||
| 424 | const f32 bg_red = Settings::values.bg_red.GetValue() / 255.0f; | ||
| 425 | const f32 bg_green = Settings::values.bg_green.GetValue() / 255.0f; | ||
| 426 | const f32 bg_blue = Settings::values.bg_blue.GetValue() / 255.0f; | ||
| 427 | const VkClearValue clear_color{ | ||
| 428 | .color = {.float32 = {bg_red, bg_green, bg_blue, 1.0f}}, | ||
| 429 | }; | ||
| 430 | const VkRenderPassBeginInfo renderpass_bi{ | ||
| 431 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | ||
| 432 | .pNext = nullptr, | ||
| 433 | .renderPass = *renderpass, | ||
| 434 | .framebuffer = host_framebuffer, | ||
| 435 | .renderArea = | ||
| 436 | { | ||
| 437 | .offset = {0, 0}, | ||
| 438 | .extent = size, | ||
| 439 | }, | ||
| 440 | .clearValueCount = 1, | ||
| 441 | .pClearValues = &clear_color, | ||
| 442 | }; | ||
| 443 | const VkViewport viewport{ | ||
| 444 | .x = 0.0f, | ||
| 445 | .y = 0.0f, | ||
| 446 | .width = static_cast<float>(size.width), | ||
| 447 | .height = static_cast<float>(size.height), | ||
| 448 | .minDepth = 0.0f, | ||
| 449 | .maxDepth = 1.0f, | ||
| 450 | }; | ||
| 451 | const VkRect2D scissor{ | ||
| 452 | .offset = {0, 0}, | ||
| 453 | .extent = size, | ||
| 454 | }; | ||
| 455 | cmdbuf.BeginRenderPass(renderpass_bi, VK_SUBPASS_CONTENTS_INLINE); | ||
| 456 | auto graphics_pipeline = [this]() { | ||
| 457 | switch (Settings::values.scaling_filter.GetValue()) { | ||
| 458 | case Settings::ScalingFilter::NearestNeighbor: | ||
| 459 | case Settings::ScalingFilter::Bilinear: | ||
| 460 | return *bilinear_pipeline; | ||
| 461 | case Settings::ScalingFilter::Bicubic: | ||
| 462 | return *bicubic_pipeline; | ||
| 463 | case Settings::ScalingFilter::Gaussian: | ||
| 464 | return *gaussian_pipeline; | ||
| 465 | case Settings::ScalingFilter::ScaleForce: | ||
| 466 | return *scaleforce_pipeline; | ||
| 467 | default: | ||
| 468 | return *bilinear_pipeline; | ||
| 469 | } | ||
| 470 | }(); | ||
| 471 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, graphics_pipeline); | ||
| 472 | cmdbuf.SetViewport(0, viewport); | ||
| 473 | cmdbuf.SetScissor(0, scissor); | ||
| 474 | 96 | ||
| 475 | cmdbuf.BindVertexBuffer(0, *buffer, offsetof(BufferData, vertices)); | 97 | // Add additional layers if needed |
| 476 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_GRAPHICS, *pipeline_layout, 0, | 98 | const VkExtent2D window_size{ |
| 477 | descriptor_sets[index], {}); | 99 | .width = layout.screen.GetWidth(), |
| 478 | cmdbuf.Draw(4, 1, 0, 0); | 100 | .height = layout.screen.GetHeight(), |
| 479 | cmdbuf.EndRenderPass(); | 101 | }; |
| 480 | }); | ||
| 481 | } | ||
| 482 | 102 | ||
| 483 | void BlitScreen::DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, | 103 | while (layers.size() < framebuffers.size()) { |
| 484 | bool use_accelerated) { | 104 | layers.emplace_back(device, memory_allocator, scheduler, device_memory, image_count, |
| 485 | // Recreate dynamic resources if the the image count or input format changed | 105 | window_size, window_adapt->GetDescriptorSetLayout()); |
| 486 | const VkFormat current_framebuffer_format = | ||
| 487 | std::exchange(framebuffer_view_format, GetFormat(framebuffer)); | ||
| 488 | if (const std::size_t swapchain_images = swapchain.GetImageCount(); | ||
| 489 | swapchain_images != image_count || current_framebuffer_format != framebuffer_view_format) { | ||
| 490 | image_count = swapchain_images; | ||
| 491 | Recreate(); | ||
| 492 | } | 106 | } |
| 493 | 107 | ||
| 494 | // Recreate the presentation frame if the dimensions of the window changed | 108 | // Perform the draw |
| 495 | const Layout::FramebufferLayout layout = render_window.GetFramebufferLayout(); | 109 | window_adapt->Draw(rasterizer, scheduler, image_index, layers, framebuffers, layout, frame); |
| 496 | if (layout.width != frame->width || layout.height != frame->height) { | ||
| 497 | Recreate(); | ||
| 498 | present_manager.RecreateFrame(frame, layout.width, layout.height, swapchain_view_format, | ||
| 499 | *renderpass); | ||
| 500 | } | ||
| 501 | 110 | ||
| 502 | const VkExtent2D render_area{frame->width, frame->height}; | 111 | // Advance to next image |
| 503 | Draw(framebuffer, *frame->framebuffer, layout, render_area, use_accelerated); | ||
| 504 | if (++image_index >= image_count) { | 112 | if (++image_index >= image_count) { |
| 505 | image_index = 0; | 113 | image_index = 0; |
| 506 | } | 114 | } |
| 507 | } | 115 | } |
| 508 | 116 | ||
| 509 | vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent) { | 117 | vk::Framebuffer BlitScreen::CreateFramebuffer(const Layout::FramebufferLayout& layout, |
| 510 | return CreateFramebuffer(image_view, extent, renderpass); | 118 | VkImageView image_view, |
| 119 | VkFormat current_view_format) { | ||
| 120 | const bool format_updated = | ||
| 121 | std::exchange(swapchain_view_format, current_view_format) != current_view_format; | ||
| 122 | if (!window_adapt || scaling_filter != Settings::values.scaling_filter.GetValue() || | ||
| 123 | format_updated) { | ||
| 124 | WaitIdle(); | ||
| 125 | SetWindowAdaptPass(); | ||
| 126 | } | ||
| 127 | const VkExtent2D extent{ | ||
| 128 | .width = layout.width, | ||
| 129 | .height = layout.height, | ||
| 130 | }; | ||
| 131 | return CreateFramebuffer(image_view, extent, window_adapt->GetRenderPass()); | ||
| 511 | } | 132 | } |
| 512 | 133 | ||
| 513 | vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, | 134 | vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, |
| 514 | vk::RenderPass& rd) { | 135 | VkRenderPass render_pass) { |
| 515 | return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ | 136 | return device.GetLogical().CreateFramebuffer(VkFramebufferCreateInfo{ |
| 516 | .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | 137 | .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, |
| 517 | .pNext = nullptr, | 138 | .pNext = nullptr, |
| 518 | .flags = 0, | 139 | .flags = 0, |
| 519 | .renderPass = *rd, | 140 | .renderPass = render_pass, |
| 520 | .attachmentCount = 1, | 141 | .attachmentCount = 1, |
| 521 | .pAttachments = &image_view, | 142 | .pAttachments = &image_view, |
| 522 | .width = extent.width, | 143 | .width = extent.width, |
| @@ -525,969 +146,4 @@ vk::Framebuffer BlitScreen::CreateFramebuffer(const VkImageView& image_view, VkE | |||
| 525 | }); | 146 | }); |
| 526 | } | 147 | } |
| 527 | 148 | ||
| 528 | void BlitScreen::CreateStaticResources() { | ||
| 529 | CreateShaders(); | ||
| 530 | CreateSampler(); | ||
| 531 | } | ||
| 532 | |||
| 533 | void BlitScreen::CreateDynamicResources() { | ||
| 534 | CreateDescriptorPool(); | ||
| 535 | CreateDescriptorSetLayout(); | ||
| 536 | CreateDescriptorSets(); | ||
| 537 | CreatePipelineLayout(); | ||
| 538 | CreateRenderPass(); | ||
| 539 | CreateGraphicsPipeline(); | ||
| 540 | fsr.reset(); | ||
| 541 | smaa.reset(); | ||
| 542 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 543 | CreateFSR(); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | void BlitScreen::RefreshResources(const Tegra::FramebufferConfig& framebuffer) { | ||
| 548 | if (Settings::values.scaling_filter.GetValue() == Settings::ScalingFilter::Fsr) { | ||
| 549 | if (!fsr) { | ||
| 550 | CreateFSR(); | ||
| 551 | } | ||
| 552 | } else { | ||
| 553 | fsr.reset(); | ||
| 554 | } | ||
| 555 | |||
| 556 | if (framebuffer.width == raw_width && framebuffer.height == raw_height && | ||
| 557 | framebuffer.pixel_format == pixel_format && !raw_images.empty()) { | ||
| 558 | return; | ||
| 559 | } | ||
| 560 | |||
| 561 | raw_width = framebuffer.width; | ||
| 562 | raw_height = framebuffer.height; | ||
| 563 | pixel_format = framebuffer.pixel_format; | ||
| 564 | |||
| 565 | smaa.reset(); | ||
| 566 | ReleaseRawImages(); | ||
| 567 | |||
| 568 | CreateStagingBuffer(framebuffer); | ||
| 569 | CreateRawImages(framebuffer); | ||
| 570 | } | ||
| 571 | |||
| 572 | void BlitScreen::CreateShaders() { | ||
| 573 | vertex_shader = BuildShader(device, VULKAN_PRESENT_VERT_SPV); | ||
| 574 | fxaa_vertex_shader = BuildShader(device, FXAA_VERT_SPV); | ||
| 575 | fxaa_fragment_shader = BuildShader(device, FXAA_FRAG_SPV); | ||
| 576 | bilinear_fragment_shader = BuildShader(device, VULKAN_PRESENT_FRAG_SPV); | ||
| 577 | bicubic_fragment_shader = BuildShader(device, PRESENT_BICUBIC_FRAG_SPV); | ||
| 578 | gaussian_fragment_shader = BuildShader(device, PRESENT_GAUSSIAN_FRAG_SPV); | ||
| 579 | if (device.IsFloat16Supported()) { | ||
| 580 | scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP16_FRAG_SPV); | ||
| 581 | } else { | ||
| 582 | scaleforce_fragment_shader = BuildShader(device, VULKAN_PRESENT_SCALEFORCE_FP32_FRAG_SPV); | ||
| 583 | } | ||
| 584 | } | ||
| 585 | |||
| 586 | void BlitScreen::CreateDescriptorPool() { | ||
| 587 | const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ | ||
| 588 | { | ||
| 589 | .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | ||
| 590 | .descriptorCount = static_cast<u32>(image_count), | ||
| 591 | }, | ||
| 592 | { | ||
| 593 | .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 594 | .descriptorCount = static_cast<u32>(image_count), | ||
| 595 | }, | ||
| 596 | }}; | ||
| 597 | |||
| 598 | const std::array<VkDescriptorPoolSize, 1> pool_sizes_aa{{ | ||
| 599 | { | ||
| 600 | .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 601 | .descriptorCount = static_cast<u32>(image_count * 2), | ||
| 602 | }, | ||
| 603 | }}; | ||
| 604 | |||
| 605 | const VkDescriptorPoolCreateInfo ci{ | ||
| 606 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | ||
| 607 | .pNext = nullptr, | ||
| 608 | .flags = 0, | ||
| 609 | .maxSets = static_cast<u32>(image_count), | ||
| 610 | .poolSizeCount = static_cast<u32>(pool_sizes.size()), | ||
| 611 | .pPoolSizes = pool_sizes.data(), | ||
| 612 | }; | ||
| 613 | descriptor_pool = device.GetLogical().CreateDescriptorPool(ci); | ||
| 614 | |||
| 615 | const VkDescriptorPoolCreateInfo ci_aa{ | ||
| 616 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | ||
| 617 | .pNext = nullptr, | ||
| 618 | .flags = 0, | ||
| 619 | .maxSets = static_cast<u32>(image_count), | ||
| 620 | .poolSizeCount = static_cast<u32>(pool_sizes_aa.size()), | ||
| 621 | .pPoolSizes = pool_sizes_aa.data(), | ||
| 622 | }; | ||
| 623 | aa_descriptor_pool = device.GetLogical().CreateDescriptorPool(ci_aa); | ||
| 624 | } | ||
| 625 | |||
| 626 | void BlitScreen::CreateRenderPass() { | ||
| 627 | renderpass = CreateRenderPassImpl(swapchain_view_format); | ||
| 628 | } | ||
| 629 | |||
| 630 | vk::RenderPass BlitScreen::CreateRenderPassImpl(VkFormat format) { | ||
| 631 | const VkAttachmentDescription color_attachment{ | ||
| 632 | .flags = 0, | ||
| 633 | .format = format, | ||
| 634 | .samples = VK_SAMPLE_COUNT_1_BIT, | ||
| 635 | .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, | ||
| 636 | .storeOp = VK_ATTACHMENT_STORE_OP_STORE, | ||
| 637 | .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, | ||
| 638 | .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, | ||
| 639 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||
| 640 | .finalLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 641 | }; | ||
| 642 | |||
| 643 | const VkAttachmentReference color_attachment_ref{ | ||
| 644 | .attachment = 0, | ||
| 645 | .layout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 646 | }; | ||
| 647 | |||
| 648 | const VkSubpassDescription subpass_description{ | ||
| 649 | .flags = 0, | ||
| 650 | .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, | ||
| 651 | .inputAttachmentCount = 0, | ||
| 652 | .pInputAttachments = nullptr, | ||
| 653 | .colorAttachmentCount = 1, | ||
| 654 | .pColorAttachments = &color_attachment_ref, | ||
| 655 | .pResolveAttachments = nullptr, | ||
| 656 | .pDepthStencilAttachment = nullptr, | ||
| 657 | .preserveAttachmentCount = 0, | ||
| 658 | .pPreserveAttachments = nullptr, | ||
| 659 | }; | ||
| 660 | |||
| 661 | const VkSubpassDependency dependency{ | ||
| 662 | .srcSubpass = VK_SUBPASS_EXTERNAL, | ||
| 663 | .dstSubpass = 0, | ||
| 664 | .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||
| 665 | .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, | ||
| 666 | .srcAccessMask = 0, | ||
| 667 | .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, | ||
| 668 | .dependencyFlags = 0, | ||
| 669 | }; | ||
| 670 | |||
| 671 | const VkRenderPassCreateInfo renderpass_ci{ | ||
| 672 | .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | ||
| 673 | .pNext = nullptr, | ||
| 674 | .flags = 0, | ||
| 675 | .attachmentCount = 1, | ||
| 676 | .pAttachments = &color_attachment, | ||
| 677 | .subpassCount = 1, | ||
| 678 | .pSubpasses = &subpass_description, | ||
| 679 | .dependencyCount = 1, | ||
| 680 | .pDependencies = &dependency, | ||
| 681 | }; | ||
| 682 | |||
| 683 | return device.GetLogical().CreateRenderPass(renderpass_ci); | ||
| 684 | } | ||
| 685 | |||
| 686 | void BlitScreen::CreateDescriptorSetLayout() { | ||
| 687 | const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{ | ||
| 688 | { | ||
| 689 | .binding = 0, | ||
| 690 | .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | ||
| 691 | .descriptorCount = 1, | ||
| 692 | .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 693 | .pImmutableSamplers = nullptr, | ||
| 694 | }, | ||
| 695 | { | ||
| 696 | .binding = 1, | ||
| 697 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 698 | .descriptorCount = 1, | ||
| 699 | .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 700 | .pImmutableSamplers = nullptr, | ||
| 701 | }, | ||
| 702 | }}; | ||
| 703 | |||
| 704 | const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings_aa{{ | ||
| 705 | { | ||
| 706 | .binding = 0, | ||
| 707 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 708 | .descriptorCount = 1, | ||
| 709 | .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 710 | .pImmutableSamplers = nullptr, | ||
| 711 | }, | ||
| 712 | { | ||
| 713 | .binding = 1, | ||
| 714 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 715 | .descriptorCount = 1, | ||
| 716 | .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 717 | .pImmutableSamplers = nullptr, | ||
| 718 | }, | ||
| 719 | }}; | ||
| 720 | |||
| 721 | const VkDescriptorSetLayoutCreateInfo ci{ | ||
| 722 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | ||
| 723 | .pNext = nullptr, | ||
| 724 | .flags = 0, | ||
| 725 | .bindingCount = static_cast<u32>(layout_bindings.size()), | ||
| 726 | .pBindings = layout_bindings.data(), | ||
| 727 | }; | ||
| 728 | |||
| 729 | const VkDescriptorSetLayoutCreateInfo ci_aa{ | ||
| 730 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | ||
| 731 | .pNext = nullptr, | ||
| 732 | .flags = 0, | ||
| 733 | .bindingCount = static_cast<u32>(layout_bindings_aa.size()), | ||
| 734 | .pBindings = layout_bindings_aa.data(), | ||
| 735 | }; | ||
| 736 | |||
| 737 | descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); | ||
| 738 | aa_descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci_aa); | ||
| 739 | } | ||
| 740 | |||
| 741 | void BlitScreen::CreateDescriptorSets() { | ||
| 742 | const std::vector layouts(image_count, *descriptor_set_layout); | ||
| 743 | const std::vector layouts_aa(image_count, *aa_descriptor_set_layout); | ||
| 744 | |||
| 745 | const VkDescriptorSetAllocateInfo ai{ | ||
| 746 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, | ||
| 747 | .pNext = nullptr, | ||
| 748 | .descriptorPool = *descriptor_pool, | ||
| 749 | .descriptorSetCount = static_cast<u32>(image_count), | ||
| 750 | .pSetLayouts = layouts.data(), | ||
| 751 | }; | ||
| 752 | |||
| 753 | const VkDescriptorSetAllocateInfo ai_aa{ | ||
| 754 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, | ||
| 755 | .pNext = nullptr, | ||
| 756 | .descriptorPool = *aa_descriptor_pool, | ||
| 757 | .descriptorSetCount = static_cast<u32>(image_count), | ||
| 758 | .pSetLayouts = layouts_aa.data(), | ||
| 759 | }; | ||
| 760 | |||
| 761 | descriptor_sets = descriptor_pool.Allocate(ai); | ||
| 762 | aa_descriptor_sets = aa_descriptor_pool.Allocate(ai_aa); | ||
| 763 | } | ||
| 764 | |||
| 765 | void BlitScreen::CreatePipelineLayout() { | ||
| 766 | const VkPipelineLayoutCreateInfo ci{ | ||
| 767 | .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||
| 768 | .pNext = nullptr, | ||
| 769 | .flags = 0, | ||
| 770 | .setLayoutCount = 1, | ||
| 771 | .pSetLayouts = descriptor_set_layout.address(), | ||
| 772 | .pushConstantRangeCount = 0, | ||
| 773 | .pPushConstantRanges = nullptr, | ||
| 774 | }; | ||
| 775 | const VkPipelineLayoutCreateInfo ci_aa{ | ||
| 776 | .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||
| 777 | .pNext = nullptr, | ||
| 778 | .flags = 0, | ||
| 779 | .setLayoutCount = 1, | ||
| 780 | .pSetLayouts = aa_descriptor_set_layout.address(), | ||
| 781 | .pushConstantRangeCount = 0, | ||
| 782 | .pPushConstantRanges = nullptr, | ||
| 783 | }; | ||
| 784 | pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); | ||
| 785 | aa_pipeline_layout = device.GetLogical().CreatePipelineLayout(ci_aa); | ||
| 786 | } | ||
| 787 | |||
| 788 | void BlitScreen::CreateGraphicsPipeline() { | ||
| 789 | const std::array<VkPipelineShaderStageCreateInfo, 2> bilinear_shader_stages{{ | ||
| 790 | { | ||
| 791 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 792 | .pNext = nullptr, | ||
| 793 | .flags = 0, | ||
| 794 | .stage = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 795 | .module = *vertex_shader, | ||
| 796 | .pName = "main", | ||
| 797 | .pSpecializationInfo = nullptr, | ||
| 798 | }, | ||
| 799 | { | ||
| 800 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 801 | .pNext = nullptr, | ||
| 802 | .flags = 0, | ||
| 803 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 804 | .module = *bilinear_fragment_shader, | ||
| 805 | .pName = "main", | ||
| 806 | .pSpecializationInfo = nullptr, | ||
| 807 | }, | ||
| 808 | }}; | ||
| 809 | |||
| 810 | const std::array<VkPipelineShaderStageCreateInfo, 2> bicubic_shader_stages{{ | ||
| 811 | { | ||
| 812 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 813 | .pNext = nullptr, | ||
| 814 | .flags = 0, | ||
| 815 | .stage = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 816 | .module = *vertex_shader, | ||
| 817 | .pName = "main", | ||
| 818 | .pSpecializationInfo = nullptr, | ||
| 819 | }, | ||
| 820 | { | ||
| 821 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 822 | .pNext = nullptr, | ||
| 823 | .flags = 0, | ||
| 824 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 825 | .module = *bicubic_fragment_shader, | ||
| 826 | .pName = "main", | ||
| 827 | .pSpecializationInfo = nullptr, | ||
| 828 | }, | ||
| 829 | }}; | ||
| 830 | |||
| 831 | const std::array<VkPipelineShaderStageCreateInfo, 2> gaussian_shader_stages{{ | ||
| 832 | { | ||
| 833 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 834 | .pNext = nullptr, | ||
| 835 | .flags = 0, | ||
| 836 | .stage = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 837 | .module = *vertex_shader, | ||
| 838 | .pName = "main", | ||
| 839 | .pSpecializationInfo = nullptr, | ||
| 840 | }, | ||
| 841 | { | ||
| 842 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 843 | .pNext = nullptr, | ||
| 844 | .flags = 0, | ||
| 845 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 846 | .module = *gaussian_fragment_shader, | ||
| 847 | .pName = "main", | ||
| 848 | .pSpecializationInfo = nullptr, | ||
| 849 | }, | ||
| 850 | }}; | ||
| 851 | |||
| 852 | const std::array<VkPipelineShaderStageCreateInfo, 2> scaleforce_shader_stages{{ | ||
| 853 | { | ||
| 854 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 855 | .pNext = nullptr, | ||
| 856 | .flags = 0, | ||
| 857 | .stage = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 858 | .module = *vertex_shader, | ||
| 859 | .pName = "main", | ||
| 860 | .pSpecializationInfo = nullptr, | ||
| 861 | }, | ||
| 862 | { | ||
| 863 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 864 | .pNext = nullptr, | ||
| 865 | .flags = 0, | ||
| 866 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 867 | .module = *scaleforce_fragment_shader, | ||
| 868 | .pName = "main", | ||
| 869 | .pSpecializationInfo = nullptr, | ||
| 870 | }, | ||
| 871 | }}; | ||
| 872 | |||
| 873 | const auto vertex_binding_description = ScreenRectVertex::GetDescription(); | ||
| 874 | const auto vertex_attrs_description = ScreenRectVertex::GetAttributes(); | ||
| 875 | |||
| 876 | const VkPipelineVertexInputStateCreateInfo vertex_input_ci{ | ||
| 877 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | ||
| 878 | .pNext = nullptr, | ||
| 879 | .flags = 0, | ||
| 880 | .vertexBindingDescriptionCount = 1, | ||
| 881 | .pVertexBindingDescriptions = &vertex_binding_description, | ||
| 882 | .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()}, | ||
| 883 | .pVertexAttributeDescriptions = vertex_attrs_description.data(), | ||
| 884 | }; | ||
| 885 | |||
| 886 | const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ | ||
| 887 | .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | ||
| 888 | .pNext = nullptr, | ||
| 889 | .flags = 0, | ||
| 890 | .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, | ||
| 891 | .primitiveRestartEnable = VK_FALSE, | ||
| 892 | }; | ||
| 893 | |||
| 894 | const VkPipelineViewportStateCreateInfo viewport_state_ci{ | ||
| 895 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | ||
| 896 | .pNext = nullptr, | ||
| 897 | .flags = 0, | ||
| 898 | .viewportCount = 1, | ||
| 899 | .pViewports = nullptr, | ||
| 900 | .scissorCount = 1, | ||
| 901 | .pScissors = nullptr, | ||
| 902 | }; | ||
| 903 | |||
| 904 | const VkPipelineRasterizationStateCreateInfo rasterization_ci{ | ||
| 905 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | ||
| 906 | .pNext = nullptr, | ||
| 907 | .flags = 0, | ||
| 908 | .depthClampEnable = VK_FALSE, | ||
| 909 | .rasterizerDiscardEnable = VK_FALSE, | ||
| 910 | .polygonMode = VK_POLYGON_MODE_FILL, | ||
| 911 | .cullMode = VK_CULL_MODE_NONE, | ||
| 912 | .frontFace = VK_FRONT_FACE_CLOCKWISE, | ||
| 913 | .depthBiasEnable = VK_FALSE, | ||
| 914 | .depthBiasConstantFactor = 0.0f, | ||
| 915 | .depthBiasClamp = 0.0f, | ||
| 916 | .depthBiasSlopeFactor = 0.0f, | ||
| 917 | .lineWidth = 1.0f, | ||
| 918 | }; | ||
| 919 | |||
| 920 | const VkPipelineMultisampleStateCreateInfo multisampling_ci{ | ||
| 921 | .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | ||
| 922 | .pNext = nullptr, | ||
| 923 | .flags = 0, | ||
| 924 | .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, | ||
| 925 | .sampleShadingEnable = VK_FALSE, | ||
| 926 | .minSampleShading = 0.0f, | ||
| 927 | .pSampleMask = nullptr, | ||
| 928 | .alphaToCoverageEnable = VK_FALSE, | ||
| 929 | .alphaToOneEnable = VK_FALSE, | ||
| 930 | }; | ||
| 931 | |||
| 932 | const VkPipelineColorBlendAttachmentState color_blend_attachment{ | ||
| 933 | .blendEnable = VK_FALSE, | ||
| 934 | .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 935 | .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 936 | .colorBlendOp = VK_BLEND_OP_ADD, | ||
| 937 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 938 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 939 | .alphaBlendOp = VK_BLEND_OP_ADD, | ||
| 940 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||
| 941 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||
| 942 | }; | ||
| 943 | |||
| 944 | const VkPipelineColorBlendStateCreateInfo color_blend_ci{ | ||
| 945 | .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | ||
| 946 | .pNext = nullptr, | ||
| 947 | .flags = 0, | ||
| 948 | .logicOpEnable = VK_FALSE, | ||
| 949 | .logicOp = VK_LOGIC_OP_COPY, | ||
| 950 | .attachmentCount = 1, | ||
| 951 | .pAttachments = &color_blend_attachment, | ||
| 952 | .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, | ||
| 953 | }; | ||
| 954 | |||
| 955 | static constexpr std::array dynamic_states{ | ||
| 956 | VK_DYNAMIC_STATE_VIEWPORT, | ||
| 957 | VK_DYNAMIC_STATE_SCISSOR, | ||
| 958 | }; | ||
| 959 | const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ | ||
| 960 | .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | ||
| 961 | .pNext = nullptr, | ||
| 962 | .flags = 0, | ||
| 963 | .dynamicStateCount = static_cast<u32>(dynamic_states.size()), | ||
| 964 | .pDynamicStates = dynamic_states.data(), | ||
| 965 | }; | ||
| 966 | |||
| 967 | const VkGraphicsPipelineCreateInfo bilinear_pipeline_ci{ | ||
| 968 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||
| 969 | .pNext = nullptr, | ||
| 970 | .flags = 0, | ||
| 971 | .stageCount = static_cast<u32>(bilinear_shader_stages.size()), | ||
| 972 | .pStages = bilinear_shader_stages.data(), | ||
| 973 | .pVertexInputState = &vertex_input_ci, | ||
| 974 | .pInputAssemblyState = &input_assembly_ci, | ||
| 975 | .pTessellationState = nullptr, | ||
| 976 | .pViewportState = &viewport_state_ci, | ||
| 977 | .pRasterizationState = &rasterization_ci, | ||
| 978 | .pMultisampleState = &multisampling_ci, | ||
| 979 | .pDepthStencilState = nullptr, | ||
| 980 | .pColorBlendState = &color_blend_ci, | ||
| 981 | .pDynamicState = &dynamic_state_ci, | ||
| 982 | .layout = *pipeline_layout, | ||
| 983 | .renderPass = *renderpass, | ||
| 984 | .subpass = 0, | ||
| 985 | .basePipelineHandle = 0, | ||
| 986 | .basePipelineIndex = 0, | ||
| 987 | }; | ||
| 988 | |||
| 989 | const VkGraphicsPipelineCreateInfo bicubic_pipeline_ci{ | ||
| 990 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||
| 991 | .pNext = nullptr, | ||
| 992 | .flags = 0, | ||
| 993 | .stageCount = static_cast<u32>(bicubic_shader_stages.size()), | ||
| 994 | .pStages = bicubic_shader_stages.data(), | ||
| 995 | .pVertexInputState = &vertex_input_ci, | ||
| 996 | .pInputAssemblyState = &input_assembly_ci, | ||
| 997 | .pTessellationState = nullptr, | ||
| 998 | .pViewportState = &viewport_state_ci, | ||
| 999 | .pRasterizationState = &rasterization_ci, | ||
| 1000 | .pMultisampleState = &multisampling_ci, | ||
| 1001 | .pDepthStencilState = nullptr, | ||
| 1002 | .pColorBlendState = &color_blend_ci, | ||
| 1003 | .pDynamicState = &dynamic_state_ci, | ||
| 1004 | .layout = *pipeline_layout, | ||
| 1005 | .renderPass = *renderpass, | ||
| 1006 | .subpass = 0, | ||
| 1007 | .basePipelineHandle = 0, | ||
| 1008 | .basePipelineIndex = 0, | ||
| 1009 | }; | ||
| 1010 | |||
| 1011 | const VkGraphicsPipelineCreateInfo gaussian_pipeline_ci{ | ||
| 1012 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||
| 1013 | .pNext = nullptr, | ||
| 1014 | .flags = 0, | ||
| 1015 | .stageCount = static_cast<u32>(gaussian_shader_stages.size()), | ||
| 1016 | .pStages = gaussian_shader_stages.data(), | ||
| 1017 | .pVertexInputState = &vertex_input_ci, | ||
| 1018 | .pInputAssemblyState = &input_assembly_ci, | ||
| 1019 | .pTessellationState = nullptr, | ||
| 1020 | .pViewportState = &viewport_state_ci, | ||
| 1021 | .pRasterizationState = &rasterization_ci, | ||
| 1022 | .pMultisampleState = &multisampling_ci, | ||
| 1023 | .pDepthStencilState = nullptr, | ||
| 1024 | .pColorBlendState = &color_blend_ci, | ||
| 1025 | .pDynamicState = &dynamic_state_ci, | ||
| 1026 | .layout = *pipeline_layout, | ||
| 1027 | .renderPass = *renderpass, | ||
| 1028 | .subpass = 0, | ||
| 1029 | .basePipelineHandle = 0, | ||
| 1030 | .basePipelineIndex = 0, | ||
| 1031 | }; | ||
| 1032 | |||
| 1033 | const VkGraphicsPipelineCreateInfo scaleforce_pipeline_ci{ | ||
| 1034 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||
| 1035 | .pNext = nullptr, | ||
| 1036 | .flags = 0, | ||
| 1037 | .stageCount = static_cast<u32>(scaleforce_shader_stages.size()), | ||
| 1038 | .pStages = scaleforce_shader_stages.data(), | ||
| 1039 | .pVertexInputState = &vertex_input_ci, | ||
| 1040 | .pInputAssemblyState = &input_assembly_ci, | ||
| 1041 | .pTessellationState = nullptr, | ||
| 1042 | .pViewportState = &viewport_state_ci, | ||
| 1043 | .pRasterizationState = &rasterization_ci, | ||
| 1044 | .pMultisampleState = &multisampling_ci, | ||
| 1045 | .pDepthStencilState = nullptr, | ||
| 1046 | .pColorBlendState = &color_blend_ci, | ||
| 1047 | .pDynamicState = &dynamic_state_ci, | ||
| 1048 | .layout = *pipeline_layout, | ||
| 1049 | .renderPass = *renderpass, | ||
| 1050 | .subpass = 0, | ||
| 1051 | .basePipelineHandle = 0, | ||
| 1052 | .basePipelineIndex = 0, | ||
| 1053 | }; | ||
| 1054 | |||
| 1055 | bilinear_pipeline = device.GetLogical().CreateGraphicsPipeline(bilinear_pipeline_ci); | ||
| 1056 | bicubic_pipeline = device.GetLogical().CreateGraphicsPipeline(bicubic_pipeline_ci); | ||
| 1057 | gaussian_pipeline = device.GetLogical().CreateGraphicsPipeline(gaussian_pipeline_ci); | ||
| 1058 | scaleforce_pipeline = device.GetLogical().CreateGraphicsPipeline(scaleforce_pipeline_ci); | ||
| 1059 | } | ||
| 1060 | |||
| 1061 | void BlitScreen::CreateSampler() { | ||
| 1062 | const VkSamplerCreateInfo ci{ | ||
| 1063 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | ||
| 1064 | .pNext = nullptr, | ||
| 1065 | .flags = 0, | ||
| 1066 | .magFilter = VK_FILTER_LINEAR, | ||
| 1067 | .minFilter = VK_FILTER_LINEAR, | ||
| 1068 | .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, | ||
| 1069 | .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 1070 | .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 1071 | .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 1072 | .mipLodBias = 0.0f, | ||
| 1073 | .anisotropyEnable = VK_FALSE, | ||
| 1074 | .maxAnisotropy = 0.0f, | ||
| 1075 | .compareEnable = VK_FALSE, | ||
| 1076 | .compareOp = VK_COMPARE_OP_NEVER, | ||
| 1077 | .minLod = 0.0f, | ||
| 1078 | .maxLod = 0.0f, | ||
| 1079 | .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, | ||
| 1080 | .unnormalizedCoordinates = VK_FALSE, | ||
| 1081 | }; | ||
| 1082 | |||
| 1083 | const VkSamplerCreateInfo ci_nn{ | ||
| 1084 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | ||
| 1085 | .pNext = nullptr, | ||
| 1086 | .flags = 0, | ||
| 1087 | .magFilter = VK_FILTER_NEAREST, | ||
| 1088 | .minFilter = VK_FILTER_NEAREST, | ||
| 1089 | .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, | ||
| 1090 | .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 1091 | .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 1092 | .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER, | ||
| 1093 | .mipLodBias = 0.0f, | ||
| 1094 | .anisotropyEnable = VK_FALSE, | ||
| 1095 | .maxAnisotropy = 0.0f, | ||
| 1096 | .compareEnable = VK_FALSE, | ||
| 1097 | .compareOp = VK_COMPARE_OP_NEVER, | ||
| 1098 | .minLod = 0.0f, | ||
| 1099 | .maxLod = 0.0f, | ||
| 1100 | .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, | ||
| 1101 | .unnormalizedCoordinates = VK_FALSE, | ||
| 1102 | }; | ||
| 1103 | |||
| 1104 | sampler = device.GetLogical().CreateSampler(ci); | ||
| 1105 | nn_sampler = device.GetLogical().CreateSampler(ci_nn); | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | void BlitScreen::ReleaseRawImages() { | ||
| 1109 | for (const u64 tick : resource_ticks) { | ||
| 1110 | scheduler.Wait(tick); | ||
| 1111 | } | ||
| 1112 | raw_images.clear(); | ||
| 1113 | aa_image_view.reset(); | ||
| 1114 | aa_image.reset(); | ||
| 1115 | buffer.reset(); | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | void BlitScreen::CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer) { | ||
| 1119 | const VkBufferCreateInfo ci{ | ||
| 1120 | .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, | ||
| 1121 | .pNext = nullptr, | ||
| 1122 | .flags = 0, | ||
| 1123 | .size = CalculateBufferSize(framebuffer), | ||
| 1124 | .usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | | ||
| 1125 | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, | ||
| 1126 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 1127 | .queueFamilyIndexCount = 0, | ||
| 1128 | .pQueueFamilyIndices = nullptr, | ||
| 1129 | }; | ||
| 1130 | |||
| 1131 | buffer = memory_allocator.CreateBuffer(ci, MemoryUsage::Upload); | ||
| 1132 | } | ||
| 1133 | |||
| 1134 | void BlitScreen::CreateRawImages(const Tegra::FramebufferConfig& framebuffer) { | ||
| 1135 | raw_images.resize(image_count); | ||
| 1136 | raw_image_views.resize(image_count); | ||
| 1137 | |||
| 1138 | const auto create_image = [&](bool used_on_framebuffer = false, u32 up_scale = 1, | ||
| 1139 | u32 down_shift = 0) { | ||
| 1140 | u32 extra_usages = used_on_framebuffer ? VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | ||
| 1141 | : VK_IMAGE_USAGE_TRANSFER_DST_BIT; | ||
| 1142 | return memory_allocator.CreateImage(VkImageCreateInfo{ | ||
| 1143 | .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | ||
| 1144 | .pNext = nullptr, | ||
| 1145 | .flags = 0, | ||
| 1146 | .imageType = VK_IMAGE_TYPE_2D, | ||
| 1147 | .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format, | ||
| 1148 | .extent = | ||
| 1149 | { | ||
| 1150 | .width = (up_scale * framebuffer.width) >> down_shift, | ||
| 1151 | .height = (up_scale * framebuffer.height) >> down_shift, | ||
| 1152 | .depth = 1, | ||
| 1153 | }, | ||
| 1154 | .mipLevels = 1, | ||
| 1155 | .arrayLayers = 1, | ||
| 1156 | .samples = VK_SAMPLE_COUNT_1_BIT, | ||
| 1157 | .tiling = used_on_framebuffer ? VK_IMAGE_TILING_OPTIMAL : VK_IMAGE_TILING_LINEAR, | ||
| 1158 | .usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | extra_usages, | ||
| 1159 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 1160 | .queueFamilyIndexCount = 0, | ||
| 1161 | .pQueueFamilyIndices = nullptr, | ||
| 1162 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||
| 1163 | }); | ||
| 1164 | }; | ||
| 1165 | const auto create_image_view = [&](vk::Image& image, bool used_on_framebuffer = false) { | ||
| 1166 | return device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | ||
| 1167 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | ||
| 1168 | .pNext = nullptr, | ||
| 1169 | .flags = 0, | ||
| 1170 | .image = *image, | ||
| 1171 | .viewType = VK_IMAGE_VIEW_TYPE_2D, | ||
| 1172 | .format = used_on_framebuffer ? VK_FORMAT_R16G16B16A16_SFLOAT : framebuffer_view_format, | ||
| 1173 | .components = | ||
| 1174 | { | ||
| 1175 | .r = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 1176 | .g = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 1177 | .b = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 1178 | .a = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 1179 | }, | ||
| 1180 | .subresourceRange = | ||
| 1181 | { | ||
| 1182 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 1183 | .baseMipLevel = 0, | ||
| 1184 | .levelCount = 1, | ||
| 1185 | .baseArrayLayer = 0, | ||
| 1186 | .layerCount = 1, | ||
| 1187 | }, | ||
| 1188 | }); | ||
| 1189 | }; | ||
| 1190 | |||
| 1191 | for (size_t i = 0; i < image_count; ++i) { | ||
| 1192 | raw_images[i] = create_image(); | ||
| 1193 | raw_image_views[i] = create_image_view(raw_images[i]); | ||
| 1194 | } | ||
| 1195 | |||
| 1196 | // AA Resources | ||
| 1197 | const u32 up_scale = Settings::values.resolution_info.up_scale; | ||
| 1198 | const u32 down_shift = Settings::values.resolution_info.down_shift; | ||
| 1199 | aa_image = create_image(true, up_scale, down_shift); | ||
| 1200 | aa_image_view = create_image_view(aa_image, true); | ||
| 1201 | VkExtent2D size{ | ||
| 1202 | .width = (up_scale * framebuffer.width) >> down_shift, | ||
| 1203 | .height = (up_scale * framebuffer.height) >> down_shift, | ||
| 1204 | }; | ||
| 1205 | if (aa_renderpass) { | ||
| 1206 | aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); | ||
| 1207 | return; | ||
| 1208 | } | ||
| 1209 | aa_renderpass = CreateRenderPassImpl(VK_FORMAT_R16G16B16A16_SFLOAT); | ||
| 1210 | aa_framebuffer = CreateFramebuffer(*aa_image_view, size, aa_renderpass); | ||
| 1211 | |||
| 1212 | const std::array<VkPipelineShaderStageCreateInfo, 2> fxaa_shader_stages{{ | ||
| 1213 | { | ||
| 1214 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 1215 | .pNext = nullptr, | ||
| 1216 | .flags = 0, | ||
| 1217 | .stage = VK_SHADER_STAGE_VERTEX_BIT, | ||
| 1218 | .module = *fxaa_vertex_shader, | ||
| 1219 | .pName = "main", | ||
| 1220 | .pSpecializationInfo = nullptr, | ||
| 1221 | }, | ||
| 1222 | { | ||
| 1223 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 1224 | .pNext = nullptr, | ||
| 1225 | .flags = 0, | ||
| 1226 | .stage = VK_SHADER_STAGE_FRAGMENT_BIT, | ||
| 1227 | .module = *fxaa_fragment_shader, | ||
| 1228 | .pName = "main", | ||
| 1229 | .pSpecializationInfo = nullptr, | ||
| 1230 | }, | ||
| 1231 | }}; | ||
| 1232 | |||
| 1233 | const auto vertex_binding_description = ScreenRectVertex::GetDescription(); | ||
| 1234 | const auto vertex_attrs_description = ScreenRectVertex::GetAttributes(); | ||
| 1235 | |||
| 1236 | const VkPipelineVertexInputStateCreateInfo vertex_input_ci{ | ||
| 1237 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, | ||
| 1238 | .pNext = nullptr, | ||
| 1239 | .flags = 0, | ||
| 1240 | .vertexBindingDescriptionCount = 1, | ||
| 1241 | .pVertexBindingDescriptions = &vertex_binding_description, | ||
| 1242 | .vertexAttributeDescriptionCount = u32{vertex_attrs_description.size()}, | ||
| 1243 | .pVertexAttributeDescriptions = vertex_attrs_description.data(), | ||
| 1244 | }; | ||
| 1245 | |||
| 1246 | const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{ | ||
| 1247 | .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, | ||
| 1248 | .pNext = nullptr, | ||
| 1249 | .flags = 0, | ||
| 1250 | .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, | ||
| 1251 | .primitiveRestartEnable = VK_FALSE, | ||
| 1252 | }; | ||
| 1253 | |||
| 1254 | const VkPipelineViewportStateCreateInfo viewport_state_ci{ | ||
| 1255 | .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, | ||
| 1256 | .pNext = nullptr, | ||
| 1257 | .flags = 0, | ||
| 1258 | .viewportCount = 1, | ||
| 1259 | .pViewports = nullptr, | ||
| 1260 | .scissorCount = 1, | ||
| 1261 | .pScissors = nullptr, | ||
| 1262 | }; | ||
| 1263 | |||
| 1264 | const VkPipelineRasterizationStateCreateInfo rasterization_ci{ | ||
| 1265 | .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, | ||
| 1266 | .pNext = nullptr, | ||
| 1267 | .flags = 0, | ||
| 1268 | .depthClampEnable = VK_FALSE, | ||
| 1269 | .rasterizerDiscardEnable = VK_FALSE, | ||
| 1270 | .polygonMode = VK_POLYGON_MODE_FILL, | ||
| 1271 | .cullMode = VK_CULL_MODE_NONE, | ||
| 1272 | .frontFace = VK_FRONT_FACE_CLOCKWISE, | ||
| 1273 | .depthBiasEnable = VK_FALSE, | ||
| 1274 | .depthBiasConstantFactor = 0.0f, | ||
| 1275 | .depthBiasClamp = 0.0f, | ||
| 1276 | .depthBiasSlopeFactor = 0.0f, | ||
| 1277 | .lineWidth = 1.0f, | ||
| 1278 | }; | ||
| 1279 | |||
| 1280 | const VkPipelineMultisampleStateCreateInfo multisampling_ci{ | ||
| 1281 | .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, | ||
| 1282 | .pNext = nullptr, | ||
| 1283 | .flags = 0, | ||
| 1284 | .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, | ||
| 1285 | .sampleShadingEnable = VK_FALSE, | ||
| 1286 | .minSampleShading = 0.0f, | ||
| 1287 | .pSampleMask = nullptr, | ||
| 1288 | .alphaToCoverageEnable = VK_FALSE, | ||
| 1289 | .alphaToOneEnable = VK_FALSE, | ||
| 1290 | }; | ||
| 1291 | |||
| 1292 | const VkPipelineColorBlendAttachmentState color_blend_attachment{ | ||
| 1293 | .blendEnable = VK_FALSE, | ||
| 1294 | .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 1295 | .dstColorBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 1296 | .colorBlendOp = VK_BLEND_OP_ADD, | ||
| 1297 | .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 1298 | .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, | ||
| 1299 | .alphaBlendOp = VK_BLEND_OP_ADD, | ||
| 1300 | .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | | ||
| 1301 | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT, | ||
| 1302 | }; | ||
| 1303 | |||
| 1304 | const VkPipelineColorBlendStateCreateInfo color_blend_ci{ | ||
| 1305 | .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, | ||
| 1306 | .pNext = nullptr, | ||
| 1307 | .flags = 0, | ||
| 1308 | .logicOpEnable = VK_FALSE, | ||
| 1309 | .logicOp = VK_LOGIC_OP_COPY, | ||
| 1310 | .attachmentCount = 1, | ||
| 1311 | .pAttachments = &color_blend_attachment, | ||
| 1312 | .blendConstants = {0.0f, 0.0f, 0.0f, 0.0f}, | ||
| 1313 | }; | ||
| 1314 | |||
| 1315 | static constexpr std::array dynamic_states{ | ||
| 1316 | VK_DYNAMIC_STATE_VIEWPORT, | ||
| 1317 | VK_DYNAMIC_STATE_SCISSOR, | ||
| 1318 | }; | ||
| 1319 | const VkPipelineDynamicStateCreateInfo dynamic_state_ci{ | ||
| 1320 | .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | ||
| 1321 | .pNext = nullptr, | ||
| 1322 | .flags = 0, | ||
| 1323 | .dynamicStateCount = static_cast<u32>(dynamic_states.size()), | ||
| 1324 | .pDynamicStates = dynamic_states.data(), | ||
| 1325 | }; | ||
| 1326 | |||
| 1327 | const VkGraphicsPipelineCreateInfo fxaa_pipeline_ci{ | ||
| 1328 | .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, | ||
| 1329 | .pNext = nullptr, | ||
| 1330 | .flags = 0, | ||
| 1331 | .stageCount = static_cast<u32>(fxaa_shader_stages.size()), | ||
| 1332 | .pStages = fxaa_shader_stages.data(), | ||
| 1333 | .pVertexInputState = &vertex_input_ci, | ||
| 1334 | .pInputAssemblyState = &input_assembly_ci, | ||
| 1335 | .pTessellationState = nullptr, | ||
| 1336 | .pViewportState = &viewport_state_ci, | ||
| 1337 | .pRasterizationState = &rasterization_ci, | ||
| 1338 | .pMultisampleState = &multisampling_ci, | ||
| 1339 | .pDepthStencilState = nullptr, | ||
| 1340 | .pColorBlendState = &color_blend_ci, | ||
| 1341 | .pDynamicState = &dynamic_state_ci, | ||
| 1342 | .layout = *aa_pipeline_layout, | ||
| 1343 | .renderPass = *aa_renderpass, | ||
| 1344 | .subpass = 0, | ||
| 1345 | .basePipelineHandle = 0, | ||
| 1346 | .basePipelineIndex = 0, | ||
| 1347 | }; | ||
| 1348 | |||
| 1349 | // AA | ||
| 1350 | aa_pipeline = device.GetLogical().CreateGraphicsPipeline(fxaa_pipeline_ci); | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | void BlitScreen::UpdateAADescriptorSet(VkImageView image_view, bool nn) const { | ||
| 1354 | const VkDescriptorImageInfo image_info{ | ||
| 1355 | .sampler = nn ? *nn_sampler : *sampler, | ||
| 1356 | .imageView = image_view, | ||
| 1357 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 1358 | }; | ||
| 1359 | |||
| 1360 | const VkWriteDescriptorSet sampler_write{ | ||
| 1361 | .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||
| 1362 | .pNext = nullptr, | ||
| 1363 | .dstSet = aa_descriptor_sets[image_index], | ||
| 1364 | .dstBinding = 0, | ||
| 1365 | .dstArrayElement = 0, | ||
| 1366 | .descriptorCount = 1, | ||
| 1367 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 1368 | .pImageInfo = &image_info, | ||
| 1369 | .pBufferInfo = nullptr, | ||
| 1370 | .pTexelBufferView = nullptr, | ||
| 1371 | }; | ||
| 1372 | |||
| 1373 | const VkWriteDescriptorSet sampler_write_2{ | ||
| 1374 | .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||
| 1375 | .pNext = nullptr, | ||
| 1376 | .dstSet = aa_descriptor_sets[image_index], | ||
| 1377 | .dstBinding = 1, | ||
| 1378 | .dstArrayElement = 0, | ||
| 1379 | .descriptorCount = 1, | ||
| 1380 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 1381 | .pImageInfo = &image_info, | ||
| 1382 | .pBufferInfo = nullptr, | ||
| 1383 | .pTexelBufferView = nullptr, | ||
| 1384 | }; | ||
| 1385 | |||
| 1386 | device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, sampler_write_2}, {}); | ||
| 1387 | } | ||
| 1388 | |||
| 1389 | void BlitScreen::UpdateDescriptorSet(VkImageView image_view, bool nn) const { | ||
| 1390 | const VkDescriptorBufferInfo buffer_info{ | ||
| 1391 | .buffer = *buffer, | ||
| 1392 | .offset = offsetof(BufferData, uniform), | ||
| 1393 | .range = sizeof(BufferData::uniform), | ||
| 1394 | }; | ||
| 1395 | |||
| 1396 | const VkWriteDescriptorSet ubo_write{ | ||
| 1397 | .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||
| 1398 | .pNext = nullptr, | ||
| 1399 | .dstSet = descriptor_sets[image_index], | ||
| 1400 | .dstBinding = 0, | ||
| 1401 | .dstArrayElement = 0, | ||
| 1402 | .descriptorCount = 1, | ||
| 1403 | .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, | ||
| 1404 | .pImageInfo = nullptr, | ||
| 1405 | .pBufferInfo = &buffer_info, | ||
| 1406 | .pTexelBufferView = nullptr, | ||
| 1407 | }; | ||
| 1408 | |||
| 1409 | const VkDescriptorImageInfo image_info{ | ||
| 1410 | .sampler = nn ? *nn_sampler : *sampler, | ||
| 1411 | .imageView = image_view, | ||
| 1412 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 1413 | }; | ||
| 1414 | |||
| 1415 | const VkWriteDescriptorSet sampler_write{ | ||
| 1416 | .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||
| 1417 | .pNext = nullptr, | ||
| 1418 | .dstSet = descriptor_sets[image_index], | ||
| 1419 | .dstBinding = 1, | ||
| 1420 | .dstArrayElement = 0, | ||
| 1421 | .descriptorCount = 1, | ||
| 1422 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 1423 | .pImageInfo = &image_info, | ||
| 1424 | .pBufferInfo = nullptr, | ||
| 1425 | .pTexelBufferView = nullptr, | ||
| 1426 | }; | ||
| 1427 | |||
| 1428 | device.GetLogical().UpdateDescriptorSets(std::array{ubo_write, sampler_write}, {}); | ||
| 1429 | } | ||
| 1430 | |||
| 1431 | void BlitScreen::SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const { | ||
| 1432 | data.uniform.modelview_matrix = | ||
| 1433 | MakeOrthographicMatrix(static_cast<f32>(layout.width), static_cast<f32>(layout.height)); | ||
| 1434 | } | ||
| 1435 | |||
| 1436 | void BlitScreen::SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, | ||
| 1437 | const Layout::FramebufferLayout layout) const { | ||
| 1438 | f32 left, top, right, bottom; | ||
| 1439 | |||
| 1440 | if (fsr) { | ||
| 1441 | // FSR has already applied the crop, so we just want to render the image | ||
| 1442 | // it has produced. | ||
| 1443 | left = 0; | ||
| 1444 | top = 0; | ||
| 1445 | right = 1; | ||
| 1446 | bottom = 1; | ||
| 1447 | } else { | ||
| 1448 | // Get the normalized crop rectangle. | ||
| 1449 | const auto crop = NormalizeCrop(framebuffer, screen_info); | ||
| 1450 | |||
| 1451 | // Apply the crop. | ||
| 1452 | left = crop.left; | ||
| 1453 | top = crop.top; | ||
| 1454 | right = crop.right; | ||
| 1455 | bottom = crop.bottom; | ||
| 1456 | } | ||
| 1457 | |||
| 1458 | // Map the coordinates to the screen. | ||
| 1459 | const auto& screen = layout.screen; | ||
| 1460 | const auto x = static_cast<f32>(screen.left); | ||
| 1461 | const auto y = static_cast<f32>(screen.top); | ||
| 1462 | const auto w = static_cast<f32>(screen.GetWidth()); | ||
| 1463 | const auto h = static_cast<f32>(screen.GetHeight()); | ||
| 1464 | |||
| 1465 | data.vertices[0] = ScreenRectVertex(x, y, left, top); | ||
| 1466 | data.vertices[1] = ScreenRectVertex(x + w, y, right, top); | ||
| 1467 | data.vertices[2] = ScreenRectVertex(x, y + h, left, bottom); | ||
| 1468 | data.vertices[3] = ScreenRectVertex(x + w, y + h, right, bottom); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | void BlitScreen::CreateSMAA(VkExtent2D smaa_size) { | ||
| 1472 | smaa = std::make_unique<SMAA>(device, memory_allocator, image_count, smaa_size); | ||
| 1473 | } | ||
| 1474 | |||
| 1475 | void BlitScreen::CreateFSR() { | ||
| 1476 | const auto& layout = render_window.GetFramebufferLayout(); | ||
| 1477 | const VkExtent2D fsr_size{ | ||
| 1478 | .width = layout.screen.GetWidth(), | ||
| 1479 | .height = layout.screen.GetHeight(), | ||
| 1480 | }; | ||
| 1481 | fsr = std::make_unique<FSR>(device, memory_allocator, image_count, fsr_size); | ||
| 1482 | } | ||
| 1483 | |||
| 1484 | u64 BlitScreen::CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const { | ||
| 1485 | return sizeof(BufferData) + GetSizeInBytes(framebuffer) * image_count; | ||
| 1486 | } | ||
| 1487 | |||
| 1488 | u64 BlitScreen::GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const { | ||
| 1489 | constexpr auto first_image_offset = static_cast<u64>(sizeof(BufferData)); | ||
| 1490 | return first_image_offset + GetSizeInBytes(framebuffer) * image_index; | ||
| 1491 | } | ||
| 1492 | |||
| 1493 | } // namespace Vulkan | 149 | } // 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 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <list> | ||
| 6 | #include <memory> | 7 | #include <memory> |
| 7 | 8 | ||
| 8 | #include "core/frontend/framebuffer_layout.h" | 9 | #include "core/frontend/framebuffer_layout.h" |
| 9 | #include "video_core/host1x/gpu_device_memory_manager.h" | 10 | #include "video_core/host1x/gpu_device_memory_manager.h" |
| 11 | #include "video_core/renderer_vulkan/present/layer.h" | ||
| 10 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | 12 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" |
| 11 | #include "video_core/vulkan_common/vulkan_wrapper.h" | 13 | #include "video_core/vulkan_common/vulkan_wrapper.h" |
| 12 | 14 | ||
| @@ -14,155 +16,67 @@ namespace Core { | |||
| 14 | class System; | 16 | class System; |
| 15 | } | 17 | } |
| 16 | 18 | ||
| 17 | namespace Core::Frontend { | ||
| 18 | class EmuWindow; | ||
| 19 | } | ||
| 20 | |||
| 21 | namespace Tegra { | 19 | namespace Tegra { |
| 22 | struct FramebufferConfig; | 20 | struct FramebufferConfig; |
| 23 | } | 21 | } |
| 24 | 22 | ||
| 25 | namespace VideoCore { | 23 | namespace Settings { |
| 26 | class RasterizerInterface; | 24 | enum class ScalingFilter : u32; |
| 27 | } | 25 | } // namespace Settings |
| 28 | |||
| 29 | namespace Service::android { | ||
| 30 | enum class PixelFormat : u32; | ||
| 31 | } | ||
| 32 | 26 | ||
| 33 | namespace Vulkan { | 27 | namespace Vulkan { |
| 34 | 28 | ||
| 35 | struct ScreenInfo; | ||
| 36 | |||
| 37 | class Device; | 29 | class Device; |
| 38 | class FSR; | ||
| 39 | class RasterizerVulkan; | 30 | class RasterizerVulkan; |
| 40 | class Scheduler; | 31 | class Scheduler; |
| 41 | class SMAA; | ||
| 42 | class Swapchain; | ||
| 43 | class PresentManager; | 32 | class PresentManager; |
| 33 | class WindowAdaptPass; | ||
| 44 | 34 | ||
| 45 | struct Frame; | 35 | struct Frame; |
| 46 | 36 | ||
| 47 | struct ScreenInfo { | 37 | struct FramebufferTextureInfo { |
| 48 | VkImage image{}; | 38 | VkImage image{}; |
| 49 | VkImageView image_view{}; | 39 | VkImageView image_view{}; |
| 50 | u32 width{}; | 40 | u32 width{}; |
| 51 | u32 height{}; | 41 | u32 height{}; |
| 42 | u32 scaled_width{}; | ||
| 43 | u32 scaled_height{}; | ||
| 52 | }; | 44 | }; |
| 53 | 45 | ||
| 54 | class BlitScreen { | 46 | class BlitScreen { |
| 55 | public: | 47 | public: |
| 56 | explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, | 48 | explicit BlitScreen(Tegra::MaxwellDeviceMemoryManager& device_memory, const Device& device, |
| 57 | Core::Frontend::EmuWindow& render_window, const Device& device, | 49 | MemoryAllocator& memory_allocator, PresentManager& present_manager, |
| 58 | MemoryAllocator& memory_manager, Swapchain& swapchain, | 50 | Scheduler& scheduler); |
| 59 | PresentManager& present_manager, Scheduler& scheduler, | ||
| 60 | const ScreenInfo& screen_info); | ||
| 61 | ~BlitScreen(); | 51 | ~BlitScreen(); |
| 62 | 52 | ||
| 63 | void Recreate(); | 53 | void DrawToFrame(RasterizerVulkan& rasterizer, Frame* frame, |
| 64 | 54 | std::span<const Tegra::FramebufferConfig> framebuffers, | |
| 65 | void Draw(const Tegra::FramebufferConfig& framebuffer, const VkFramebuffer& host_framebuffer, | 55 | const Layout::FramebufferLayout& layout, size_t current_swapchain_image_count, |
| 66 | const Layout::FramebufferLayout layout, VkExtent2D render_area, bool use_accelerated); | 56 | VkFormat current_swapchain_view_format); |
| 67 | |||
| 68 | void DrawToSwapchain(Frame* frame, const Tegra::FramebufferConfig& framebuffer, | ||
| 69 | bool use_accelerated); | ||
| 70 | 57 | ||
| 71 | [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, | 58 | [[nodiscard]] vk::Framebuffer CreateFramebuffer(const Layout::FramebufferLayout& layout, |
| 72 | VkExtent2D extent); | 59 | VkImageView image_view, |
| 73 | 60 | VkFormat current_view_format); | |
| 74 | [[nodiscard]] vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, | ||
| 75 | VkExtent2D extent, vk::RenderPass& rd); | ||
| 76 | 61 | ||
| 77 | private: | 62 | private: |
| 78 | struct BufferData; | 63 | void WaitIdle(); |
| 79 | 64 | void SetWindowAdaptPass(); | |
| 80 | void CreateStaticResources(); | 65 | vk::Framebuffer CreateFramebuffer(const VkImageView& image_view, VkExtent2D extent, |
| 81 | void CreateShaders(); | 66 | VkRenderPass render_pass); |
| 82 | void CreateDescriptorPool(); | ||
| 83 | void CreateRenderPass(); | ||
| 84 | vk::RenderPass CreateRenderPassImpl(VkFormat format); | ||
| 85 | void CreateDescriptorSetLayout(); | ||
| 86 | void CreateDescriptorSets(); | ||
| 87 | void CreatePipelineLayout(); | ||
| 88 | void CreateGraphicsPipeline(); | ||
| 89 | void CreateSampler(); | ||
| 90 | |||
| 91 | void CreateDynamicResources(); | ||
| 92 | |||
| 93 | void RefreshResources(const Tegra::FramebufferConfig& framebuffer); | ||
| 94 | void ReleaseRawImages(); | ||
| 95 | void CreateStagingBuffer(const Tegra::FramebufferConfig& framebuffer); | ||
| 96 | void CreateRawImages(const Tegra::FramebufferConfig& framebuffer); | ||
| 97 | |||
| 98 | void UpdateDescriptorSet(VkImageView image_view, bool nn) const; | ||
| 99 | void UpdateAADescriptorSet(VkImageView image_view, bool nn) const; | ||
| 100 | void SetUniformData(BufferData& data, const Layout::FramebufferLayout layout) const; | ||
| 101 | void SetVertexData(BufferData& data, const Tegra::FramebufferConfig& framebuffer, | ||
| 102 | const Layout::FramebufferLayout layout) const; | ||
| 103 | |||
| 104 | void CreateSMAA(VkExtent2D smaa_size); | ||
| 105 | void CreateFSR(); | ||
| 106 | |||
| 107 | u64 CalculateBufferSize(const Tegra::FramebufferConfig& framebuffer) const; | ||
| 108 | u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer) const; | ||
| 109 | 67 | ||
| 110 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 68 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| 111 | Core::Frontend::EmuWindow& render_window; | ||
| 112 | const Device& device; | 69 | const Device& device; |
| 113 | MemoryAllocator& memory_allocator; | 70 | MemoryAllocator& memory_allocator; |
| 114 | Swapchain& swapchain; | ||
| 115 | PresentManager& present_manager; | 71 | PresentManager& present_manager; |
| 116 | Scheduler& scheduler; | 72 | Scheduler& scheduler; |
| 117 | std::size_t image_count; | 73 | std::size_t image_count{}; |
| 118 | std::size_t image_index{}; | 74 | std::size_t image_index{}; |
| 119 | const ScreenInfo& screen_info; | 75 | VkFormat swapchain_view_format{}; |
| 120 | 76 | ||
| 121 | vk::ShaderModule vertex_shader; | 77 | Settings::ScalingFilter scaling_filter{}; |
| 122 | vk::ShaderModule fxaa_vertex_shader; | 78 | std::unique_ptr<WindowAdaptPass> window_adapt{}; |
| 123 | vk::ShaderModule fxaa_fragment_shader; | 79 | std::list<Layer> layers{}; |
| 124 | vk::ShaderModule bilinear_fragment_shader; | ||
| 125 | vk::ShaderModule bicubic_fragment_shader; | ||
| 126 | vk::ShaderModule gaussian_fragment_shader; | ||
| 127 | vk::ShaderModule scaleforce_fragment_shader; | ||
| 128 | vk::DescriptorPool descriptor_pool; | ||
| 129 | vk::DescriptorSetLayout descriptor_set_layout; | ||
| 130 | vk::PipelineLayout pipeline_layout; | ||
| 131 | vk::Pipeline nearest_neighbor_pipeline; | ||
| 132 | vk::Pipeline bilinear_pipeline; | ||
| 133 | vk::Pipeline bicubic_pipeline; | ||
| 134 | vk::Pipeline gaussian_pipeline; | ||
| 135 | vk::Pipeline scaleforce_pipeline; | ||
| 136 | vk::RenderPass renderpass; | ||
| 137 | vk::DescriptorSets descriptor_sets; | ||
| 138 | vk::Sampler nn_sampler; | ||
| 139 | vk::Sampler sampler; | ||
| 140 | |||
| 141 | vk::Buffer buffer; | ||
| 142 | |||
| 143 | std::vector<u64> resource_ticks; | ||
| 144 | |||
| 145 | std::vector<vk::Image> raw_images; | ||
| 146 | std::vector<vk::ImageView> raw_image_views; | ||
| 147 | |||
| 148 | vk::DescriptorPool aa_descriptor_pool; | ||
| 149 | vk::DescriptorSetLayout aa_descriptor_set_layout; | ||
| 150 | vk::PipelineLayout aa_pipeline_layout; | ||
| 151 | vk::Pipeline aa_pipeline; | ||
| 152 | vk::RenderPass aa_renderpass; | ||
| 153 | vk::Framebuffer aa_framebuffer; | ||
| 154 | vk::DescriptorSets aa_descriptor_sets; | ||
| 155 | vk::Image aa_image; | ||
| 156 | vk::ImageView aa_image_view; | ||
| 157 | |||
| 158 | u32 raw_width = 0; | ||
| 159 | u32 raw_height = 0; | ||
| 160 | Service::android::PixelFormat pixel_format{}; | ||
| 161 | VkFormat framebuffer_view_format; | ||
| 162 | VkFormat swapchain_view_format; | ||
| 163 | |||
| 164 | std::unique_ptr<FSR> fsr; | ||
| 165 | std::unique_ptr<SMAA> smaa; | ||
| 166 | }; | 80 | }; |
| 167 | 81 | ||
| 168 | } // namespace Vulkan | 82 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #include "common/common_types.h" | ||
| 5 | #include "common/div_ceil.h" | ||
| 6 | #include "common/settings.h" | ||
| 7 | |||
| 8 | #include "video_core/fsr.h" | ||
| 9 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp16_comp_spv.h" | ||
| 10 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_easu_fp32_comp_spv.h" | ||
| 11 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp16_comp_spv.h" | ||
| 12 | #include "video_core/host_shaders/vulkan_fidelityfx_fsr_rcas_fp32_comp_spv.h" | ||
| 13 | #include "video_core/renderer_vulkan/vk_fsr.h" | ||
| 14 | #include "video_core/renderer_vulkan/vk_scheduler.h" | ||
| 15 | #include "video_core/renderer_vulkan/vk_shader_util.h" | ||
| 16 | #include "video_core/vulkan_common/vulkan_device.h" | ||
| 17 | |||
| 18 | namespace Vulkan { | ||
| 19 | using namespace FSR; | ||
| 20 | |||
| 21 | FSR::FSR(const Device& device_, MemoryAllocator& memory_allocator_, size_t image_count_, | ||
| 22 | VkExtent2D output_size_) | ||
| 23 | : device{device_}, memory_allocator{memory_allocator_}, image_count{image_count_}, | ||
| 24 | output_size{output_size_} { | ||
| 25 | |||
| 26 | CreateImages(); | ||
| 27 | CreateSampler(); | ||
| 28 | CreateShaders(); | ||
| 29 | CreateDescriptorPool(); | ||
| 30 | CreateDescriptorSetLayout(); | ||
| 31 | CreateDescriptorSets(); | ||
| 32 | CreatePipelineLayout(); | ||
| 33 | CreatePipeline(); | ||
| 34 | } | ||
| 35 | |||
| 36 | VkImageView FSR::Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, | ||
| 37 | VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect) { | ||
| 38 | |||
| 39 | UpdateDescriptorSet(image_index, image_view); | ||
| 40 | |||
| 41 | scheduler.Record([this, image_index, input_image_extent, crop_rect](vk::CommandBuffer cmdbuf) { | ||
| 42 | const VkImageMemoryBarrier base_barrier{ | ||
| 43 | .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||
| 44 | .pNext = nullptr, | ||
| 45 | .srcAccessMask = 0, | ||
| 46 | .dstAccessMask = 0, | ||
| 47 | .oldLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 48 | .newLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 49 | .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 50 | .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||
| 51 | .image = {}, | ||
| 52 | .subresourceRange = | ||
| 53 | { | ||
| 54 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 55 | .baseMipLevel = 0, | ||
| 56 | .levelCount = 1, | ||
| 57 | .baseArrayLayer = 0, | ||
| 58 | .layerCount = 1, | ||
| 59 | }, | ||
| 60 | }; | ||
| 61 | |||
| 62 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *easu_pipeline); | ||
| 63 | |||
| 64 | const f32 input_image_width = static_cast<f32>(input_image_extent.width); | ||
| 65 | const f32 input_image_height = static_cast<f32>(input_image_extent.height); | ||
| 66 | const f32 output_image_width = static_cast<f32>(output_size.width); | ||
| 67 | const f32 output_image_height = static_cast<f32>(output_size.height); | ||
| 68 | const f32 viewport_width = (crop_rect.right - crop_rect.left) * input_image_width; | ||
| 69 | const f32 viewport_x = crop_rect.left * input_image_width; | ||
| 70 | const f32 viewport_height = (crop_rect.bottom - crop_rect.top) * input_image_height; | ||
| 71 | const f32 viewport_y = crop_rect.top * input_image_height; | ||
| 72 | |||
| 73 | std::array<u32, 4 * 4> push_constants; | ||
| 74 | FsrEasuConOffset(push_constants.data() + 0, push_constants.data() + 4, | ||
| 75 | push_constants.data() + 8, push_constants.data() + 12, | ||
| 76 | |||
| 77 | viewport_width, viewport_height, input_image_width, input_image_height, | ||
| 78 | output_image_width, output_image_height, viewport_x, viewport_y); | ||
| 79 | cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); | ||
| 80 | |||
| 81 | { | ||
| 82 | VkImageMemoryBarrier fsr_write_barrier = base_barrier; | ||
| 83 | fsr_write_barrier.image = *images[image_index]; | ||
| 84 | fsr_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||
| 85 | |||
| 86 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, | ||
| 87 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, fsr_write_barrier); | ||
| 88 | } | ||
| 89 | |||
| 90 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0, | ||
| 91 | descriptor_sets[image_index * 2], {}); | ||
| 92 | cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u), | ||
| 93 | Common::DivCeil(output_size.height, 16u), 1); | ||
| 94 | |||
| 95 | cmdbuf.BindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, *rcas_pipeline); | ||
| 96 | |||
| 97 | const float sharpening = | ||
| 98 | static_cast<float>(Settings::values.fsr_sharpening_slider.GetValue()) / 100.0f; | ||
| 99 | |||
| 100 | FsrRcasCon(push_constants.data(), sharpening); | ||
| 101 | cmdbuf.PushConstants(*pipeline_layout, VK_SHADER_STAGE_COMPUTE_BIT, push_constants); | ||
| 102 | |||
| 103 | { | ||
| 104 | std::array<VkImageMemoryBarrier, 2> barriers; | ||
| 105 | auto& fsr_read_barrier = barriers[0]; | ||
| 106 | auto& blit_write_barrier = barriers[1]; | ||
| 107 | |||
| 108 | fsr_read_barrier = base_barrier; | ||
| 109 | fsr_read_barrier.image = *images[image_index]; | ||
| 110 | fsr_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; | ||
| 111 | fsr_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||
| 112 | |||
| 113 | blit_write_barrier = base_barrier; | ||
| 114 | blit_write_barrier.image = *images[image_count + image_index]; | ||
| 115 | blit_write_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | ||
| 116 | blit_write_barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; | ||
| 117 | |||
| 118 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||
| 119 | VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, {}, {}, barriers); | ||
| 120 | } | ||
| 121 | |||
| 122 | cmdbuf.BindDescriptorSets(VK_PIPELINE_BIND_POINT_COMPUTE, *pipeline_layout, 0, | ||
| 123 | descriptor_sets[image_index * 2 + 1], {}); | ||
| 124 | cmdbuf.Dispatch(Common::DivCeil(output_size.width, 16u), | ||
| 125 | Common::DivCeil(output_size.height, 16u), 1); | ||
| 126 | |||
| 127 | { | ||
| 128 | std::array<VkImageMemoryBarrier, 1> barriers; | ||
| 129 | auto& blit_read_barrier = barriers[0]; | ||
| 130 | |||
| 131 | blit_read_barrier = base_barrier; | ||
| 132 | blit_read_barrier.image = *images[image_count + image_index]; | ||
| 133 | blit_read_barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; | ||
| 134 | blit_read_barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; | ||
| 135 | |||
| 136 | cmdbuf.PipelineBarrier(VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, | ||
| 137 | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, {}, {}, barriers); | ||
| 138 | } | ||
| 139 | }); | ||
| 140 | |||
| 141 | return *image_views[image_count + image_index]; | ||
| 142 | } | ||
| 143 | |||
| 144 | void FSR::CreateDescriptorPool() { | ||
| 145 | const std::array<VkDescriptorPoolSize, 2> pool_sizes{{ | ||
| 146 | { | ||
| 147 | .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 148 | .descriptorCount = static_cast<u32>(image_count * 2), | ||
| 149 | }, | ||
| 150 | { | ||
| 151 | .type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, | ||
| 152 | .descriptorCount = static_cast<u32>(image_count * 2), | ||
| 153 | }, | ||
| 154 | }}; | ||
| 155 | |||
| 156 | const VkDescriptorPoolCreateInfo ci{ | ||
| 157 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | ||
| 158 | .pNext = nullptr, | ||
| 159 | .flags = 0, | ||
| 160 | .maxSets = static_cast<u32>(image_count * 2), | ||
| 161 | .poolSizeCount = static_cast<u32>(pool_sizes.size()), | ||
| 162 | .pPoolSizes = pool_sizes.data(), | ||
| 163 | }; | ||
| 164 | descriptor_pool = device.GetLogical().CreateDescriptorPool(ci); | ||
| 165 | } | ||
| 166 | |||
| 167 | void FSR::CreateDescriptorSetLayout() { | ||
| 168 | const std::array<VkDescriptorSetLayoutBinding, 2> layout_bindings{{ | ||
| 169 | { | ||
| 170 | .binding = 0, | ||
| 171 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 172 | .descriptorCount = 1, | ||
| 173 | .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | ||
| 174 | .pImmutableSamplers = sampler.address(), | ||
| 175 | }, | ||
| 176 | { | ||
| 177 | .binding = 1, | ||
| 178 | .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, | ||
| 179 | .descriptorCount = 1, | ||
| 180 | .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | ||
| 181 | .pImmutableSamplers = sampler.address(), | ||
| 182 | }, | ||
| 183 | }}; | ||
| 184 | |||
| 185 | const VkDescriptorSetLayoutCreateInfo ci{ | ||
| 186 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, | ||
| 187 | .pNext = nullptr, | ||
| 188 | .flags = 0, | ||
| 189 | .bindingCount = static_cast<u32>(layout_bindings.size()), | ||
| 190 | .pBindings = layout_bindings.data(), | ||
| 191 | }; | ||
| 192 | |||
| 193 | descriptor_set_layout = device.GetLogical().CreateDescriptorSetLayout(ci); | ||
| 194 | } | ||
| 195 | |||
| 196 | void FSR::CreateDescriptorSets() { | ||
| 197 | const u32 sets = static_cast<u32>(image_count * 2); | ||
| 198 | const std::vector layouts(sets, *descriptor_set_layout); | ||
| 199 | |||
| 200 | const VkDescriptorSetAllocateInfo ai{ | ||
| 201 | .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, | ||
| 202 | .pNext = nullptr, | ||
| 203 | .descriptorPool = *descriptor_pool, | ||
| 204 | .descriptorSetCount = sets, | ||
| 205 | .pSetLayouts = layouts.data(), | ||
| 206 | }; | ||
| 207 | |||
| 208 | descriptor_sets = descriptor_pool.Allocate(ai); | ||
| 209 | } | ||
| 210 | |||
| 211 | void FSR::CreateImages() { | ||
| 212 | images.resize(image_count * 2); | ||
| 213 | image_views.resize(image_count * 2); | ||
| 214 | |||
| 215 | for (size_t i = 0; i < image_count * 2; ++i) { | ||
| 216 | images[i] = memory_allocator.CreateImage(VkImageCreateInfo{ | ||
| 217 | .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | ||
| 218 | .pNext = nullptr, | ||
| 219 | .flags = 0, | ||
| 220 | .imageType = VK_IMAGE_TYPE_2D, | ||
| 221 | .format = VK_FORMAT_R16G16B16A16_SFLOAT, | ||
| 222 | .extent = | ||
| 223 | { | ||
| 224 | .width = output_size.width, | ||
| 225 | .height = output_size.height, | ||
| 226 | .depth = 1, | ||
| 227 | }, | ||
| 228 | .mipLevels = 1, | ||
| 229 | .arrayLayers = 1, | ||
| 230 | .samples = VK_SAMPLE_COUNT_1_BIT, | ||
| 231 | .tiling = VK_IMAGE_TILING_OPTIMAL, | ||
| 232 | .usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT | | ||
| 233 | VK_IMAGE_USAGE_SAMPLED_BIT, | ||
| 234 | .sharingMode = VK_SHARING_MODE_EXCLUSIVE, | ||
| 235 | .queueFamilyIndexCount = 0, | ||
| 236 | .pQueueFamilyIndices = nullptr, | ||
| 237 | .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED, | ||
| 238 | }); | ||
| 239 | image_views[i] = device.GetLogical().CreateImageView(VkImageViewCreateInfo{ | ||
| 240 | .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | ||
| 241 | .pNext = nullptr, | ||
| 242 | .flags = 0, | ||
| 243 | .image = *images[i], | ||
| 244 | .viewType = VK_IMAGE_VIEW_TYPE_2D, | ||
| 245 | .format = VK_FORMAT_R16G16B16A16_SFLOAT, | ||
| 246 | .components = | ||
| 247 | { | ||
| 248 | .r = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 249 | .g = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 250 | .b = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 251 | .a = VK_COMPONENT_SWIZZLE_IDENTITY, | ||
| 252 | }, | ||
| 253 | .subresourceRange = | ||
| 254 | { | ||
| 255 | .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, | ||
| 256 | .baseMipLevel = 0, | ||
| 257 | .levelCount = 1, | ||
| 258 | .baseArrayLayer = 0, | ||
| 259 | .layerCount = 1, | ||
| 260 | }, | ||
| 261 | }); | ||
| 262 | } | ||
| 263 | } | ||
| 264 | |||
| 265 | void FSR::CreatePipelineLayout() { | ||
| 266 | VkPushConstantRange push_const{ | ||
| 267 | .stageFlags = VK_SHADER_STAGE_COMPUTE_BIT, | ||
| 268 | .offset = 0, | ||
| 269 | .size = sizeof(std::array<u32, 4 * 4>), | ||
| 270 | }; | ||
| 271 | VkPipelineLayoutCreateInfo ci{ | ||
| 272 | .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||
| 273 | .pNext = nullptr, | ||
| 274 | .flags = 0, | ||
| 275 | .setLayoutCount = 1, | ||
| 276 | .pSetLayouts = descriptor_set_layout.address(), | ||
| 277 | .pushConstantRangeCount = 1, | ||
| 278 | .pPushConstantRanges = &push_const, | ||
| 279 | }; | ||
| 280 | |||
| 281 | pipeline_layout = device.GetLogical().CreatePipelineLayout(ci); | ||
| 282 | } | ||
| 283 | |||
| 284 | void FSR::UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const { | ||
| 285 | const auto fsr_image_view = *image_views[image_index]; | ||
| 286 | const auto blit_image_view = *image_views[image_count + image_index]; | ||
| 287 | |||
| 288 | const VkDescriptorImageInfo image_info{ | ||
| 289 | .sampler = VK_NULL_HANDLE, | ||
| 290 | .imageView = image_view, | ||
| 291 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 292 | }; | ||
| 293 | const VkDescriptorImageInfo fsr_image_info{ | ||
| 294 | .sampler = VK_NULL_HANDLE, | ||
| 295 | .imageView = fsr_image_view, | ||
| 296 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 297 | }; | ||
| 298 | const VkDescriptorImageInfo blit_image_info{ | ||
| 299 | .sampler = VK_NULL_HANDLE, | ||
| 300 | .imageView = blit_image_view, | ||
| 301 | .imageLayout = VK_IMAGE_LAYOUT_GENERAL, | ||
| 302 | }; | ||
| 303 | |||
| 304 | VkWriteDescriptorSet sampler_write{ | ||
| 305 | .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||
| 306 | .pNext = nullptr, | ||
| 307 | .dstSet = descriptor_sets[image_index * 2], | ||
| 308 | .dstBinding = 0, | ||
| 309 | .dstArrayElement = 0, | ||
| 310 | .descriptorCount = 1, | ||
| 311 | .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, | ||
| 312 | .pImageInfo = &image_info, | ||
| 313 | .pBufferInfo = nullptr, | ||
| 314 | .pTexelBufferView = nullptr, | ||
| 315 | }; | ||
| 316 | |||
| 317 | VkWriteDescriptorSet output_write{ | ||
| 318 | .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||
| 319 | .pNext = nullptr, | ||
| 320 | .dstSet = descriptor_sets[image_index * 2], | ||
| 321 | .dstBinding = 1, | ||
| 322 | .dstArrayElement = 0, | ||
| 323 | .descriptorCount = 1, | ||
| 324 | .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, | ||
| 325 | .pImageInfo = &fsr_image_info, | ||
| 326 | .pBufferInfo = nullptr, | ||
| 327 | .pTexelBufferView = nullptr, | ||
| 328 | }; | ||
| 329 | |||
| 330 | device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {}); | ||
| 331 | |||
| 332 | sampler_write.dstSet = descriptor_sets[image_index * 2 + 1]; | ||
| 333 | sampler_write.pImageInfo = &fsr_image_info; | ||
| 334 | output_write.dstSet = descriptor_sets[image_index * 2 + 1]; | ||
| 335 | output_write.pImageInfo = &blit_image_info; | ||
| 336 | |||
| 337 | device.GetLogical().UpdateDescriptorSets(std::array{sampler_write, output_write}, {}); | ||
| 338 | } | ||
| 339 | |||
| 340 | void FSR::CreateSampler() { | ||
| 341 | const VkSamplerCreateInfo ci{ | ||
| 342 | .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, | ||
| 343 | .pNext = nullptr, | ||
| 344 | .flags = 0, | ||
| 345 | .magFilter = VK_FILTER_LINEAR, | ||
| 346 | .minFilter = VK_FILTER_LINEAR, | ||
| 347 | .mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR, | ||
| 348 | .addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | ||
| 349 | .addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | ||
| 350 | .addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE, | ||
| 351 | .mipLodBias = 0.0f, | ||
| 352 | .anisotropyEnable = VK_FALSE, | ||
| 353 | .maxAnisotropy = 0.0f, | ||
| 354 | .compareEnable = VK_FALSE, | ||
| 355 | .compareOp = VK_COMPARE_OP_NEVER, | ||
| 356 | .minLod = 0.0f, | ||
| 357 | .maxLod = 0.0f, | ||
| 358 | .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_BLACK, | ||
| 359 | .unnormalizedCoordinates = VK_FALSE, | ||
| 360 | }; | ||
| 361 | |||
| 362 | sampler = device.GetLogical().CreateSampler(ci); | ||
| 363 | } | ||
| 364 | |||
| 365 | void FSR::CreateShaders() { | ||
| 366 | if (device.IsFloat16Supported()) { | ||
| 367 | easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP16_COMP_SPV); | ||
| 368 | rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP16_COMP_SPV); | ||
| 369 | } else { | ||
| 370 | easu_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_EASU_FP32_COMP_SPV); | ||
| 371 | rcas_shader = BuildShader(device, VULKAN_FIDELITYFX_FSR_RCAS_FP32_COMP_SPV); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | void FSR::CreatePipeline() { | ||
| 376 | VkPipelineShaderStageCreateInfo shader_stage_easu{ | ||
| 377 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 378 | .pNext = nullptr, | ||
| 379 | .flags = 0, | ||
| 380 | .stage = VK_SHADER_STAGE_COMPUTE_BIT, | ||
| 381 | .module = *easu_shader, | ||
| 382 | .pName = "main", | ||
| 383 | .pSpecializationInfo = nullptr, | ||
| 384 | }; | ||
| 385 | |||
| 386 | VkPipelineShaderStageCreateInfo shader_stage_rcas{ | ||
| 387 | .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||
| 388 | .pNext = nullptr, | ||
| 389 | .flags = 0, | ||
| 390 | .stage = VK_SHADER_STAGE_COMPUTE_BIT, | ||
| 391 | .module = *rcas_shader, | ||
| 392 | .pName = "main", | ||
| 393 | .pSpecializationInfo = nullptr, | ||
| 394 | }; | ||
| 395 | |||
| 396 | VkComputePipelineCreateInfo pipeline_ci_easu{ | ||
| 397 | .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, | ||
| 398 | .pNext = nullptr, | ||
| 399 | .flags = 0, | ||
| 400 | .stage = shader_stage_easu, | ||
| 401 | .layout = *pipeline_layout, | ||
| 402 | .basePipelineHandle = VK_NULL_HANDLE, | ||
| 403 | .basePipelineIndex = 0, | ||
| 404 | }; | ||
| 405 | |||
| 406 | VkComputePipelineCreateInfo pipeline_ci_rcas{ | ||
| 407 | .sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO, | ||
| 408 | .pNext = nullptr, | ||
| 409 | .flags = 0, | ||
| 410 | .stage = shader_stage_rcas, | ||
| 411 | .layout = *pipeline_layout, | ||
| 412 | .basePipelineHandle = VK_NULL_HANDLE, | ||
| 413 | .basePipelineIndex = 0, | ||
| 414 | }; | ||
| 415 | |||
| 416 | easu_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci_easu); | ||
| 417 | rcas_pipeline = device.GetLogical().CreateComputePipeline(pipeline_ci_rcas); | ||
| 418 | } | ||
| 419 | |||
| 420 | } // 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 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | ||
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 3 | |||
| 4 | #pragma once | ||
| 5 | |||
| 6 | #include "common/math_util.h" | ||
| 7 | #include "video_core/vulkan_common/vulkan_memory_allocator.h" | ||
| 8 | #include "video_core/vulkan_common/vulkan_wrapper.h" | ||
| 9 | |||
| 10 | namespace Vulkan { | ||
| 11 | |||
| 12 | class Device; | ||
| 13 | class Scheduler; | ||
| 14 | |||
| 15 | class FSR { | ||
| 16 | public: | ||
| 17 | explicit FSR(const Device& device, MemoryAllocator& memory_allocator, size_t image_count, | ||
| 18 | VkExtent2D output_size); | ||
| 19 | VkImageView Draw(Scheduler& scheduler, size_t image_index, VkImageView image_view, | ||
| 20 | VkExtent2D input_image_extent, const Common::Rectangle<f32>& crop_rect); | ||
| 21 | |||
| 22 | private: | ||
| 23 | void CreateDescriptorPool(); | ||
| 24 | void CreateDescriptorSetLayout(); | ||
| 25 | void CreateDescriptorSets(); | ||
| 26 | void CreateImages(); | ||
| 27 | void CreateSampler(); | ||
| 28 | void CreateShaders(); | ||
| 29 | void CreatePipeline(); | ||
| 30 | void CreatePipelineLayout(); | ||
| 31 | |||
| 32 | void UpdateDescriptorSet(std::size_t image_index, VkImageView image_view) const; | ||
| 33 | |||
| 34 | const Device& device; | ||
| 35 | MemoryAllocator& memory_allocator; | ||
| 36 | size_t image_count; | ||
| 37 | VkExtent2D output_size; | ||
| 38 | |||
| 39 | vk::DescriptorPool descriptor_pool; | ||
| 40 | vk::DescriptorSetLayout descriptor_set_layout; | ||
| 41 | vk::DescriptorSets descriptor_sets; | ||
| 42 | vk::PipelineLayout pipeline_layout; | ||
| 43 | vk::ShaderModule easu_shader; | ||
| 44 | vk::ShaderModule rcas_shader; | ||
| 45 | vk::Pipeline easu_pipeline; | ||
| 46 | vk::Pipeline rcas_pipeline; | ||
| 47 | vk::Sampler sampler; | ||
| 48 | std::vector<vk::Image> images; | ||
| 49 | std::vector<vk::ImageView> image_views; | ||
| 50 | }; | ||
| 51 | |||
| 52 | } // 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_, | |||
| 381 | .support_float64 = device.IsFloat64Supported(), | 381 | .support_float64 = device.IsFloat64Supported(), |
| 382 | .support_float16 = device.IsFloat16Supported(), | 382 | .support_float16 = device.IsFloat16Supported(), |
| 383 | .support_int64 = device.IsShaderInt64Supported(), | 383 | .support_int64 = device.IsShaderInt64Supported(), |
| 384 | .needs_demote_reorder = | 384 | .needs_demote_reorder = driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || |
| 385 | driver_id == VK_DRIVER_ID_AMD_PROPRIETARY || driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE, | 385 | driver_id == VK_DRIVER_ID_AMD_OPEN_SOURCE || |
| 386 | driver_id == VK_DRIVER_ID_SAMSUNG_PROPRIETARY, | ||
| 386 | .support_snorm_render_buffer = true, | 387 | .support_snorm_render_buffer = true, |
| 387 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), | 388 | .support_viewport_index_layer = device.IsExtShaderViewportIndexLayerSupported(), |
| 388 | .min_ssbo_alignment = static_cast<u32>(device.GetStorageBufferAlignment()), | 389 | .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, | |||
| 165 | 165 | ||
| 166 | RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, | 166 | RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, |
| 167 | Tegra::MaxwellDeviceMemoryManager& device_memory_, | 167 | Tegra::MaxwellDeviceMemoryManager& device_memory_, |
| 168 | ScreenInfo& screen_info_, const Device& device_, | 168 | const Device& device_, MemoryAllocator& memory_allocator_, |
| 169 | MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, | 169 | StateTracker& state_tracker_, Scheduler& scheduler_) |
| 170 | Scheduler& scheduler_) | 170 | : gpu{gpu_}, device_memory{device_memory_}, device{device_}, |
| 171 | : gpu{gpu_}, device_memory{device_memory_}, screen_info{screen_info_}, device{device_}, | ||
| 172 | memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, | 171 | memory_allocator{memory_allocator_}, state_tracker{state_tracker_}, scheduler{scheduler_}, |
| 173 | staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), | 172 | staging_pool(device, memory_allocator, scheduler), descriptor_pool(device, scheduler), |
| 174 | guest_descriptor_queue(device, scheduler), compute_pass_descriptor_queue(device, scheduler), | 173 | guest_descriptor_queue(device, scheduler), compute_pass_descriptor_queue(device, scheduler), |
| @@ -783,23 +782,29 @@ void RasterizerVulkan::AccelerateInlineToMemory(GPUVAddr address, size_t copy_si | |||
| 783 | query_cache.InvalidateRegion(*cpu_addr, copy_size); | 782 | query_cache.InvalidateRegion(*cpu_addr, copy_size); |
| 784 | } | 783 | } |
| 785 | 784 | ||
| 786 | bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config, | 785 | std::optional<FramebufferTextureInfo> RasterizerVulkan::AccelerateDisplay( |
| 787 | DAddr framebuffer_addr, u32 pixel_stride) { | 786 | const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, u32 pixel_stride) { |
| 788 | if (!framebuffer_addr) { | 787 | if (!framebuffer_addr) { |
| 789 | return false; | 788 | return {}; |
| 790 | } | 789 | } |
| 791 | std::scoped_lock lock{texture_cache.mutex}; | 790 | std::scoped_lock lock{texture_cache.mutex}; |
| 792 | ImageView* const image_view = | 791 | const auto [image_view, scaled] = |
| 793 | texture_cache.TryFindFramebufferImageView(config, framebuffer_addr); | 792 | texture_cache.TryFindFramebufferImageView(config, framebuffer_addr); |
| 794 | if (!image_view) { | 793 | if (!image_view) { |
| 795 | return false; | 794 | return {}; |
| 796 | } | 795 | } |
| 797 | query_cache.NotifySegment(false); | 796 | query_cache.NotifySegment(false); |
| 798 | screen_info.image = image_view->ImageHandle(); | 797 | |
| 799 | screen_info.image_view = image_view->Handle(Shader::TextureType::Color2D); | 798 | const auto& resolution = Settings::values.resolution_info; |
| 800 | screen_info.width = image_view->size.width; | 799 | |
| 801 | screen_info.height = image_view->size.height; | 800 | FramebufferTextureInfo info{}; |
| 802 | return true; | 801 | info.image = image_view->ImageHandle(); |
| 802 | info.image_view = image_view->Handle(Shader::TextureType::Color2D); | ||
| 803 | info.width = image_view->size.width; | ||
| 804 | info.height = image_view->size.height; | ||
| 805 | info.scaled_width = scaled ? resolution.ScaleUp(info.width) : info.width; | ||
| 806 | info.scaled_height = scaled ? resolution.ScaleUp(info.height) : info.height; | ||
| 807 | return info; | ||
| 803 | } | 808 | } |
| 804 | 809 | ||
| 805 | void RasterizerVulkan::LoadDiskResources(u64 title_id, std::stop_token stop_loading, | 810 | 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; | |||
| 43 | 43 | ||
| 44 | namespace Vulkan { | 44 | namespace Vulkan { |
| 45 | 45 | ||
| 46 | struct ScreenInfo; | 46 | struct FramebufferTextureInfo; |
| 47 | 47 | ||
| 48 | class StateTracker; | 48 | class StateTracker; |
| 49 | 49 | ||
| @@ -78,9 +78,8 @@ class RasterizerVulkan final : public VideoCore::RasterizerInterface, | |||
| 78 | public: | 78 | public: |
| 79 | explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, | 79 | explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_, |
| 80 | Tegra::MaxwellDeviceMemoryManager& device_memory_, | 80 | Tegra::MaxwellDeviceMemoryManager& device_memory_, |
| 81 | ScreenInfo& screen_info_, const Device& device_, | 81 | const Device& device_, MemoryAllocator& memory_allocator_, |
| 82 | MemoryAllocator& memory_allocator_, StateTracker& state_tracker_, | 82 | StateTracker& state_tracker_, Scheduler& scheduler_); |
| 83 | Scheduler& scheduler_); | ||
| 84 | ~RasterizerVulkan() override; | 83 | ~RasterizerVulkan() override; |
| 85 | 84 | ||
| 86 | void Draw(bool is_indexed, u32 instance_count) override; | 85 | void Draw(bool is_indexed, u32 instance_count) override; |
| @@ -126,8 +125,6 @@ public: | |||
| 126 | Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; | 125 | Tegra::Engines::AccelerateDMAInterface& AccessAccelerateDMA() override; |
| 127 | void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, | 126 | void AccelerateInlineToMemory(GPUVAddr address, size_t copy_size, |
| 128 | std::span<const u8> memory) override; | 127 | std::span<const u8> memory) override; |
| 129 | bool AccelerateDisplay(const Tegra::FramebufferConfig& config, DAddr framebuffer_addr, | ||
| 130 | u32 pixel_stride) override; | ||
| 131 | void LoadDiskResources(u64 title_id, std::stop_token stop_loading, | 128 | void LoadDiskResources(u64 title_id, std::stop_token stop_loading, |
| 132 | const VideoCore::DiskResourceLoadCallback& callback) override; | 129 | const VideoCore::DiskResourceLoadCallback& callback) override; |
| 133 | 130 | ||
| @@ -137,6 +134,10 @@ public: | |||
| 137 | 134 | ||
| 138 | void ReleaseChannel(s32 channel_id) override; | 135 | void ReleaseChannel(s32 channel_id) override; |
| 139 | 136 | ||
| 137 | std::optional<FramebufferTextureInfo> AccelerateDisplay(const Tegra::FramebufferConfig& config, | ||
| 138 | VAddr framebuffer_addr, | ||
| 139 | u32 pixel_stride); | ||
| 140 | |||
| 140 | private: | 141 | private: |
| 141 | static constexpr size_t MAX_TEXTURES = 192; | 142 | static constexpr size_t MAX_TEXTURES = 192; |
| 142 | static constexpr size_t MAX_IMAGES = 48; | 143 | static constexpr size_t MAX_IMAGES = 48; |
| @@ -182,7 +183,6 @@ private: | |||
| 182 | Tegra::GPU& gpu; | 183 | Tegra::GPU& gpu; |
| 183 | Tegra::MaxwellDeviceMemoryManager& device_memory; | 184 | Tegra::MaxwellDeviceMemoryManager& device_memory; |
| 184 | 185 | ||
| 185 | ScreenInfo& screen_info; | ||
| 186 | const Device& device; | 186 | const Device& device; |
| 187 | MemoryAllocator& memory_allocator; | 187 | MemoryAllocator& memory_allocator; |
| 188 | StateTracker& state_tracker; | 188 | 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, | |||
| 713 | } | 713 | } |
| 714 | 714 | ||
| 715 | template <class P> | 715 | template <class P> |
| 716 | typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView( | 716 | std::pair<typename P::ImageView*, bool> TextureCache<P>::TryFindFramebufferImageView( |
| 717 | const Tegra::FramebufferConfig& config, DAddr cpu_addr) { | 717 | const Tegra::FramebufferConfig& config, DAddr cpu_addr) { |
| 718 | // TODO: Properly implement this | 718 | // TODO: Properly implement this |
| 719 | const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS); | 719 | const auto it = page_table.find(cpu_addr >> YUZU_PAGEBITS); |
| 720 | if (it == page_table.end()) { | 720 | if (it == page_table.end()) { |
| 721 | return nullptr; | 721 | return {}; |
| 722 | } | 722 | } |
| 723 | const auto& image_map_ids = it->second; | 723 | const auto& image_map_ids = it->second; |
| 724 | boost::container::small_vector<ImageId, 4> valid_image_ids; | 724 | boost::container::small_vector<ImageId, 4> valid_image_ids; |
| @@ -747,7 +747,8 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView( | |||
| 747 | 747 | ||
| 748 | const auto GetImageViewForFramebuffer = [&](ImageId image_id) { | 748 | const auto GetImageViewForFramebuffer = [&](ImageId image_id) { |
| 749 | const ImageViewInfo info{ImageViewType::e2D, view_format}; | 749 | const ImageViewInfo info{ImageViewType::e2D, view_format}; |
| 750 | return &slot_image_views[FindOrEmplaceImageView(image_id, info)]; | 750 | return std::make_pair(&slot_image_views[FindOrEmplaceImageView(image_id, info)], |
| 751 | slot_images[image_id].IsRescaled()); | ||
| 751 | }; | 752 | }; |
| 752 | 753 | ||
| 753 | if (valid_image_ids.size() == 1) [[likely]] { | 754 | if (valid_image_ids.size() == 1) [[likely]] { |
| @@ -761,7 +762,7 @@ typename P::ImageView* TextureCache<P>::TryFindFramebufferImageView( | |||
| 761 | return GetImageViewForFramebuffer(*most_recent); | 762 | return GetImageViewForFramebuffer(*most_recent); |
| 762 | } | 763 | } |
| 763 | 764 | ||
| 764 | return nullptr; | 765 | return {}; |
| 765 | } | 766 | } |
| 766 | 767 | ||
| 767 | template <class P> | 768 | 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: | |||
| 212 | const Tegra::Engines::Fermi2D::Config& copy); | 212 | const Tegra::Engines::Fermi2D::Config& copy); |
| 213 | 213 | ||
| 214 | /// Try to find a cached image view in the given CPU address | 214 | /// Try to find a cached image view in the given CPU address |
| 215 | [[nodiscard]] ImageView* TryFindFramebufferImageView(const Tegra::FramebufferConfig& config, | 215 | [[nodiscard]] std::pair<ImageView*, bool> TryFindFramebufferImageView( |
| 216 | DAddr cpu_addr); | 216 | const Tegra::FramebufferConfig& config, DAddr cpu_addr); |
| 217 | 217 | ||
| 218 | /// Return true when there are uncommitted images to be downloaded | 218 | /// Return true when there are uncommitted images to be downloaded |
| 219 | [[nodiscard]] bool HasUncommittedFlushes() const noexcept; | 219 | [[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 { | |||
| 868 | return "Qualcomm"; | 868 | return "Qualcomm"; |
| 869 | case VK_DRIVER_ID_ARM_PROPRIETARY: | 869 | case VK_DRIVER_ID_ARM_PROPRIETARY: |
| 870 | return "Mali"; | 870 | return "Mali"; |
| 871 | case VK_DRIVER_ID_SAMSUNG_PROPRIETARY: | ||
| 872 | return "Xclipse"; | ||
| 871 | case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: | 873 | case VK_DRIVER_ID_GOOGLE_SWIFTSHADER: |
| 872 | return "SwiftShader"; | 874 | return "SwiftShader"; |
| 873 | case VK_DRIVER_ID_BROADCOM_PROPRIETARY: | 875 | 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 | |||
| 73 | ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); | 73 | ui->tabWidget->addTab(graphics_advanced_tab.get(), tr("Adv. Graphics")); |
| 74 | ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); | 74 | ui->tabWidget->addTab(audio_tab.get(), tr("Audio")); |
| 75 | ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles")); | 75 | ui->tabWidget->addTab(input_tab.get(), tr("Input Profiles")); |
| 76 | |||
| 76 | // Only show Linux tab on Unix | 77 | // Only show Linux tab on Unix |
| 78 | linux_tab->setVisible(false); | ||
| 77 | #ifdef __unix__ | 79 | #ifdef __unix__ |
| 80 | linux_tab->setVisible(true); | ||
| 78 | ui->tabWidget->addTab(linux_tab.get(), tr("Linux")); | 81 | ui->tabWidget->addTab(linux_tab.get(), tr("Linux")); |
| 79 | #endif | 82 | #endif |
| 80 | 83 | ||
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() { | |||
| 1353 | LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true); | 1353 | LinkActionShortcut(ui->action_TAS_Start, QStringLiteral("TAS Start/Stop"), true); |
| 1354 | LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true); | 1354 | LinkActionShortcut(ui->action_TAS_Record, QStringLiteral("TAS Record"), true); |
| 1355 | LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true); | 1355 | LinkActionShortcut(ui->action_TAS_Reset, QStringLiteral("TAS Reset"), true); |
| 1356 | LinkActionShortcut(ui->action_View_Lobby, | ||
| 1357 | QStringLiteral("Multiplayer Browse Public Game Lobby")); | ||
| 1358 | LinkActionShortcut(ui->action_Start_Room, QStringLiteral("Multiplayer Create Room")); | ||
| 1359 | LinkActionShortcut(ui->action_Connect_To_Room, | ||
| 1360 | QStringLiteral("Multiplayer Direct Connect to Room")); | ||
| 1361 | LinkActionShortcut(ui->action_Show_Room, QStringLiteral("Multiplayer Show Current Room")); | ||
| 1362 | LinkActionShortcut(ui->action_Leave_Room, QStringLiteral("Multiplayer Leave Room")); | ||
| 1356 | 1363 | ||
| 1357 | static const QString main_window = QStringLiteral("Main Window"); | 1364 | static const QString main_window = QStringLiteral("Main Window"); |
| 1358 | const auto connect_shortcut = [&]<typename Fn>(const QString& action_name, const Fn& function) { | 1365 | 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, | |||
| 77 | 77 | ||
| 78 | // UI Buttons | 78 | // UI Buttons |
| 79 | connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); | 79 | connect(ui->refresh_list, &QPushButton::clicked, this, &Lobby::RefreshLobby); |
| 80 | connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); | ||
| 80 | connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); | 81 | connect(ui->games_owned, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterOwned); |
| 81 | connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty); | 82 | connect(ui->hide_empty, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterEmpty); |
| 82 | connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); | 83 | connect(ui->hide_full, &QCheckBox::toggled, proxy, &LobbyFilterProxyModel::SetFilterFull); |
| 83 | connect(ui->search, &QLineEdit::textChanged, proxy, &LobbyFilterProxyModel::SetFilterSearch); | ||
| 84 | connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); | 84 | connect(ui->room_list, &QTreeView::doubleClicked, this, &Lobby::OnJoinRoom); |
| 85 | connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom); | 85 | connect(ui->room_list, &QTreeView::clicked, this, &Lobby::OnExpandRoom); |
| 86 | 86 | ||
| 87 | // Actions | 87 | // Actions |
| 88 | connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this, | 88 | connect(&room_list_watcher, &QFutureWatcher<AnnounceMultiplayerRoom::RoomList>::finished, this, |
| 89 | &Lobby::OnRefreshLobby); | 89 | &Lobby::OnRefreshLobby); |
| 90 | |||
| 91 | // Load persistent filters after events are connected to make sure they apply | ||
| 92 | ui->search->setText( | ||
| 93 | QString::fromStdString(UISettings::values.multiplayer_filter_text.GetValue())); | ||
| 94 | ui->games_owned->setChecked(UISettings::values.multiplayer_filter_games_owned.GetValue()); | ||
| 95 | ui->hide_empty->setChecked(UISettings::values.multiplayer_filter_hide_empty.GetValue()); | ||
| 96 | ui->hide_full->setChecked(UISettings::values.multiplayer_filter_hide_full.GetValue()); | ||
| 90 | } | 97 | } |
| 91 | 98 | ||
| 92 | Lobby::~Lobby() = default; | 99 | Lobby::~Lobby() = default; |
| @@ -204,6 +211,10 @@ void Lobby::OnJoinRoom(const QModelIndex& source) { | |||
| 204 | 211 | ||
| 205 | // Save settings | 212 | // Save settings |
| 206 | UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString(); | 213 | UISettings::values.multiplayer_nickname = ui->nickname->text().toStdString(); |
| 214 | UISettings::values.multiplayer_filter_text = ui->search->text().toStdString(); | ||
| 215 | UISettings::values.multiplayer_filter_games_owned = ui->games_owned->isChecked(); | ||
| 216 | UISettings::values.multiplayer_filter_hide_empty = ui->hide_empty->isChecked(); | ||
| 217 | UISettings::values.multiplayer_filter_hide_full = ui->hide_full->isChecked(); | ||
| 207 | UISettings::values.multiplayer_ip = | 218 | UISettings::values.multiplayer_ip = |
| 208 | proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString(); | 219 | proxy->data(connection_index, LobbyItemHost::HostIPRole).value<QString>().toStdString(); |
| 209 | UISettings::values.multiplayer_port = | 220 | 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: | |||
| 193 | } | 193 | } |
| 194 | 194 | ||
| 195 | QVariant data(int role) const override { | 195 | QVariant data(int role) const override { |
| 196 | if (role != Qt::DisplayRole) { | 196 | switch (role) { |
| 197 | case Qt::DisplayRole: { | ||
| 198 | auto members = data(MemberListRole).toList(); | ||
| 199 | return QStringLiteral("%1 / %2 ") | ||
| 200 | .arg(QString::number(members.size()), data(MaxPlayerRole).toString()); | ||
| 201 | } | ||
| 202 | case Qt::ForegroundRole: { | ||
| 203 | auto members = data(MemberListRole).toList(); | ||
| 204 | auto max_players = data(MaxPlayerRole).toInt(); | ||
| 205 | if (members.size() >= max_players) { | ||
| 206 | return QBrush(QColor(255, 48, 32)); | ||
| 207 | } else if (members.size() == (max_players - 1)) { | ||
| 208 | return QBrush(QColor(255, 140, 32)); | ||
| 209 | } else if (members.size() == 0) { | ||
| 210 | return QBrush(QColor(128, 128, 128)); | ||
| 211 | } | ||
| 212 | // FIXME: How to return a value that tells Qt not to modify the | ||
| 213 | // text color from the default (as if Qt::ForegroundRole wasn't overridden)? | ||
| 214 | return QBrush(nullptr); | ||
| 215 | } | ||
| 216 | default: | ||
| 197 | return LobbyItem::data(role); | 217 | return LobbyItem::data(role); |
| 198 | } | 218 | } |
| 199 | auto members = data(MemberListRole).toList(); | ||
| 200 | return QStringLiteral("%1 / %2 ") | ||
| 201 | .arg(QString::number(members.size()), data(MaxPlayerRole).toString()); | ||
| 202 | } | 219 | } |
| 203 | 220 | ||
| 204 | bool operator<(const QStandardItem& other) const override { | 221 | 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 { | |||
| 169 | 169 | ||
| 170 | // multiplayer settings | 170 | // multiplayer settings |
| 171 | Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer}; | 171 | Setting<std::string> multiplayer_nickname{linkage, {}, "nickname", Category::Multiplayer}; |
| 172 | Setting<std::string> multiplayer_filter_text{linkage, {}, "filter_text", Category::Multiplayer}; | ||
| 173 | Setting<bool> multiplayer_filter_games_owned{linkage, false, "filter_games_owned", | ||
| 174 | Category::Multiplayer}; | ||
| 175 | Setting<bool> multiplayer_filter_hide_empty{linkage, false, "filter_games_hide_empty", | ||
| 176 | Category::Multiplayer}; | ||
| 177 | Setting<bool> multiplayer_filter_hide_full{linkage, false, "filter_games_hide_full", | ||
| 178 | Category::Multiplayer}; | ||
| 172 | Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer}; | 179 | Setting<std::string> multiplayer_ip{linkage, {}, "ip", Category::Multiplayer}; |
| 173 | Setting<u16, true> multiplayer_port{linkage, 24872, 0, | 180 | Setting<u16, true> multiplayer_port{linkage, 24872, 0, |
| 174 | UINT16_MAX, "port", Category::Multiplayer}; | 181 | UINT16_MAX, "port", Category::Multiplayer}; |
| @@ -222,7 +229,7 @@ void RestoreWindowState(std::unique_ptr<QtConfig>& qtConfig); | |||
| 222 | // This must be in alphabetical order according to action name as it must have the same order as | 229 | // This must be in alphabetical order according to action name as it must have the same order as |
| 223 | // UISetting::values.shortcuts, which is alphabetically ordered. | 230 | // UISetting::values.shortcuts, which is alphabetically ordered. |
| 224 | // clang-format off | 231 | // clang-format off |
| 225 | const std::array<Shortcut, 23> default_hotkeys{{ | 232 | const std::array<Shortcut, 28> default_hotkeys{{ |
| 226 | {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}}, | 233 | {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}}, |
| 227 | {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}}, | 234 | {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}}, |
| 228 | {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}}, | 235 | {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{{ | |||
| 236 | {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}}, | 243 | {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}}, |
| 237 | {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}}, | 244 | {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}}, |
| 238 | {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}}, | 245 | {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}}, |
| 246 | {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}}, | ||
| 247 | {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}}, | ||
| 248 | {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}}, | ||
| 249 | {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}}, | ||
| 250 | {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}}, | ||
| 239 | {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}}, | 251 | {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}}, |
| 240 | {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}}, | 252 | {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}}, |
| 241 | {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}}, | 253 | {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}}, |