diff options
23 files changed, 308 insertions, 86 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6068c7a1f..a9f68a8f2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt | |||
| @@ -105,6 +105,8 @@ if (MSVC) | |||
| 105 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) | 105 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/DEBUG /MANIFEST:NO /INCREMENTAL:NO /OPT:REF,ICF" CACHE STRING "" FORCE) |
| 106 | else() | 106 | else() |
| 107 | add_compile_options( | 107 | add_compile_options( |
| 108 | -fwrapv | ||
| 109 | |||
| 108 | -Werror=all | 110 | -Werror=all |
| 109 | -Werror=extra | 111 | -Werror=extra |
| 110 | -Werror=missing-declarations | 112 | -Werror=missing-declarations |
| @@ -129,7 +131,6 @@ else() | |||
| 129 | 131 | ||
| 130 | if (ARCHITECTURE_x86_64) | 132 | if (ARCHITECTURE_x86_64) |
| 131 | add_compile_options("-mcx16") | 133 | add_compile_options("-mcx16") |
| 132 | add_compile_options("-fwrapv") | ||
| 133 | endif() | 134 | endif() |
| 134 | 135 | ||
| 135 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) | 136 | if (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang) |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 2f67e60a9..e95ae80da 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -273,7 +273,8 @@ struct System::Impl { | |||
| 273 | time_manager.Initialize(); | 273 | time_manager.Initialize(); |
| 274 | 274 | ||
| 275 | is_powered_on = true; | 275 | is_powered_on = true; |
| 276 | exit_lock = false; | 276 | exit_locked = false; |
| 277 | exit_requested = false; | ||
| 277 | 278 | ||
| 278 | microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); | 279 | microprofile_cpu[0] = MICROPROFILE_TOKEN(ARM_CPU0); |
| 279 | microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); | 280 | microprofile_cpu[1] = MICROPROFILE_TOKEN(ARM_CPU1); |
| @@ -398,7 +399,8 @@ struct System::Impl { | |||
| 398 | } | 399 | } |
| 399 | 400 | ||
| 400 | is_powered_on = false; | 401 | is_powered_on = false; |
| 401 | exit_lock = false; | 402 | exit_locked = false; |
| 403 | exit_requested = false; | ||
| 402 | 404 | ||
| 403 | if (gpu_core != nullptr) { | 405 | if (gpu_core != nullptr) { |
| 404 | gpu_core->NotifyShutdown(); | 406 | gpu_core->NotifyShutdown(); |
| @@ -507,7 +509,8 @@ struct System::Impl { | |||
| 507 | 509 | ||
| 508 | CpuManager cpu_manager; | 510 | CpuManager cpu_manager; |
| 509 | std::atomic_bool is_powered_on{}; | 511 | std::atomic_bool is_powered_on{}; |
| 510 | bool exit_lock = false; | 512 | bool exit_locked = false; |
| 513 | bool exit_requested = false; | ||
| 511 | 514 | ||
| 512 | bool nvdec_active{}; | 515 | bool nvdec_active{}; |
| 513 | 516 | ||
| @@ -943,12 +946,20 @@ const Service::Time::TimeManager& System::GetTimeManager() const { | |||
| 943 | return impl->time_manager; | 946 | return impl->time_manager; |
| 944 | } | 947 | } |
| 945 | 948 | ||
| 946 | void System::SetExitLock(bool locked) { | 949 | void System::SetExitLocked(bool locked) { |
| 947 | impl->exit_lock = locked; | 950 | impl->exit_locked = locked; |
| 948 | } | 951 | } |
| 949 | 952 | ||
| 950 | bool System::GetExitLock() const { | 953 | bool System::GetExitLocked() const { |
| 951 | return impl->exit_lock; | 954 | return impl->exit_locked; |
| 955 | } | ||
| 956 | |||
| 957 | void System::SetExitRequested(bool requested) { | ||
| 958 | impl->exit_requested = requested; | ||
| 959 | } | ||
| 960 | |||
| 961 | bool System::GetExitRequested() const { | ||
| 962 | return impl->exit_requested; | ||
| 952 | } | 963 | } |
| 953 | 964 | ||
| 954 | void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { | 965 | void System::SetApplicationProcessBuildID(const CurrentBuildProcessID& id) { |
diff --git a/src/core/core.h b/src/core/core.h index c70ea1965..a9ff9315e 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -412,8 +412,11 @@ public: | |||
| 412 | /// Gets an immutable reference to the Room Network. | 412 | /// Gets an immutable reference to the Room Network. |
| 413 | [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; | 413 | [[nodiscard]] const Network::RoomNetwork& GetRoomNetwork() const; |
| 414 | 414 | ||
| 415 | void SetExitLock(bool locked); | 415 | void SetExitLocked(bool locked); |
| 416 | [[nodiscard]] bool GetExitLock() const; | 416 | bool GetExitLocked() const; |
| 417 | |||
| 418 | void SetExitRequested(bool requested); | ||
| 419 | bool GetExitRequested() const; | ||
| 417 | 420 | ||
| 418 | void SetApplicationProcessBuildID(const CurrentBuildProcessID& id); | 421 | void SetApplicationProcessBuildID(const CurrentBuildProcessID& id); |
| 419 | [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const; | 422 | [[nodiscard]] const CurrentBuildProcessID& GetApplicationProcessBuildID() const; |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 44e6852fe..7d2f0abb8 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -22,6 +22,10 @@ | |||
| 22 | 22 | ||
| 23 | namespace FileSys { | 23 | namespace FileSys { |
| 24 | 24 | ||
| 25 | static u8 MasterKeyIdForKeyGeneration(u8 key_generation) { | ||
| 26 | return std::max<u8>(key_generation, 1) - 1; | ||
| 27 | } | ||
| 28 | |||
| 25 | NCA::NCA(VirtualFile file_, const NCA* base_nca) | 29 | NCA::NCA(VirtualFile file_, const NCA* base_nca) |
| 26 | : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { | 30 | : file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} { |
| 27 | if (file == nullptr) { | 31 | if (file == nullptr) { |
| @@ -41,12 +45,17 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca) | |||
| 41 | return; | 45 | return; |
| 42 | } | 46 | } |
| 43 | 47 | ||
| 48 | // Ensure we have the proper key area keys to continue. | ||
| 49 | const u8 master_key_id = MasterKeyIdForKeyGeneration(reader->GetKeyGeneration()); | ||
| 50 | if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, reader->GetKeyIndex())) { | ||
| 51 | status = Loader::ResultStatus::ErrorMissingKeyAreaKey; | ||
| 52 | return; | ||
| 53 | } | ||
| 54 | |||
| 44 | RightsId rights_id{}; | 55 | RightsId rights_id{}; |
| 45 | reader->GetRightsId(rights_id.data(), rights_id.size()); | 56 | reader->GetRightsId(rights_id.data(), rights_id.size()); |
| 46 | if (rights_id != RightsId{}) { | 57 | if (rights_id != RightsId{}) { |
| 47 | // External decryption key required; provide it here. | 58 | // External decryption key required; provide it here. |
| 48 | const auto key_generation = std::max<s32>(reader->GetKeyGeneration(), 1) - 1; | ||
| 49 | |||
| 50 | u128 rights_id_u128; | 59 | u128 rights_id_u128; |
| 51 | std::memcpy(rights_id_u128.data(), rights_id.data(), sizeof(rights_id)); | 60 | std::memcpy(rights_id_u128.data(), rights_id.data(), sizeof(rights_id)); |
| 52 | 61 | ||
| @@ -57,12 +66,12 @@ NCA::NCA(VirtualFile file_, const NCA* base_nca) | |||
| 57 | return; | 66 | return; |
| 58 | } | 67 | } |
| 59 | 68 | ||
| 60 | if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, key_generation)) { | 69 | if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) { |
| 61 | status = Loader::ResultStatus::ErrorMissingTitlekek; | 70 | status = Loader::ResultStatus::ErrorMissingTitlekek; |
| 62 | return; | 71 | return; |
| 63 | } | 72 | } |
| 64 | 73 | ||
| 65 | auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, key_generation); | 74 | auto titlekek = keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id); |
| 66 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(titlekek, Core::Crypto::Mode::ECB); | 75 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(titlekek, Core::Crypto::Mode::ECB); |
| 67 | cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), | 76 | cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), |
| 68 | Core::Crypto::Op::Decrypt); | 77 | Core::Crypto::Op::Decrypt); |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index da33f0e44..e92f400de 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -341,7 +341,7 @@ void ISelfController::Exit(HLERequestContext& ctx) { | |||
| 341 | void ISelfController::LockExit(HLERequestContext& ctx) { | 341 | void ISelfController::LockExit(HLERequestContext& ctx) { |
| 342 | LOG_DEBUG(Service_AM, "called"); | 342 | LOG_DEBUG(Service_AM, "called"); |
| 343 | 343 | ||
| 344 | system.SetExitLock(true); | 344 | system.SetExitLocked(true); |
| 345 | 345 | ||
| 346 | IPC::ResponseBuilder rb{ctx, 2}; | 346 | IPC::ResponseBuilder rb{ctx, 2}; |
| 347 | rb.Push(ResultSuccess); | 347 | rb.Push(ResultSuccess); |
| @@ -350,10 +350,14 @@ void ISelfController::LockExit(HLERequestContext& ctx) { | |||
| 350 | void ISelfController::UnlockExit(HLERequestContext& ctx) { | 350 | void ISelfController::UnlockExit(HLERequestContext& ctx) { |
| 351 | LOG_DEBUG(Service_AM, "called"); | 351 | LOG_DEBUG(Service_AM, "called"); |
| 352 | 352 | ||
| 353 | system.SetExitLock(false); | 353 | system.SetExitLocked(false); |
| 354 | 354 | ||
| 355 | IPC::ResponseBuilder rb{ctx, 2}; | 355 | IPC::ResponseBuilder rb{ctx, 2}; |
| 356 | rb.Push(ResultSuccess); | 356 | rb.Push(ResultSuccess); |
| 357 | |||
| 358 | if (system.GetExitRequested()) { | ||
| 359 | system.Exit(); | ||
| 360 | } | ||
| 357 | } | 361 | } |
| 358 | 362 | ||
| 359 | void ISelfController::EnterFatalSection(HLERequestContext& ctx) { | 363 | void ISelfController::EnterFatalSection(HLERequestContext& ctx) { |
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp index 3ad668a47..d9872ecc2 100644 --- a/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp +++ b/src/shader_recompiler/backend/glsl/emit_glsl_image.cpp | |||
| @@ -558,7 +558,7 @@ void EmitImageGradient(EmitContext& ctx, IR::Inst& inst, const IR::Value& index, | |||
| 558 | if (multi_component) { | 558 | if (multi_component) { |
| 559 | if (info.num_derivates >= 3) { | 559 | if (info.num_derivates >= 3) { |
| 560 | const auto offset_vec{ctx.var_alloc.Consume(offset)}; | 560 | const auto offset_vec{ctx.var_alloc.Consume(offset)}; |
| 561 | ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yz, {}.y));", texel, texture, | 561 | ctx.Add("{}=textureGrad({},{},vec3({}.xz, {}.x),vec3({}.yw, {}.y));", texel, texture, |
| 562 | coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); | 562 | coords, derivatives_vec, offset_vec, derivatives_vec, offset_vec); |
| 563 | return; | 563 | return; |
| 564 | } | 564 | } |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index 7d901c04b..34240b36f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp | |||
| @@ -91,6 +91,34 @@ public: | |||
| 91 | } | 91 | } |
| 92 | } | 92 | } |
| 93 | 93 | ||
| 94 | explicit ImageOperands(EmitContext& ctx, bool has_lod_clamp, Id derivates_1, Id derivates_2, | ||
| 95 | Id offset, Id lod_clamp) { | ||
| 96 | if (!Sirit::ValidId(derivates_1) || !Sirit::ValidId(derivates_2)) { | ||
| 97 | throw LogicError("Derivates must be present"); | ||
| 98 | } | ||
| 99 | boost::container::static_vector<Id, 3> deriv_1_accum{ | ||
| 100 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 0), | ||
| 101 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 2), | ||
| 102 | ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 0), | ||
| 103 | }; | ||
| 104 | boost::container::static_vector<Id, 3> deriv_2_accum{ | ||
| 105 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 1), | ||
| 106 | ctx.OpCompositeExtract(ctx.F32[1], derivates_1, 3), | ||
| 107 | ctx.OpCompositeExtract(ctx.F32[1], derivates_2, 1), | ||
| 108 | }; | ||
| 109 | const Id derivates_id1{ctx.OpCompositeConstruct( | ||
| 110 | ctx.F32[3], std::span{deriv_1_accum.data(), deriv_1_accum.size()})}; | ||
| 111 | const Id derivates_id2{ctx.OpCompositeConstruct( | ||
| 112 | ctx.F32[3], std::span{deriv_2_accum.data(), deriv_2_accum.size()})}; | ||
| 113 | Add(spv::ImageOperandsMask::Grad, derivates_id1, derivates_id2); | ||
| 114 | if (Sirit::ValidId(offset)) { | ||
| 115 | Add(spv::ImageOperandsMask::Offset, offset); | ||
| 116 | } | ||
| 117 | if (has_lod_clamp) { | ||
| 118 | Add(spv::ImageOperandsMask::MinLod, lod_clamp); | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 94 | std::span<const Id> Span() const noexcept { | 122 | std::span<const Id> Span() const noexcept { |
| 95 | return std::span{operands.data(), operands.size()}; | 123 | return std::span{operands.data(), operands.size()}; |
| 96 | } | 124 | } |
| @@ -524,8 +552,11 @@ Id EmitImageQueryLod(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, I | |||
| 524 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, | 552 | Id EmitImageGradient(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id coords, |
| 525 | Id derivates, Id offset, Id lod_clamp) { | 553 | Id derivates, Id offset, Id lod_clamp) { |
| 526 | const auto info{inst->Flags<IR::TextureInstInfo>()}; | 554 | const auto info{inst->Flags<IR::TextureInstInfo>()}; |
| 527 | const ImageOperands operands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, | 555 | const auto operands = |
| 528 | offset, lod_clamp); | 556 | info.num_derivates == 3 |
| 557 | ? ImageOperands(ctx, info.has_lod_clamp != 0, derivates, offset, {}, lod_clamp) | ||
| 558 | : ImageOperands(ctx, info.has_lod_clamp != 0, derivates, info.num_derivates, offset, | ||
| 559 | lod_clamp); | ||
| 529 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, | 560 | return Emit(&EmitContext::OpImageSparseSampleExplicitLod, |
| 530 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], | 561 | &EmitContext::OpImageSampleExplicitLod, ctx, inst, ctx.F32[4], |
| 531 | Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); | 562 | Texture(ctx, info, index), coords, operands.Mask(), operands.Span()); |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp index 753c62098..e593132e6 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/move_special_register.cpp | |||
| @@ -161,7 +161,8 @@ enum class SpecialRegister : u64 { | |||
| 161 | LOG_WARNING(Shader, "(STUBBED) SR_AFFINITY"); | 161 | LOG_WARNING(Shader, "(STUBBED) SR_AFFINITY"); |
| 162 | return ir.Imm32(0); // This is the default value hardware returns. | 162 | return ir.Imm32(0); // This is the default value hardware returns. |
| 163 | default: | 163 | default: |
| 164 | throw NotImplementedException("S2R special register {}", special_register); | 164 | LOG_CRITICAL(Shader, "(STUBBED) Special register {}", special_register); |
| 165 | return ir.Imm32(0); // This is the default value hardware returns. | ||
| 165 | } | 166 | } |
| 166 | } | 167 | } |
| 167 | } // Anonymous namespace | 168 | } // Anonymous namespace |
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp index 9f1b340a9..58ce0d8c2 100644 --- a/src/video_core/dma_pusher.cpp +++ b/src/video_core/dma_pusher.cpp | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | namespace Tegra { | 14 | namespace Tegra { |
| 15 | 15 | ||
| 16 | constexpr u32 MacroRegistersStart = 0xE00; | 16 | constexpr u32 MacroRegistersStart = 0xE00; |
| 17 | constexpr u32 ComputeInline = 0x6D; | ||
| 17 | 18 | ||
| 18 | DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, | 19 | DmaPusher::DmaPusher(Core::System& system_, GPU& gpu_, MemoryManager& memory_manager_, |
| 19 | Control::ChannelState& channel_state_) | 20 | Control::ChannelState& channel_state_) |
| @@ -83,12 +84,35 @@ bool DmaPusher::Step() { | |||
| 83 | dma_state.dma_get, command_list_header.size * sizeof(u32)); | 84 | dma_state.dma_get, command_list_header.size * sizeof(u32)); |
| 84 | } | 85 | } |
| 85 | } | 86 | } |
| 86 | Core::Memory::GpuGuestMemory<Tegra::CommandHeader, | 87 | const auto safe_process = [&] { |
| 87 | Core::Memory::GuestMemoryFlags::UnsafeRead> | 88 | Core::Memory::GpuGuestMemory<Tegra::CommandHeader, |
| 88 | headers(memory_manager, dma_state.dma_get, command_list_header.size, &command_headers); | 89 | Core::Memory::GuestMemoryFlags::SafeRead> |
| 89 | ProcessCommands(headers); | 90 | headers(memory_manager, dma_state.dma_get, command_list_header.size, |
| 91 | &command_headers); | ||
| 92 | ProcessCommands(headers); | ||
| 93 | }; | ||
| 94 | const auto unsafe_process = [&] { | ||
| 95 | Core::Memory::GpuGuestMemory<Tegra::CommandHeader, | ||
| 96 | Core::Memory::GuestMemoryFlags::UnsafeRead> | ||
| 97 | headers(memory_manager, dma_state.dma_get, command_list_header.size, | ||
| 98 | &command_headers); | ||
| 99 | ProcessCommands(headers); | ||
| 100 | }; | ||
| 101 | if (Settings::IsGPULevelHigh()) { | ||
| 102 | if (dma_state.method >= MacroRegistersStart) { | ||
| 103 | unsafe_process(); | ||
| 104 | return true; | ||
| 105 | } | ||
| 106 | if (subchannel_type[dma_state.subchannel] == Engines::EngineTypes::KeplerCompute && | ||
| 107 | dma_state.method == ComputeInline) { | ||
| 108 | unsafe_process(); | ||
| 109 | return true; | ||
| 110 | } | ||
| 111 | safe_process(); | ||
| 112 | return true; | ||
| 113 | } | ||
| 114 | unsafe_process(); | ||
| 90 | } | 115 | } |
| 91 | |||
| 92 | return true; | 116 | return true; |
| 93 | } | 117 | } |
| 94 | 118 | ||
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h index 8a2784cdc..c9fab2d90 100644 --- a/src/video_core/dma_pusher.h +++ b/src/video_core/dma_pusher.h | |||
| @@ -130,8 +130,10 @@ public: | |||
| 130 | 130 | ||
| 131 | void DispatchCalls(); | 131 | void DispatchCalls(); |
| 132 | 132 | ||
| 133 | void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id) { | 133 | void BindSubchannel(Engines::EngineInterface* engine, u32 subchannel_id, |
| 134 | Engines::EngineTypes engine_type) { | ||
| 134 | subchannels[subchannel_id] = engine; | 135 | subchannels[subchannel_id] = engine; |
| 136 | subchannel_type[subchannel_id] = engine_type; | ||
| 135 | } | 137 | } |
| 136 | 138 | ||
| 137 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); | 139 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); |
| @@ -170,6 +172,7 @@ private: | |||
| 170 | const bool ib_enable{true}; ///< IB mode enabled | 172 | const bool ib_enable{true}; ///< IB mode enabled |
| 171 | 173 | ||
| 172 | std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; | 174 | std::array<Engines::EngineInterface*, max_subchannels> subchannels{}; |
| 175 | std::array<Engines::EngineTypes, max_subchannels> subchannel_type; | ||
| 173 | 176 | ||
| 174 | GPU& gpu; | 177 | GPU& gpu; |
| 175 | Core::System& system; | 178 | Core::System& system; |
diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h index 392322358..54631ee6c 100644 --- a/src/video_core/engines/engine_interface.h +++ b/src/video_core/engines/engine_interface.h | |||
| @@ -11,6 +11,14 @@ | |||
| 11 | 11 | ||
| 12 | namespace Tegra::Engines { | 12 | namespace Tegra::Engines { |
| 13 | 13 | ||
| 14 | enum class EngineTypes : u32 { | ||
| 15 | KeplerCompute, | ||
| 16 | Maxwell3D, | ||
| 17 | Fermi2D, | ||
| 18 | MaxwellDMA, | ||
| 19 | KeplerMemory, | ||
| 20 | }; | ||
| 21 | |||
| 14 | class EngineInterface { | 22 | class EngineInterface { |
| 15 | public: | 23 | public: |
| 16 | virtual ~EngineInterface() = default; | 24 | virtual ~EngineInterface() = default; |
diff --git a/src/video_core/engines/engine_upload.h b/src/video_core/engines/engine_upload.h index 7242d2529..21bf8aeb4 100644 --- a/src/video_core/engines/engine_upload.h +++ b/src/video_core/engines/engine_upload.h | |||
| @@ -69,6 +69,14 @@ public: | |||
| 69 | /// Binds a rasterizer to this engine. | 69 | /// Binds a rasterizer to this engine. |
| 70 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); | 70 | void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); |
| 71 | 71 | ||
| 72 | GPUVAddr ExecTargetAddress() const { | ||
| 73 | return regs.dest.Address(); | ||
| 74 | } | ||
| 75 | |||
| 76 | u32 GetUploadSize() const { | ||
| 77 | return copy_size; | ||
| 78 | } | ||
| 79 | |||
| 72 | private: | 80 | private: |
| 73 | void ProcessData(std::span<const u8> read_buffer); | 81 | void ProcessData(std::span<const u8> read_buffer); |
| 74 | 82 | ||
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp index a38d9528a..cd61ab222 100644 --- a/src/video_core/engines/kepler_compute.cpp +++ b/src/video_core/engines/kepler_compute.cpp | |||
| @@ -43,16 +43,33 @@ void KeplerCompute::CallMethod(u32 method, u32 method_argument, bool is_last_cal | |||
| 43 | 43 | ||
| 44 | switch (method) { | 44 | switch (method) { |
| 45 | case KEPLER_COMPUTE_REG_INDEX(exec_upload): { | 45 | case KEPLER_COMPUTE_REG_INDEX(exec_upload): { |
| 46 | UploadInfo info{.upload_address = upload_address, | ||
| 47 | .exec_address = upload_state.ExecTargetAddress(), | ||
| 48 | .copy_size = upload_state.GetUploadSize()}; | ||
| 49 | uploads.push_back(info); | ||
| 46 | upload_state.ProcessExec(regs.exec_upload.linear != 0); | 50 | upload_state.ProcessExec(regs.exec_upload.linear != 0); |
| 47 | break; | 51 | break; |
| 48 | } | 52 | } |
| 49 | case KEPLER_COMPUTE_REG_INDEX(data_upload): { | 53 | case KEPLER_COMPUTE_REG_INDEX(data_upload): { |
| 54 | upload_address = current_dma_segment; | ||
| 50 | upload_state.ProcessData(method_argument, is_last_call); | 55 | upload_state.ProcessData(method_argument, is_last_call); |
| 51 | break; | 56 | break; |
| 52 | } | 57 | } |
| 53 | case KEPLER_COMPUTE_REG_INDEX(launch): | 58 | case KEPLER_COMPUTE_REG_INDEX(launch): { |
| 59 | const GPUVAddr launch_desc_loc = regs.launch_desc_loc.Address(); | ||
| 60 | |||
| 61 | for (auto& data : uploads) { | ||
| 62 | const GPUVAddr offset = data.exec_address - launch_desc_loc; | ||
| 63 | if (offset / sizeof(u32) == LAUNCH_REG_INDEX(grid_dim_x) && | ||
| 64 | memory_manager.IsMemoryDirty(data.upload_address, data.copy_size)) { | ||
| 65 | indirect_compute = {data.upload_address}; | ||
| 66 | } | ||
| 67 | } | ||
| 68 | uploads.clear(); | ||
| 54 | ProcessLaunch(); | 69 | ProcessLaunch(); |
| 70 | indirect_compute = std::nullopt; | ||
| 55 | break; | 71 | break; |
| 72 | } | ||
| 56 | default: | 73 | default: |
| 57 | break; | 74 | break; |
| 58 | } | 75 | } |
| @@ -62,6 +79,7 @@ void KeplerCompute::CallMultiMethod(u32 method, const u32* base_start, u32 amoun | |||
| 62 | u32 methods_pending) { | 79 | u32 methods_pending) { |
| 63 | switch (method) { | 80 | switch (method) { |
| 64 | case KEPLER_COMPUTE_REG_INDEX(data_upload): | 81 | case KEPLER_COMPUTE_REG_INDEX(data_upload): |
| 82 | upload_address = current_dma_segment; | ||
| 65 | upload_state.ProcessData(base_start, amount); | 83 | upload_state.ProcessData(base_start, amount); |
| 66 | return; | 84 | return; |
| 67 | default: | 85 | default: |
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h index 2092e685f..735e05fb4 100644 --- a/src/video_core/engines/kepler_compute.h +++ b/src/video_core/engines/kepler_compute.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <optional> | ||
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 10 | #include "common/common_funcs.h" | 11 | #include "common/common_funcs.h" |
| @@ -36,6 +37,9 @@ namespace Tegra::Engines { | |||
| 36 | #define KEPLER_COMPUTE_REG_INDEX(field_name) \ | 37 | #define KEPLER_COMPUTE_REG_INDEX(field_name) \ |
| 37 | (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) | 38 | (offsetof(Tegra::Engines::KeplerCompute::Regs, field_name) / sizeof(u32)) |
| 38 | 39 | ||
| 40 | #define LAUNCH_REG_INDEX(field_name) \ | ||
| 41 | (offsetof(Tegra::Engines::KeplerCompute::LaunchParams, field_name) / sizeof(u32)) | ||
| 42 | |||
| 39 | class KeplerCompute final : public EngineInterface { | 43 | class KeplerCompute final : public EngineInterface { |
| 40 | public: | 44 | public: |
| 41 | explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager); | 45 | explicit KeplerCompute(Core::System& system, MemoryManager& memory_manager); |
| @@ -201,6 +205,10 @@ public: | |||
| 201 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, | 205 | void CallMultiMethod(u32 method, const u32* base_start, u32 amount, |
| 202 | u32 methods_pending) override; | 206 | u32 methods_pending) override; |
| 203 | 207 | ||
| 208 | std::optional<GPUVAddr> GetIndirectComputeAddress() const { | ||
| 209 | return indirect_compute; | ||
| 210 | } | ||
| 211 | |||
| 204 | private: | 212 | private: |
| 205 | void ProcessLaunch(); | 213 | void ProcessLaunch(); |
| 206 | 214 | ||
| @@ -216,6 +224,15 @@ private: | |||
| 216 | MemoryManager& memory_manager; | 224 | MemoryManager& memory_manager; |
| 217 | VideoCore::RasterizerInterface* rasterizer = nullptr; | 225 | VideoCore::RasterizerInterface* rasterizer = nullptr; |
| 218 | Upload::State upload_state; | 226 | Upload::State upload_state; |
| 227 | GPUVAddr upload_address; | ||
| 228 | |||
| 229 | struct UploadInfo { | ||
| 230 | GPUVAddr upload_address; | ||
| 231 | GPUVAddr exec_address; | ||
| 232 | u32 copy_size; | ||
| 233 | }; | ||
| 234 | std::vector<UploadInfo> uploads; | ||
| 235 | std::optional<GPUVAddr> indirect_compute{}; | ||
| 219 | }; | 236 | }; |
| 220 | 237 | ||
| 221 | #define ASSERT_REG_POSITION(field_name, position) \ | 238 | #define ASSERT_REG_POSITION(field_name, position) \ |
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp index c3696096d..06e349e43 100644 --- a/src/video_core/engines/maxwell_3d.cpp +++ b/src/video_core/engines/maxwell_3d.cpp | |||
| @@ -257,6 +257,7 @@ u32 Maxwell3D::GetMaxCurrentVertices() { | |||
| 257 | const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); | 257 | const u32 address_size = static_cast<u32>(gpu_addr_end - gpu_addr_begin); |
| 258 | num_vertices = std::max( | 258 | num_vertices = std::max( |
| 259 | num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value())); | 259 | num_vertices, address_size / std::max(attribute.SizeInBytes(), array.stride.Value())); |
| 260 | break; | ||
| 260 | } | 261 | } |
| 261 | return num_vertices; | 262 | return num_vertices; |
| 262 | } | 263 | } |
| @@ -269,10 +270,13 @@ size_t Maxwell3D::EstimateIndexBufferSize() { | |||
| 269 | std::numeric_limits<u32>::max()}; | 270 | std::numeric_limits<u32>::max()}; |
| 270 | const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); | 271 | const size_t byte_size = regs.index_buffer.FormatSizeInBytes(); |
| 271 | const size_t log2_byte_size = Common::Log2Ceil64(byte_size); | 272 | const size_t log2_byte_size = Common::Log2Ceil64(byte_size); |
| 273 | const size_t cap{GetMaxCurrentVertices() * 3 * byte_size}; | ||
| 274 | const size_t lower_cap = | ||
| 275 | std::min<size_t>(static_cast<size_t>(end_address - start_address), cap); | ||
| 272 | return std::min<size_t>( | 276 | return std::min<size_t>( |
| 273 | memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) / | 277 | memory_manager.GetMemoryLayoutSize(start_address, byte_size * max_sizes[log2_byte_size]) / |
| 274 | byte_size, | 278 | byte_size, |
| 275 | static_cast<size_t>(end_address - start_address)); | 279 | lower_cap); |
| 276 | } | 280 | } |
| 277 | 281 | ||
| 278 | u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { | 282 | u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) { |
diff --git a/src/video_core/engines/puller.cpp b/src/video_core/engines/puller.cpp index 7718a09b3..6de2543b7 100644 --- a/src/video_core/engines/puller.cpp +++ b/src/video_core/engines/puller.cpp | |||
| @@ -34,19 +34,24 @@ void Puller::ProcessBindMethod(const MethodCall& method_call) { | |||
| 34 | bound_engines[method_call.subchannel] = engine_id; | 34 | bound_engines[method_call.subchannel] = engine_id; |
| 35 | switch (engine_id) { | 35 | switch (engine_id) { |
| 36 | case EngineID::FERMI_TWOD_A: | 36 | case EngineID::FERMI_TWOD_A: |
| 37 | dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel); | 37 | dma_pusher.BindSubchannel(channel_state.fermi_2d.get(), method_call.subchannel, |
| 38 | EngineTypes::Fermi2D); | ||
| 38 | break; | 39 | break; |
| 39 | case EngineID::MAXWELL_B: | 40 | case EngineID::MAXWELL_B: |
| 40 | dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel); | 41 | dma_pusher.BindSubchannel(channel_state.maxwell_3d.get(), method_call.subchannel, |
| 42 | EngineTypes::Maxwell3D); | ||
| 41 | break; | 43 | break; |
| 42 | case EngineID::KEPLER_COMPUTE_B: | 44 | case EngineID::KEPLER_COMPUTE_B: |
| 43 | dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel); | 45 | dma_pusher.BindSubchannel(channel_state.kepler_compute.get(), method_call.subchannel, |
| 46 | EngineTypes::KeplerCompute); | ||
| 44 | break; | 47 | break; |
| 45 | case EngineID::MAXWELL_DMA_COPY_A: | 48 | case EngineID::MAXWELL_DMA_COPY_A: |
| 46 | dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel); | 49 | dma_pusher.BindSubchannel(channel_state.maxwell_dma.get(), method_call.subchannel, |
| 50 | EngineTypes::MaxwellDMA); | ||
| 47 | break; | 51 | break; |
| 48 | case EngineID::KEPLER_INLINE_TO_MEMORY_B: | 52 | case EngineID::KEPLER_INLINE_TO_MEMORY_B: |
| 49 | dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel); | 53 | dma_pusher.BindSubchannel(channel_state.kepler_memory.get(), method_call.subchannel, |
| 54 | EngineTypes::KeplerMemory); | ||
| 50 | break; | 55 | break; |
| 51 | default: | 56 | default: |
| 52 | UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); | 57 | UNIMPLEMENTED_MSG("Unimplemented engine {:04X}", engine_id); |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 1ba31be88..dd03efecd 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -380,6 +380,17 @@ void RasterizerOpenGL::DispatchCompute() { | |||
| 380 | pipeline->SetEngine(kepler_compute, gpu_memory); | 380 | pipeline->SetEngine(kepler_compute, gpu_memory); |
| 381 | pipeline->Configure(); | 381 | pipeline->Configure(); |
| 382 | const auto& qmd{kepler_compute->launch_description}; | 382 | const auto& qmd{kepler_compute->launch_description}; |
| 383 | auto indirect_address = kepler_compute->GetIndirectComputeAddress(); | ||
| 384 | if (indirect_address) { | ||
| 385 | // DispatchIndirect | ||
| 386 | static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; | ||
| 387 | const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite; | ||
| 388 | const auto [buffer, offset] = | ||
| 389 | buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op); | ||
| 390 | glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, buffer->Handle()); | ||
| 391 | glDispatchComputeIndirect(static_cast<GLintptr>(offset)); | ||
| 392 | return; | ||
| 393 | } | ||
| 383 | glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); | 394 | glDispatchCompute(qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z); |
| 384 | ++num_queued_commands; | 395 | ++num_queued_commands; |
| 385 | has_written_global_memory |= pipeline->WritesGlobalMemory(); | 396 | has_written_global_memory |= pipeline->WritesGlobalMemory(); |
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp index fe432dfe1..4f83a88e1 100644 --- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp +++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp | |||
| @@ -665,6 +665,19 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline( | |||
| 665 | std::move(modules), infos); | 665 | std::move(modules), infos); |
| 666 | 666 | ||
| 667 | } catch (const Shader::Exception& exception) { | 667 | } catch (const Shader::Exception& exception) { |
| 668 | auto hash = key.Hash(); | ||
| 669 | size_t env_index{0}; | ||
| 670 | for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { | ||
| 671 | if (key.unique_hashes[index] == 0) { | ||
| 672 | continue; | ||
| 673 | } | ||
| 674 | Shader::Environment& env{*envs[env_index]}; | ||
| 675 | ++env_index; | ||
| 676 | |||
| 677 | const u32 cfg_offset{static_cast<u32>(env.StartAddress() + sizeof(Shader::ProgramHeader))}; | ||
| 678 | Shader::Maxwell::Flow::CFG cfg(env, pools.flow_block, cfg_offset, index == 0); | ||
| 679 | env.Dump(hash, key.unique_hashes[index]); | ||
| 680 | } | ||
| 668 | LOG_ERROR(Render_Vulkan, "{}", exception.what()); | 681 | LOG_ERROR(Render_Vulkan, "{}", exception.what()); |
| 669 | return nullptr; | 682 | return nullptr; |
| 670 | } | 683 | } |
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 032f694bc..01e76a82c 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp | |||
| @@ -463,6 +463,20 @@ void RasterizerVulkan::DispatchCompute() { | |||
| 463 | pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache); | 463 | pipeline->Configure(*kepler_compute, *gpu_memory, scheduler, buffer_cache, texture_cache); |
| 464 | 464 | ||
| 465 | const auto& qmd{kepler_compute->launch_description}; | 465 | const auto& qmd{kepler_compute->launch_description}; |
| 466 | auto indirect_address = kepler_compute->GetIndirectComputeAddress(); | ||
| 467 | if (indirect_address) { | ||
| 468 | // DispatchIndirect | ||
| 469 | static constexpr auto sync_info = VideoCommon::ObtainBufferSynchronize::FullSynchronize; | ||
| 470 | const auto post_op = VideoCommon::ObtainBufferOperation::DiscardWrite; | ||
| 471 | const auto [buffer, offset] = | ||
| 472 | buffer_cache.ObtainBuffer(*indirect_address, 12, sync_info, post_op); | ||
| 473 | scheduler.RequestOutsideRenderPassOperationContext(); | ||
| 474 | scheduler.Record([indirect_buffer = buffer->Handle(), | ||
| 475 | indirect_offset = offset](vk::CommandBuffer cmdbuf) { | ||
| 476 | cmdbuf.DispatchIndirect(indirect_buffer, indirect_offset); | ||
| 477 | }); | ||
| 478 | return; | ||
| 479 | } | ||
| 466 | const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; | 480 | const std::array<u32, 3> dim{qmd.grid_dim_x, qmd.grid_dim_y, qmd.grid_dim_z}; |
| 467 | scheduler.RequestOutsideRenderPassOperationContext(); | 481 | scheduler.RequestOutsideRenderPassOperationContext(); |
| 468 | scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); | 482 | scheduler.Record([dim](vk::CommandBuffer cmdbuf) { cmdbuf.Dispatch(dim[0], dim[1], dim[2]); }); |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.cpp b/src/video_core/vulkan_common/vulkan_wrapper.cpp index 78e5a248f..c3f388d89 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.cpp +++ b/src/video_core/vulkan_common/vulkan_wrapper.cpp | |||
| @@ -92,6 +92,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept { | |||
| 92 | X(vkCmdCopyImage); | 92 | X(vkCmdCopyImage); |
| 93 | X(vkCmdCopyImageToBuffer); | 93 | X(vkCmdCopyImageToBuffer); |
| 94 | X(vkCmdDispatch); | 94 | X(vkCmdDispatch); |
| 95 | X(vkCmdDispatchIndirect); | ||
| 95 | X(vkCmdDraw); | 96 | X(vkCmdDraw); |
| 96 | X(vkCmdDrawIndexed); | 97 | X(vkCmdDrawIndexed); |
| 97 | X(vkCmdDrawIndirect); | 98 | X(vkCmdDrawIndirect); |
diff --git a/src/video_core/vulkan_common/vulkan_wrapper.h b/src/video_core/vulkan_common/vulkan_wrapper.h index c226a2a29..049fa8038 100644 --- a/src/video_core/vulkan_common/vulkan_wrapper.h +++ b/src/video_core/vulkan_common/vulkan_wrapper.h | |||
| @@ -203,6 +203,7 @@ struct DeviceDispatch : InstanceDispatch { | |||
| 203 | PFN_vkCmdCopyImage vkCmdCopyImage{}; | 203 | PFN_vkCmdCopyImage vkCmdCopyImage{}; |
| 204 | PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{}; | 204 | PFN_vkCmdCopyImageToBuffer vkCmdCopyImageToBuffer{}; |
| 205 | PFN_vkCmdDispatch vkCmdDispatch{}; | 205 | PFN_vkCmdDispatch vkCmdDispatch{}; |
| 206 | PFN_vkCmdDispatchIndirect vkCmdDispatchIndirect{}; | ||
| 206 | PFN_vkCmdDraw vkCmdDraw{}; | 207 | PFN_vkCmdDraw vkCmdDraw{}; |
| 207 | PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; | 208 | PFN_vkCmdDrawIndexed vkCmdDrawIndexed{}; |
| 208 | PFN_vkCmdDrawIndirect vkCmdDrawIndirect{}; | 209 | PFN_vkCmdDrawIndirect vkCmdDrawIndirect{}; |
| @@ -1209,6 +1210,10 @@ public: | |||
| 1209 | dld->vkCmdDispatch(handle, x, y, z); | 1210 | dld->vkCmdDispatch(handle, x, y, z); |
| 1210 | } | 1211 | } |
| 1211 | 1212 | ||
| 1213 | void DispatchIndirect(VkBuffer indirect_buffer, VkDeviceSize offset) const noexcept { | ||
| 1214 | dld->vkCmdDispatchIndirect(handle, indirect_buffer, offset); | ||
| 1215 | } | ||
| 1216 | |||
| 1212 | void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, | 1217 | void PipelineBarrier(VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, |
| 1213 | VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers, | 1218 | VkDependencyFlags dependency_flags, Span<VkMemoryBarrier> memory_barriers, |
| 1214 | Span<VkBufferMemoryBarrier> buffer_barriers, | 1219 | Span<VkBufferMemoryBarrier> buffer_barriers, |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 33c9fd0af..f2e6c03f0 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -2010,8 +2010,16 @@ bool GMainWindow::OnShutdownBegin() { | |||
| 2010 | 2010 | ||
| 2011 | emit EmulationStopping(); | 2011 | emit EmulationStopping(); |
| 2012 | 2012 | ||
| 2013 | int shutdown_time = 1000; | ||
| 2014 | |||
| 2015 | if (system->DebuggerEnabled()) { | ||
| 2016 | shutdown_time = 0; | ||
| 2017 | } else if (system->GetExitLocked()) { | ||
| 2018 | shutdown_time = 5000; | ||
| 2019 | } | ||
| 2020 | |||
| 2013 | shutdown_timer.setSingleShot(true); | 2021 | shutdown_timer.setSingleShot(true); |
| 2014 | shutdown_timer.start(system->DebuggerEnabled() ? 0 : 5000); | 2022 | shutdown_timer.start(shutdown_time); |
| 2015 | connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); | 2023 | connect(&shutdown_timer, &QTimer::timeout, this, &GMainWindow::OnEmulationStopTimeExpired); |
| 2016 | connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); | 2024 | connect(emu_thread.get(), &QThread::finished, this, &GMainWindow::OnEmulationStopped); |
| 2017 | 2025 | ||
| @@ -2573,50 +2581,48 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 2573 | return; | 2581 | return; |
| 2574 | } | 2582 | } |
| 2575 | 2583 | ||
| 2576 | FileSys::VirtualFile base_romfs; | 2584 | FileSys::VirtualFile packed_update_raw{}; |
| 2577 | if (loader->ReadRomFS(base_romfs) != Loader::ResultStatus::Success) { | 2585 | loader->ReadUpdateRaw(packed_update_raw); |
| 2578 | failed(); | ||
| 2579 | return; | ||
| 2580 | } | ||
| 2581 | 2586 | ||
| 2582 | const auto& installed = system->GetContentProvider(); | 2587 | const auto& installed = system->GetContentProvider(); |
| 2583 | const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id); | ||
| 2584 | 2588 | ||
| 2585 | if (!romfs_title_id) { | 2589 | u64 title_id{}; |
| 2590 | u8 raw_type{}; | ||
| 2591 | if (!SelectRomFSDumpTarget(installed, program_id, &title_id, &raw_type)) { | ||
| 2586 | failed(); | 2592 | failed(); |
| 2587 | return; | 2593 | return; |
| 2588 | } | 2594 | } |
| 2589 | 2595 | ||
| 2590 | const auto type = *romfs_title_id == program_id ? FileSys::ContentRecordType::Program | 2596 | const auto type = static_cast<FileSys::ContentRecordType>(raw_type); |
| 2591 | : FileSys::ContentRecordType::Data; | 2597 | const auto base_nca = installed.GetEntry(title_id, type); |
| 2592 | const auto base_nca = installed.GetEntry(*romfs_title_id, type); | ||
| 2593 | if (!base_nca) { | 2598 | if (!base_nca) { |
| 2594 | failed(); | 2599 | failed(); |
| 2595 | return; | 2600 | return; |
| 2596 | } | 2601 | } |
| 2597 | 2602 | ||
| 2603 | const FileSys::NCA update_nca{packed_update_raw, nullptr}; | ||
| 2604 | if (type != FileSys::ContentRecordType::Program || | ||
| 2605 | update_nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS || | ||
| 2606 | update_nca.GetTitleId() != FileSys::GetUpdateTitleID(title_id)) { | ||
| 2607 | packed_update_raw = {}; | ||
| 2608 | } | ||
| 2609 | |||
| 2610 | const auto base_romfs = base_nca->GetRomFS(); | ||
| 2611 | if (!base_romfs) { | ||
| 2612 | failed(); | ||
| 2613 | return; | ||
| 2614 | } | ||
| 2615 | |||
| 2598 | const auto dump_dir = | 2616 | const auto dump_dir = |
| 2599 | target == DumpRomFSTarget::Normal | 2617 | target == DumpRomFSTarget::Normal |
| 2600 | ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) | 2618 | ? Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir) |
| 2601 | : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; | 2619 | : Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "atmosphere" / "contents"; |
| 2602 | const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); | 2620 | const auto romfs_dir = fmt::format("{:016X}/romfs", title_id); |
| 2603 | 2621 | ||
| 2604 | const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); | 2622 | const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); |
| 2605 | 2623 | ||
| 2606 | FileSys::VirtualFile romfs; | 2624 | const FileSys::PatchManager pm{title_id, system->GetFileSystemController(), installed}; |
| 2607 | 2625 | auto romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, packed_update_raw, false); | |
| 2608 | if (*romfs_title_id == program_id) { | ||
| 2609 | const FileSys::PatchManager pm{program_id, system->GetFileSystemController(), installed}; | ||
| 2610 | romfs = pm.PatchRomFS(base_nca.get(), base_romfs, type, nullptr, false); | ||
| 2611 | } else { | ||
| 2612 | romfs = installed.GetEntry(*romfs_title_id, type)->GetRomFS(); | ||
| 2613 | } | ||
| 2614 | |||
| 2615 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); | ||
| 2616 | if (extracted == nullptr) { | ||
| 2617 | failed(); | ||
| 2618 | return; | ||
| 2619 | } | ||
| 2620 | 2626 | ||
| 2621 | const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); | 2627 | const auto out = VfsFilesystemCreateDirectoryWrapper(vfs, path, FileSys::Mode::ReadWrite); |
| 2622 | 2628 | ||
| @@ -2640,6 +2646,12 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 2640 | return; | 2646 | return; |
| 2641 | } | 2647 | } |
| 2642 | 2648 | ||
| 2649 | const auto extracted = FileSys::ExtractRomFS(romfs, FileSys::RomFSExtractionType::Full); | ||
| 2650 | if (extracted == nullptr) { | ||
| 2651 | failed(); | ||
| 2652 | return; | ||
| 2653 | } | ||
| 2654 | |||
| 2643 | const auto full = res == selections.constFirst(); | 2655 | const auto full = res == selections.constFirst(); |
| 2644 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); | 2656 | const auto entry_size = CalculateRomFSEntrySize(extracted, full); |
| 2645 | 2657 | ||
| @@ -3261,7 +3273,7 @@ void GMainWindow::OnPauseContinueGame() { | |||
| 3261 | } | 3273 | } |
| 3262 | 3274 | ||
| 3263 | void GMainWindow::OnStopGame() { | 3275 | void GMainWindow::OnStopGame() { |
| 3264 | if (system->GetExitLock() && !ConfirmForceLockedExit()) { | 3276 | if (system->GetExitLocked() && !ConfirmForceLockedExit()) { |
| 3265 | return; | 3277 | return; |
| 3266 | } | 3278 | } |
| 3267 | 3279 | ||
| @@ -4350,28 +4362,41 @@ bool GMainWindow::CheckSystemArchiveDecryption() { | |||
| 4350 | return mii_nca->GetRomFS().get() != nullptr; | 4362 | return mii_nca->GetRomFS().get() != nullptr; |
| 4351 | } | 4363 | } |
| 4352 | 4364 | ||
| 4353 | std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, | 4365 | bool GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProvider& installed, u64 program_id, |
| 4354 | u64 program_id) { | 4366 | u64* selected_title_id, u8* selected_content_record_type) { |
| 4355 | const auto dlc_entries = | 4367 | using ContentInfo = std::pair<FileSys::TitleType, FileSys::ContentRecordType>; |
| 4356 | installed.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | 4368 | boost::container::flat_map<u64, ContentInfo> available_title_ids; |
| 4357 | std::vector<FileSys::ContentProviderEntry> dlc_match; | 4369 | |
| 4358 | dlc_match.reserve(dlc_entries.size()); | 4370 | const auto RetrieveEntries = [&](FileSys::TitleType title_type, |
| 4359 | std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match), | 4371 | FileSys::ContentRecordType record_type) { |
| 4360 | [&program_id, &installed](const FileSys::ContentProviderEntry& entry) { | 4372 | const auto entries = installed.ListEntriesFilter(title_type, record_type); |
| 4361 | return FileSys::GetBaseTitleID(entry.title_id) == program_id && | 4373 | for (const auto& entry : entries) { |
| 4362 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success; | 4374 | if (FileSys::GetBaseTitleID(entry.title_id) == program_id && |
| 4363 | }); | 4375 | installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success) { |
| 4364 | 4376 | available_title_ids[entry.title_id] = {title_type, record_type}; | |
| 4365 | std::vector<u64> romfs_tids; | 4377 | } |
| 4366 | romfs_tids.push_back(program_id); | 4378 | } |
| 4367 | for (const auto& entry : dlc_match) { | 4379 | }; |
| 4368 | romfs_tids.push_back(entry.title_id); | 4380 | |
| 4369 | } | 4381 | RetrieveEntries(FileSys::TitleType::Application, FileSys::ContentRecordType::Program); |
| 4370 | 4382 | RetrieveEntries(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data); | |
| 4371 | if (romfs_tids.size() > 1) { | 4383 | |
| 4372 | QStringList list{QStringLiteral("Base")}; | 4384 | if (available_title_ids.empty()) { |
| 4373 | for (std::size_t i = 1; i < romfs_tids.size(); ++i) { | 4385 | return false; |
| 4374 | list.push_back(QStringLiteral("DLC %1").arg(romfs_tids[i] & 0x7FF)); | 4386 | } |
| 4387 | |||
| 4388 | size_t title_index = 0; | ||
| 4389 | |||
| 4390 | if (available_title_ids.size() > 1) { | ||
| 4391 | QStringList list; | ||
| 4392 | for (auto& [title_id, content_info] : available_title_ids) { | ||
| 4393 | const auto hex_title_id = QString::fromStdString(fmt::format("{:X}", title_id)); | ||
| 4394 | if (content_info.first == FileSys::TitleType::Application) { | ||
| 4395 | list.push_back(QStringLiteral("Application [%1]").arg(hex_title_id)); | ||
| 4396 | } else { | ||
| 4397 | list.push_back( | ||
| 4398 | QStringLiteral("DLC %1 [%2]").arg(title_id & 0x7FF).arg(hex_title_id)); | ||
| 4399 | } | ||
| 4375 | } | 4400 | } |
| 4376 | 4401 | ||
| 4377 | bool ok; | 4402 | bool ok; |
| @@ -4379,13 +4404,16 @@ std::optional<u64> GMainWindow::SelectRomFSDumpTarget(const FileSys::ContentProv | |||
| 4379 | this, tr("Select RomFS Dump Target"), | 4404 | this, tr("Select RomFS Dump Target"), |
| 4380 | tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); | 4405 | tr("Please select which RomFS you would like to dump."), list, 0, false, &ok); |
| 4381 | if (!ok) { | 4406 | if (!ok) { |
| 4382 | return {}; | 4407 | return false; |
| 4383 | } | 4408 | } |
| 4384 | 4409 | ||
| 4385 | return romfs_tids[list.indexOf(res)]; | 4410 | title_index = list.indexOf(res); |
| 4386 | } | 4411 | } |
| 4387 | 4412 | ||
| 4388 | return program_id; | 4413 | const auto selected_info = available_title_ids.nth(title_index); |
| 4414 | *selected_title_id = selected_info->first; | ||
| 4415 | *selected_content_record_type = static_cast<u8>(selected_info->second.second); | ||
| 4416 | return true; | ||
| 4389 | } | 4417 | } |
| 4390 | 4418 | ||
| 4391 | bool GMainWindow::ConfirmClose() { | 4419 | bool GMainWindow::ConfirmClose() { |
| @@ -4515,6 +4543,8 @@ void GMainWindow::RequestGameExit() { | |||
| 4515 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); | 4543 | auto applet_ae = sm.GetService<Service::AM::AppletAE>("appletAE"); |
| 4516 | bool has_signalled = false; | 4544 | bool has_signalled = false; |
| 4517 | 4545 | ||
| 4546 | system->SetExitRequested(true); | ||
| 4547 | |||
| 4518 | if (applet_oe != nullptr) { | 4548 | if (applet_oe != nullptr) { |
| 4519 | applet_oe->GetMessageQueue()->RequestExit(); | 4549 | applet_oe->GetMessageQueue()->RequestExit(); |
| 4520 | has_signalled = true; | 4550 | has_signalled = true; |
diff --git a/src/yuzu/main.h b/src/yuzu/main.h index 1b7055122..668dbc3b1 100644 --- a/src/yuzu/main.h +++ b/src/yuzu/main.h | |||
| @@ -375,7 +375,8 @@ private: | |||
| 375 | void RemoveAllTransferableShaderCaches(u64 program_id); | 375 | void RemoveAllTransferableShaderCaches(u64 program_id); |
| 376 | void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); | 376 | void RemoveCustomConfiguration(u64 program_id, const std::string& game_path); |
| 377 | void RemoveCacheStorage(u64 program_id); | 377 | void RemoveCacheStorage(u64 program_id); |
| 378 | std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id); | 378 | bool SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id, |
| 379 | u64* selected_title_id, u8* selected_content_record_type); | ||
| 379 | InstallResult InstallNSPXCI(const QString& filename); | 380 | InstallResult InstallNSPXCI(const QString& filename); |
| 380 | InstallResult InstallNCA(const QString& filename); | 381 | InstallResult InstallNCA(const QString& filename); |
| 381 | void MigrateConfigFiles(); | 382 | void MigrateConfigFiles(); |