From 5eda82bbab95a7f608c7f8e4a8615d0223bb1d53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Mon, 26 May 2025 21:43:01 +0100 Subject: [PATCH 01/31] Enable VK_EXT_conditional_rendering --- src/video_core/renderer_vulkan/vk_instance.cpp | 10 +++++++++- src/video_core/renderer_vulkan/vk_instance.h | 6 ++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index 9584329f0..f9e9606ca 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -212,7 +212,8 @@ bool Instance::CreateDevice() { vk::PhysicalDeviceExtendedDynamicState3FeaturesEXT, vk::PhysicalDevicePrimitiveTopologyListRestartFeaturesEXT, vk::PhysicalDevicePortabilitySubsetFeaturesKHR, - vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT>(); + vk::PhysicalDeviceShaderAtomicFloat2FeaturesEXT, + vk::PhysicalDeviceConditionalRenderingFeaturesEXT>(); features = feature_chain.get().features; const vk::StructureChain properties_chain = physical_device.getProperties2< @@ -283,6 +284,7 @@ bool Instance::CreateDevice() { LOG_INFO(Render_Vulkan, "- shaderImageFloat32AtomicMinMax: {}", shader_atomic_float2_features.shaderImageFloat32AtomicMinMax); } + conditional_rendering = add_extension(VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME); const bool calibrated_timestamps = TRACY_GPU_ENABLED ? add_extension(VK_EXT_CALIBRATED_TIMESTAMPS_EXTENSION_NAME) : false; @@ -420,6 +422,9 @@ bool Instance::CreateDevice() { .shaderImageFloat32AtomicMinMax = shader_atomic_float2_features.shaderImageFloat32AtomicMinMax, }, + vk::PhysicalDeviceConditionalRenderingFeaturesEXT{ + .conditionalRendering = true, + } #ifdef __APPLE__ portability_features, #endif @@ -452,6 +457,9 @@ bool Instance::CreateDevice() { if (!shader_atomic_float2) { device_chain.unlink(); } + if (!conditional_rendering) { + device_chain.unlink(); + } auto [device_result, dev] = physical_device.createDeviceUnique(device_chain.get()); if (device_result != vk::Result::eSuccess) { diff --git a/src/video_core/renderer_vulkan/vk_instance.h b/src/video_core/renderer_vulkan/vk_instance.h index 30848e8b7..6cb550496 100644 --- a/src/video_core/renderer_vulkan/vk_instance.h +++ b/src/video_core/renderer_vulkan/vk_instance.h @@ -191,6 +191,11 @@ public: return !portability_subset || portability_features.tessellationPointMode; } + /// Returns true when VK_EXT_conditional_rendering is supported by the device + bool IsConditionalRenderingSupported() const { + return conditional_rendering; + } + /// Returns the vendor ID of the physical device u32 GetVendorID() const { return properties.vendorID; @@ -374,6 +379,7 @@ private: bool amd_gcn_shader{}; bool amd_shader_trinary_minmax{}; bool shader_atomic_float2{}; + bool conditional_rendering{}; bool portability_subset{}; }; From d2ed73005ea12a7ec4ab5d729b917dae797bdbf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Mon, 26 May 2025 22:42:17 +0100 Subject: [PATCH 02/31] Define PM4 SetPredication command --- src/video_core/amdgpu/pm4_cmds.h | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index 58ecda93e..a61f46278 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -415,6 +415,13 @@ struct PM4CmdEventWrite { BitField<20, 1, u32> inv_l2; ///< Send WBINVL2 op to the TC L2 cache when EVENT_INDEX = 0111 }; u32 address[]; + + template + T Address() const { + ASSERT(event_index.Value() >= EventIndex::ZpassDone && + event_index.Value() <= EventIndex::SampleStreamoutStatSx); + return reinterpret_cast((u64(address[1]) << 32u) | u64(address[0])); + } }; struct PM4CmdEventWriteEop { @@ -1104,4 +1111,42 @@ struct PM4CmdMemSemaphore { } }; +enum class Predication : u32 { + DrawIfNotVisible = 0, + DrawIfVisible = 1, +}; + +enum class PredicationHint : u32 { + Wait = 0, + Draw = 1, +}; + +enum class PredicateOperation : u32 { + Clear = 0, + Zpass = 1, + PrimCount = 2, + // other values are reserved +}; + +struct PM4CmdSetPredication { + PM4Type3Header header; + union { + BitField<4, 28, u32> start_address_lo; + u32 raw1; + }; + union { + BitField<0, 8, u32> start_address_hi; + BitField<8, 1, Predication> action; + BitField<12, 1, PredicationHint> hint; + BitField<16, 3, PredicateOperation> pred_op; + BitField<31, 1, u32> continue_bit; + u32 raw2; + }; + + template + T Address() const { + return reinterpret_cast(u64(start_address_lo.Value()) << 4 | u64(start_address_hi.Value()) << 32); + } +}; + } // namespace AmdGpu From 0543f1fd6da53217343d09cbe765c357d960104c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Mon, 26 May 2025 22:46:35 +0100 Subject: [PATCH 03/31] Handle predication PM4 commands --- src/video_core/amdgpu/liverpool.cpp | 19 ++++++++++++++++++- .../renderer_vulkan/vk_rasterizer.cpp | 9 +++++++++ .../renderer_vulkan/vk_rasterizer.h | 3 +++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 4db7648c6..f6dd954db 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -394,7 +394,24 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::span(header); + if (predication->continue_bit.Value()) { + LOG_WARNING(Render_Vulkan, "unhandled continue bit in predication command"); + } + if (predication->pred_op.Value() == PredicateOperation::Clear) { + if (rasterizer) { + rasterizer->EndPredication(); + } + } + else if (predication->pred_op.Value() == PredicateOperation::Zpass) { + if (rasterizer) { + rasterizer->StartPredication(); + } + } + else { + LOG_WARNING(Render_Vulkan, "unhandled predicate operation {}", + magic_enum::enum_name(predication->pred_op.Value())); + } break; } case PM4ItOpcode::IndexType: { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 4bdb08bf2..cf0c354c9 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1263,4 +1263,13 @@ void Rasterizer::ScopedMarkerInsertColor(const std::string_view& str, const u32 (f32)(color & 0xff) / 255.0f, (f32)((color >> 24) & 0xff) / 255.0f})}); } +void Rasterizer::StartPredication() { + +} + +void Rasterizer::EndPredication() { + +} + + } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index fb9ca4bbe..e4b8aefb4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -55,6 +55,9 @@ public: void ScopedMarkerInsertColor(const std::string_view& str, const u32 color, bool from_guest = false); + void StartPredication(); + void EndPredication(); + void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); bool InvalidateMemory(VAddr addr, u64 size); From abdc55726210df2f45930d5298ba5161b2201018 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Mon, 26 May 2025 23:01:32 +0100 Subject: [PATCH 04/31] Handle occlusion query commands --- src/video_core/amdgpu/liverpool.cpp | 20 +++++++++++++++++++ .../renderer_vulkan/vk_rasterizer.cpp | 9 ++++++++- .../renderer_vulkan/vk_rasterizer.h | 2 ++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index f6dd954db..21a2c9288 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -612,6 +612,26 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanevent_index.Value() == EventIndex::ZpassDone) { + if (event->event_type.Value() == EventType::PixelPipeStatControl) { + + } + else if (event->event_type.Value() == EventType::PixelPipeStatDump) { + if ((event->Address() & 0x8) == 0) { + // occlusion query start + if (rasterizer) { + rasterizer->StartOcclusionQuery(); + } + } + else { + // occlusion query end + if (rasterizer) { + rasterizer->EndOcclusionQuery(); + } + } + } + } break; } case PM4ItOpcode::EventWriteEos: { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index cf0c354c9..970d68ecb 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1268,8 +1268,15 @@ void Rasterizer::StartPredication() { } void Rasterizer::EndPredication() { - + } +void Rasterizer::StartOcclusionQuery() { + +} + +void Rasterizer::EndOcclusionQuery() { + +} } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index e4b8aefb4..22b62e9d4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -57,6 +57,8 @@ public: void StartPredication(); void EndPredication(); + void StartOcclusionQuery(); + void EndOcclusionQuery(); void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); From cb69968d47e695999d1e2275385ee4e4f750ccc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Mon, 26 May 2025 23:53:51 +0100 Subject: [PATCH 05/31] Run occlusion queries when requested by a game --- src/video_core/amdgpu/liverpool.cpp | 4 +-- .../renderer_vulkan/vk_rasterizer.cpp | 32 +++++++++++++++++-- .../renderer_vulkan/vk_rasterizer.h | 7 ++-- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 21a2c9288..6e698038c 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -621,13 +621,13 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanAddress() & 0x8) == 0) { // occlusion query start if (rasterizer) { - rasterizer->StartOcclusionQuery(); + rasterizer->StartOcclusionQuery(event->Address()); } } else { // occlusion query end if (rasterizer) { - rasterizer->EndOcclusionQuery(); + rasterizer->EndOcclusionQuery(event->Address() & ~0xF); } } } diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 970d68ecb..a9920e310 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -17,6 +17,10 @@ #undef MemoryBarrier #endif +namespace { + const int OCCLUSION_QUERIES_COUNT = 16; +} + namespace Vulkan { static Shader::PushData MakeUserData(const AmdGpu::Liverpool::Regs& regs) { @@ -43,6 +47,11 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, liverpool->BindRasterizer(this); } memory->SetRasterizer(this); + occlusion_query_pool = Check<"occlusion query pool">(instance.GetDevice().createQueryPool({ + .queryType = vk::QueryType::eOcclusion, + .queryCount = OCCLUSION_QUERIES_COUNT, + })); + instance.GetDevice().resetQueryPool(occlusion_query_pool, 0, OCCLUSION_QUERIES_COUNT); } Rasterizer::~Rasterizer() = default; @@ -1271,12 +1280,31 @@ void Rasterizer::EndPredication() { } -void Rasterizer::StartOcclusionQuery() { +void Rasterizer::StartOcclusionQuery(VAddr addr) { + LOG_DEBUG(Render_Vulkan, "addr = {:#x}, index = {}", addr, occlusion_current_index); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.resetQueryPool(occlusion_query_pool, occlusion_current_index, 1); + ScopeMarkerBegin("gfx:{}:occlusionQuery", fmt::ptr(reinterpret_cast(addr))); + cmdbuf.beginQuery(occlusion_query_pool, occlusion_current_index, vk::QueryControlFlags()); + + occlusion_index_mapping.insert_or_assign(addr, occlusion_current_index); + + occlusion_current_index++; + if (occlusion_current_index > OCCLUSION_QUERIES_COUNT - 1) { + occlusion_current_index = 0; + } } -void Rasterizer::EndOcclusionQuery() { +void Rasterizer::EndOcclusionQuery(VAddr addr) { + ASSERT(occlusion_index_mapping.contains(addr)); + auto index = occlusion_index_mapping[addr]; + LOG_DEBUG(Render_Vulkan, "addr = {:#x}, index = {}", addr, index); + + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.endQuery(occlusion_query_pool, index); + ScopeMarkerEnd(); } } // namespace Vulkan diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 22b62e9d4..5be1748a8 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -57,8 +57,8 @@ public: void StartPredication(); void EndPredication(); - void StartOcclusionQuery(); - void EndOcclusionQuery(); + void StartOcclusionQuery(VAddr addr); + void EndOcclusionQuery(VAddr addr); void InlineData(VAddr address, const void* value, u32 num_bytes, bool is_gds); u32 ReadDataFromGds(u32 gsd_offset); @@ -127,6 +127,9 @@ private: boost::icl::interval_set mapped_ranges; std::shared_mutex mapped_ranges_mutex; PipelineCache pipeline_cache; + vk::QueryPool occlusion_query_pool; + u32 occlusion_current_index{}; + std::map occlusion_index_mapping; boost::container::static_vector< std::pair, 8> From ab9254f21a58e86e02b10c7f65d33f030c063828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Tue, 27 May 2025 00:06:56 +0100 Subject: [PATCH 06/31] Fix Vulkan validation errors with using queries --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index a9920e310..73022f639 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1283,6 +1283,7 @@ void Rasterizer::EndPredication() { void Rasterizer::StartOcclusionQuery(VAddr addr) { LOG_DEBUG(Render_Vulkan, "addr = {:#x}, index = {}", addr, occlusion_current_index); + scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.resetQueryPool(occlusion_query_pool, occlusion_current_index, 1); ScopeMarkerBegin("gfx:{}:occlusionQuery", fmt::ptr(reinterpret_cast(addr))); @@ -1302,6 +1303,7 @@ void Rasterizer::EndOcclusionQuery(VAddr addr) { auto index = occlusion_index_mapping[addr]; LOG_DEBUG(Render_Vulkan, "addr = {:#x}, index = {}", addr, index); + scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.endQuery(occlusion_query_pool, index); ScopeMarkerEnd(); From 34f0f03f99758f4fede636aa6e597f452926c693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Tue, 27 May 2025 01:21:54 +0100 Subject: [PATCH 07/31] Use conditional rendering based on the occlusion query results --- src/video_core/amdgpu/liverpool.cpp | 4 +- .../renderer_vulkan/vk_rasterizer.cpp | 58 ++++++++++++++++++- .../renderer_vulkan/vk_rasterizer.h | 4 +- 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 6e698038c..8c297ff83 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -405,7 +405,9 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanpred_op.Value() == PredicateOperation::Zpass) { if (rasterizer) { - rasterizer->StartPredication(); + rasterizer->StartPredication(predication->Address(), + predication->action.Value() == Predication::DrawIfVisible, + predication->hint.Value() == PredicationHint::Wait); } } else { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 73022f639..12d187403 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -42,7 +42,9 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, : instance{instance_}, scheduler{scheduler_}, page_manager{this}, buffer_cache{instance, scheduler, *this, liverpool_, texture_cache, page_manager}, texture_cache{instance, scheduler, buffer_cache, page_manager}, liverpool{liverpool_}, - memory{Core::Memory::Instance()}, pipeline_cache{instance, scheduler, liverpool} { + memory{Core::Memory::Instance()}, pipeline_cache{instance, scheduler, liverpool}, + occlusion_query_buffer{instance, scheduler, VideoCore::MemoryUsage::DeviceLocal, 0, + vk::BufferUsageFlagBits::eConditionalRenderingEXT | vk::BufferUsageFlagBits::eTransferDst, sizeof(u32)*OCCLUSION_QUERIES_COUNT} { if (!Config::nullGpu()) { liverpool->BindRasterizer(this); } @@ -52,6 +54,7 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, .queryCount = OCCLUSION_QUERIES_COUNT, })); instance.GetDevice().resetQueryPool(occlusion_query_pool, 0, OCCLUSION_QUERIES_COUNT); + Vulkan::SetObjectName(instance.GetDevice(), occlusion_query_buffer.Handle(), "OcclusionQueryBuffer:{:#x}", sizeof(u32)*OCCLUSION_QUERIES_COUNT); } Rasterizer::~Rasterizer() = default; @@ -1272,12 +1275,63 @@ void Rasterizer::ScopedMarkerInsertColor(const std::string_view& str, const u32 (f32)(color & 0xff) / 255.0f, (f32)((color >> 24) & 0xff) / 255.0f})}); } -void Rasterizer::StartPredication() { +void Rasterizer::StartPredication(VAddr addr, bool draw_if_visible, bool wait_for_result) { + if (!instance.IsConditionalRenderingSupported()) { + return; + } + ASSERT(!active_predication); + ASSERT(occlusion_index_mapping.contains(addr)); + + auto index = occlusion_index_mapping[addr]; + LOG_DEBUG(Render_Vulkan, "addr = {:#x}, index = {}", addr, index); + + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + + cmdbuf.copyQueryPoolResults(occlusion_query_pool, index, 1, occlusion_query_buffer.Handle(), + index*sizeof(u32), sizeof(u32), wait_for_result ? vk::QueryResultFlagBits::eWait + : vk::QueryResultFlagBits::ePartial); + + const auto pre_barrier = vk::BufferMemoryBarrier2{ + .srcStageMask = vk::PipelineStageFlagBits2::eCopy, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eCopy, + .dstAccessMask = vk::AccessFlagBits2::eTransferWrite, + .buffer = occlusion_query_buffer.Handle(), + .offset = index * sizeof(u32), + .size = sizeof(u32), + }; + cmdbuf.pipelineBarrier2(vk::DependencyInfo{ + .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .bufferMemoryBarrierCount = 1, + .pBufferMemoryBarriers = &pre_barrier, + }); + + ScopeMarkerBegin("gfx:{}:predication", fmt::ptr(reinterpret_cast(addr))); + vk::ConditionalRenderingBeginInfoEXT conditional_rendering_info { + .buffer = occlusion_query_buffer.Handle(), + .offset = index * sizeof(u32), + .flags = draw_if_visible ? vk::ConditionalRenderingFlagBitsEXT::eInverted + : vk::ConditionalRenderingFlagsEXT(), + }; + cmdbuf.beginConditionalRenderingEXT(&conditional_rendering_info); + + active_predication = true; } void Rasterizer::EndPredication() { + if (!active_predication) { + return; + } + LOG_DEBUG(Render_Vulkan, ""); + + scheduler.EndRendering(); + const auto cmdbuf = scheduler.CommandBuffer(); + cmdbuf.endConditionalRenderingEXT(); + ScopeMarkerEnd(); + active_predication = false; } void Rasterizer::StartOcclusionQuery(VAddr addr) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 5be1748a8..9883a606f 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -55,7 +55,7 @@ public: void ScopedMarkerInsertColor(const std::string_view& str, const u32 color, bool from_guest = false); - void StartPredication(); + void StartPredication(VAddr addr, bool discard_if_zero, bool wait_for_result); void EndPredication(); void StartOcclusionQuery(VAddr addr); void EndOcclusionQuery(VAddr addr); @@ -130,6 +130,8 @@ private: vk::QueryPool occlusion_query_pool; u32 occlusion_current_index{}; std::map occlusion_index_mapping; + VideoCore::Buffer occlusion_query_buffer; + bool active_predication; boost::container::static_vector< std::pair, 8> From 979e17459ff337fbf2ad5084e13e30ed2ac889a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Tue, 27 May 2025 01:23:34 +0100 Subject: [PATCH 08/31] clang-format --- src/video_core/amdgpu/liverpool.cpp | 17 +++++++-------- src/video_core/amdgpu/pm4_cmds.h | 5 +++-- .../renderer_vulkan/vk_rasterizer.cpp | 21 ++++++++++++------- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index 8c297ff83..c99dbb9a9 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -402,17 +402,16 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanEndPredication(); } - } - else if (predication->pred_op.Value() == PredicateOperation::Zpass) { + } else if (predication->pred_op.Value() == PredicateOperation::Zpass) { if (rasterizer) { - rasterizer->StartPredication(predication->Address(), + rasterizer->StartPredication( + predication->Address(), predication->action.Value() == Predication::DrawIfVisible, predication->hint.Value() == PredicationHint::Wait); } - } - else { + } else { LOG_WARNING(Render_Vulkan, "unhandled predicate operation {}", - magic_enum::enum_name(predication->pred_op.Value())); + magic_enum::enum_name(predication->pred_op.Value())); } break; } @@ -618,15 +617,13 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spanevent_index.Value() == EventIndex::ZpassDone) { if (event->event_type.Value() == EventType::PixelPipeStatControl) { - } - else if (event->event_type.Value() == EventType::PixelPipeStatDump) { + } else if (event->event_type.Value() == EventType::PixelPipeStatDump) { if ((event->Address() & 0x8) == 0) { // occlusion query start if (rasterizer) { rasterizer->StartOcclusionQuery(event->Address()); } - } - else { + } else { // occlusion query end if (rasterizer) { rasterizer->EndOcclusionQuery(event->Address() & ~0xF); diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index a61f46278..898d8cb69 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -419,7 +419,7 @@ struct PM4CmdEventWrite { template T Address() const { ASSERT(event_index.Value() >= EventIndex::ZpassDone && - event_index.Value() <= EventIndex::SampleStreamoutStatSx); + event_index.Value() <= EventIndex::SampleStreamoutStatSx); return reinterpret_cast((u64(address[1]) << 32u) | u64(address[0])); } }; @@ -1145,7 +1145,8 @@ struct PM4CmdSetPredication { template T Address() const { - return reinterpret_cast(u64(start_address_lo.Value()) << 4 | u64(start_address_hi.Value()) << 32); + return reinterpret_cast(u64(start_address_lo.Value()) << 4 | + u64(start_address_hi.Value()) << 32); } }; diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 12d187403..e6ca0d546 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -18,7 +18,7 @@ #endif namespace { - const int OCCLUSION_QUERIES_COUNT = 16; +const int OCCLUSION_QUERIES_COUNT = 16; } namespace Vulkan { @@ -43,8 +43,13 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, buffer_cache{instance, scheduler, *this, liverpool_, texture_cache, page_manager}, texture_cache{instance, scheduler, buffer_cache, page_manager}, liverpool{liverpool_}, memory{Core::Memory::Instance()}, pipeline_cache{instance, scheduler, liverpool}, - occlusion_query_buffer{instance, scheduler, VideoCore::MemoryUsage::DeviceLocal, 0, - vk::BufferUsageFlagBits::eConditionalRenderingEXT | vk::BufferUsageFlagBits::eTransferDst, sizeof(u32)*OCCLUSION_QUERIES_COUNT} { + occlusion_query_buffer{instance, + scheduler, + VideoCore::MemoryUsage::DeviceLocal, + 0, + vk::BufferUsageFlagBits::eConditionalRenderingEXT | + vk::BufferUsageFlagBits::eTransferDst, + sizeof(u32) * OCCLUSION_QUERIES_COUNT} { if (!Config::nullGpu()) { liverpool->BindRasterizer(this); } @@ -54,7 +59,8 @@ Rasterizer::Rasterizer(const Instance& instance_, Scheduler& scheduler_, .queryCount = OCCLUSION_QUERIES_COUNT, })); instance.GetDevice().resetQueryPool(occlusion_query_pool, 0, OCCLUSION_QUERIES_COUNT); - Vulkan::SetObjectName(instance.GetDevice(), occlusion_query_buffer.Handle(), "OcclusionQueryBuffer:{:#x}", sizeof(u32)*OCCLUSION_QUERIES_COUNT); + Vulkan::SetObjectName(instance.GetDevice(), occlusion_query_buffer.Handle(), + "OcclusionQueryBuffer:{:#x}", sizeof(u32) * OCCLUSION_QUERIES_COUNT); } Rasterizer::~Rasterizer() = default; @@ -1290,8 +1296,9 @@ void Rasterizer::StartPredication(VAddr addr, bool draw_if_visible, bool wait_fo const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.copyQueryPoolResults(occlusion_query_pool, index, 1, occlusion_query_buffer.Handle(), - index*sizeof(u32), sizeof(u32), wait_for_result ? vk::QueryResultFlagBits::eWait - : vk::QueryResultFlagBits::ePartial); + index * sizeof(u32), sizeof(u32), + wait_for_result ? vk::QueryResultFlagBits::eWait + : vk::QueryResultFlagBits::ePartial); const auto pre_barrier = vk::BufferMemoryBarrier2{ .srcStageMask = vk::PipelineStageFlagBits2::eCopy, @@ -1309,7 +1316,7 @@ void Rasterizer::StartPredication(VAddr addr, bool draw_if_visible, bool wait_fo }); ScopeMarkerBegin("gfx:{}:predication", fmt::ptr(reinterpret_cast(addr))); - vk::ConditionalRenderingBeginInfoEXT conditional_rendering_info { + vk::ConditionalRenderingBeginInfoEXT conditional_rendering_info{ .buffer = occlusion_query_buffer.Handle(), .offset = index * sizeof(u32), .flags = draw_if_visible ? vk::ConditionalRenderingFlagBitsEXT::eInverted From 4a49e2bb31d83ca5c4d20ae9a828daecf99a9dd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Tue, 27 May 2025 18:03:38 +0100 Subject: [PATCH 09/31] Include a barrier for conditional rendering read --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index e6ca0d546..63cbbb2b5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1309,8 +1309,17 @@ void Rasterizer::StartPredication(VAddr addr, bool draw_if_visible, bool wait_fo .offset = index * sizeof(u32), .size = sizeof(u32), }; + + const vk::MemoryBarrier2 ib_barrier{ + .srcStageMask = vk::PipelineStageFlagBits2::eCopy, + .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, + .dstStageMask = vk::PipelineStageFlagBits2::eConditionalRenderingEXT, + .dstAccessMask = vk::AccessFlagBits2::eConditionalRenderingReadEXT, + }; cmdbuf.pipelineBarrier2(vk::DependencyInfo{ .dependencyFlags = vk::DependencyFlagBits::eByRegion, + .memoryBarrierCount = 1, + .pMemoryBarriers = &ib_barrier, .bufferMemoryBarrierCount = 1, .pBufferMemoryBarriers = &pre_barrier, }); From c52778a2d9cc3ed7292fdbe8e36d7b98d7f1c52c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Tue, 27 May 2025 23:49:37 +0100 Subject: [PATCH 10/31] Fixes --- src/video_core/amdgpu/liverpool.cpp | 5 +++++ src/video_core/amdgpu/pm4_cmds.h | 6 +++--- src/video_core/renderer_vulkan/vk_instance.cpp | 2 +- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 5 ++++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.cpp b/src/video_core/amdgpu/liverpool.cpp index c99dbb9a9..3358af2b7 100644 --- a/src/video_core/amdgpu/liverpool.cpp +++ b/src/video_core/amdgpu/liverpool.cpp @@ -239,6 +239,11 @@ Liverpool::Task Liverpool::ProcessGraphics(std::span dcb, std::spantype3.NumWords(); const PM4ItOpcode opcode = header->type3.opcode; + const auto predicate = header->type3.predicate; + if (predicate == PM4Predicate::PredEnable) { + LOG_DEBUG(Render_Vulkan, "PM4 command {} is predicated", + magic_enum::enum_name(opcode)); + } switch (opcode) { case PM4ItOpcode::Nop: { const auto* nop = reinterpret_cast(header); diff --git a/src/video_core/amdgpu/pm4_cmds.h b/src/video_core/amdgpu/pm4_cmds.h index 898d8cb69..d4ce9a03a 100644 --- a/src/video_core/amdgpu/pm4_cmds.h +++ b/src/video_core/amdgpu/pm4_cmds.h @@ -420,7 +420,7 @@ struct PM4CmdEventWrite { T Address() const { ASSERT(event_index.Value() >= EventIndex::ZpassDone && event_index.Value() <= EventIndex::SampleStreamoutStatSx); - return reinterpret_cast((u64(address[1]) << 32u) | u64(address[0])); + return std::bit_cast((u64(address[1]) << 32u) | u64(address[0])); } }; @@ -1145,8 +1145,8 @@ struct PM4CmdSetPredication { template T Address() const { - return reinterpret_cast(u64(start_address_lo.Value()) << 4 | - u64(start_address_hi.Value()) << 32); + return std::bit_cast(u64(start_address_lo.Value()) << 4 | u64(start_address_hi.Value()) + << 32); } }; diff --git a/src/video_core/renderer_vulkan/vk_instance.cpp b/src/video_core/renderer_vulkan/vk_instance.cpp index f9e9606ca..654f611f6 100644 --- a/src/video_core/renderer_vulkan/vk_instance.cpp +++ b/src/video_core/renderer_vulkan/vk_instance.cpp @@ -424,7 +424,7 @@ bool Instance::CreateDevice() { }, vk::PhysicalDeviceConditionalRenderingFeaturesEXT{ .conditionalRendering = true, - } + }, #ifdef __APPLE__ portability_features, #endif diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 63cbbb2b5..02539d4a2 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -1290,7 +1290,10 @@ void Rasterizer::StartPredication(VAddr addr, bool draw_if_visible, bool wait_fo ASSERT(occlusion_index_mapping.contains(addr)); auto index = occlusion_index_mapping[addr]; - LOG_DEBUG(Render_Vulkan, "addr = {:#x}, index = {}", addr, index); + LOG_DEBUG(Render_Vulkan, + "addr = {:#x}, index = {}, draw_if_visible = {} " + "wait_for_result = {}", + addr, index, draw_if_visible, wait_for_result); scheduler.EndRendering(); const auto cmdbuf = scheduler.CommandBuffer(); From f1cdd0168a198e961a87d9414af8ea296a8e7a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Tue, 27 May 2025 23:49:37 +0100 Subject: [PATCH 11/31] Fixes --- src/video_core/renderer_vulkan/vk_rasterizer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 02539d4a2..4c2053ac4 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -18,7 +18,7 @@ #endif namespace { -const int OCCLUSION_QUERIES_COUNT = 16; +const int OCCLUSION_QUERIES_COUNT = 256; } namespace Vulkan { @@ -1291,7 +1291,7 @@ void Rasterizer::StartPredication(VAddr addr, bool draw_if_visible, bool wait_fo auto index = occlusion_index_mapping[addr]; LOG_DEBUG(Render_Vulkan, - "addr = {:#x}, index = {}, draw_if_visible = {} " + "addr = {:#x}, index = {}, draw_if_visible = {}, " "wait_for_result = {}", addr, index, draw_if_visible, wait_for_result); From 8a5c056ed388c8a86be0f811d58a2a34e8ce11a2 Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 29 May 2025 11:59:34 +0300 Subject: [PATCH 12/31] Implemented sceNetInetNtop in RE (#3003) * implemented sceNetInetNtop in RE * some logging * added freebsd_inet_ntop4 * freebsd_inet_ntop6 * fixups based on reviews * review fixes --- src/core/libraries/network/net.cpp | 148 +++++++++++++++++++++++++++-- src/core/libraries/network/net.h | 4 + 2 files changed, 144 insertions(+), 8 deletions(-) diff --git a/src/core/libraries/network/net.cpp b/src/core/libraries/network/net.cpp index 0ef4a84f5..9607f0d78 100644 --- a/src/core/libraries/network/net.cpp +++ b/src/core/libraries/network/net.cpp @@ -955,16 +955,148 @@ u16 PS4_SYSV_ABI sceNetHtons(u16 host16) { return htons(host16); } -const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) { #ifdef WIN32 - const char* res = InetNtopA(af, src, dst, size); -#else - const char* res = inet_ntop(af, src, dst, size); -#endif - if (res == nullptr) { - UNREACHABLE(); +// there isn't a strlcpy function in windows so implement one +u64 strlcpy(char* dst, const char* src, u64 size) { + u64 src_len = strlen(src); + + if (size > 0) { + u64 copy_len = (src_len >= size) ? (size - 1) : src_len; + memcpy(dst, src, copy_len); + dst[copy_len] = '\0'; } - return dst; + + return src_len; +} + +#endif + +const char* freebsd_inet_ntop4(const char* src, char* dst, u64 size) { + static const char fmt[] = "%u.%u.%u.%u"; + char tmp[sizeof "255.255.255.255"]; + int l; + + l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); + if (l <= 0 || (socklen_t)l >= size) { + return nullptr; + } + strlcpy(dst, tmp, size); + return (dst); +} + +const char* freebsd_inet_ntop6(const char* src, char* dst, u64 size) { + /* + * Note that int32_t and int16_t need only be "at least" large enough + * to contain a value of the specified size. On some systems, like + * Crays, there is no such thing as an integer variable with 16 bits. + * Keep this in mind if you think this function should have been coded + * to use pointer overlays. All the world's not a VAX. + */ + char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; + struct { + int base, len; + } best, cur; +#define NS_IN6ADDRSZ 16 +#define NS_INT16SZ 2 + u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; + int i; + + /* + * Preprocess: + * Copy the input (bytewise) array into a wordwise array. + * Find the longest run of 0x00's in src[] for :: shorthanding. + */ + memset(words, '\0', sizeof words); + for (i = 0; i < NS_IN6ADDRSZ; i++) + words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); + best.base = -1; + best.len = 0; + cur.base = -1; + cur.len = 0; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + if (words[i] == 0) { + if (cur.base == -1) + cur.base = i, cur.len = 1; + else + cur.len++; + } else { + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + cur.base = -1; + } + } + } + if (cur.base != -1) { + if (best.base == -1 || cur.len > best.len) + best = cur; + } + if (best.base != -1 && best.len < 2) + best.base = -1; + + /* + * Format the result. + */ + tp = tmp; + for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { + /* Are we inside the best run of 0x00's? */ + if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { + if (i == best.base) + *tp++ = ':'; + continue; + } + /* Are we following an initial run of 0x00s or any real hex? */ + if (i != 0) + *tp++ = ':'; + /* Is this address an encapsulated IPv4? */ + if (i == 6 && best.base == 0 && + (best.len == 6 || (best.len == 7 && words[7] != 0x0001) || + (best.len == 5 && words[5] == 0xffff))) { + if (!freebsd_inet_ntop4(src + 12, tp, sizeof tmp - (tp - tmp))) + return nullptr; + tp += strlen(tp); + break; + } + tp += sprintf(tp, "%x", words[i]); + } + /* Was it a trailing run of 0x00's? */ + if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ)) + *tp++ = ':'; + *tp++ = '\0'; + + /* + * Check for overflow, copy, and we're done. + */ + if ((u64)(tp - tmp) > size) { + return nullptr; + } + strcpy(dst, tmp); + return (dst); +} +const char* PS4_SYSV_ABI sceNetInetNtop(int af, const void* src, char* dst, u32 size) { + if (!(src && dst)) { + *sceNetErrnoLoc() = ORBIS_NET_ENOSPC; + LOG_ERROR(Lib_Net, "returned ORBIS_NET_ENOSPC"); + return nullptr; + } + const char* returnvalue = nullptr; + switch (af) { + case ORBIS_NET_AF_INET: + returnvalue = freebsd_inet_ntop4((const char*)src, dst, size); + break; + case ORBIS_NET_AF_INET6: + returnvalue = freebsd_inet_ntop6((const char*)src, dst, size); + break; + default: + *sceNetErrnoLoc() = ORBIS_NET_EAFNOSUPPORT; + LOG_ERROR(Lib_Net, "returned ORBIS_NET_EAFNOSUPPORT"); + return nullptr; + } + if (returnvalue == nullptr) { + *sceNetErrnoLoc() = ORBIS_NET_ENOSPC; + LOG_ERROR(Lib_Net, "returned ORBIS_NET_ENOSPC"); + } + return returnvalue; } int PS4_SYSV_ABI sceNetInetNtopWithScopeId() { diff --git a/src/core/libraries/network/net.h b/src/core/libraries/network/net.h index 812ee6bd7..1393ecb1d 100644 --- a/src/core/libraries/network/net.h +++ b/src/core/libraries/network/net.h @@ -20,6 +20,10 @@ class SymbolsResolver; namespace Libraries::Net { +enum OrbisNetFamily : u32 { + ORBIS_NET_AF_INET = 2, + ORBIS_NET_AF_INET6 = 28, +}; enum OrbisNetSocketType : u32 { ORBIS_NET_SOCK_STREAM = 1, ORBIS_NET_SOCK_DGRAM = 2, From f91cf9f869056891fb7b84e96fbf69ed411110fc Mon Sep 17 00:00:00 2001 From: Fire Cube Date: Thu, 29 May 2025 11:33:56 +0200 Subject: [PATCH 13/31] Libs: CompanionUtil (#2963) * impl * more * move log * cleanup type definitions * error code cleanup and clang * cleanup ugly RE code * shame * clang --------- Co-authored-by: georgemoralis --- CMakeLists.txt | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + .../libraries/companion/companion_error.h | 7 ++ .../libraries/companion/companion_util.cpp | 72 +++++++++++++++++++ src/core/libraries/companion/companion_util.h | 33 +++++++++ src/core/libraries/libs.cpp | 2 + 7 files changed, 118 insertions(+) create mode 100644 src/core/libraries/companion/companion_util.cpp create mode 100644 src/core/libraries/companion/companion_util.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7962488c8..f85d22b6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -603,6 +603,8 @@ set(CAMERA_LIBS src/core/libraries/camera/camera.cpp set(COMPANION_LIBS src/core/libraries/companion/companion_httpd.cpp src/core/libraries/companion/companion_httpd.h + src/core/libraries/companion/companion_util.cpp + src/core/libraries/companion/companion_util.h src/core/libraries/companion/companion_error.h ) set(DEV_TOOLS src/core/devtools/layer.cpp diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index 231cbf849..fd0614e1b 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -140,6 +140,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, SigninDialog) \ SUB(Lib, Camera) \ SUB(Lib, CompanionHttpd) \ + SUB(Lib, CompanionUtil) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index e4eae59af..bbfd7455b 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -107,6 +107,7 @@ enum class Class : u8 { Lib_SigninDialog, ///< The LibSigninDialog implementation. Lib_Camera, ///< The LibCamera implementation. Lib_CompanionHttpd, ///< The LibCompanionHttpd implementation. + Lib_CompanionUtil, ///< The LibCompanionUtil implementation. Frontend, ///< Emulator UI Render, ///< Video Core Render_Vulkan, ///< Vulkan backend diff --git a/src/core/libraries/companion/companion_error.h b/src/core/libraries/companion/companion_error.h index 2d1a3833c..0459c33f8 100644 --- a/src/core/libraries/companion/companion_error.h +++ b/src/core/libraries/companion/companion_error.h @@ -3,6 +3,8 @@ #pragma once +#include "common/types.h" + // companion_httpd error codes constexpr int ORBIS_COMPANION_HTTPD_ERROR_UNKNOWN = 0x80E40001; constexpr int ORBIS_COMPANION_HTTPD_ERROR_FATAL = 0x80E40002; @@ -18,3 +20,8 @@ constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_STARTED = 0x80E4000B; constexpr int ORBIS_COMPANION_HTTPD_ERROR_ALREADY_REGISTERED = 0x80E4000; constexpr int ORBIS_COMPANION_HTTPD_ERROR_NOT_CONNECTED = 0x80E4000D; constexpr int ORBIS_COMPANION_HTTPD_ERROR_USER_NOT_FOUND = 0x80E4000E; + +// companion_util error codes +constexpr u32 ORBIS_COMPANION_UTIL_INVALID_ARGUMENT = 0x80AD0004; +constexpr u32 ORBIS_COMPANION_UTIL_INVALID_POINTER = 0x80AD0006; +constexpr u32 ORBIS_COMPANION_UTIL_NO_EVENT = 0x80AD0008; \ No newline at end of file diff --git a/src/core/libraries/companion/companion_util.cpp b/src/core/libraries/companion/companion_util.cpp new file mode 100644 index 000000000..c144ebdcc --- /dev/null +++ b/src/core/libraries/companion/companion_util.cpp @@ -0,0 +1,72 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "companion_error.h" +#include "core/libraries/companion/companion_util.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" + +namespace Libraries::CompanionUtil { + +u32 PS4_SYSV_ABI getEvent(sceCompanionUtilContext* ctx, sceCompanionUtilEvent* outEvent, + s32 param_3) { + if (outEvent == 0) { + return ORBIS_COMPANION_UTIL_INVALID_ARGUMENT; + } + + if (ctx == nullptr) { + return ORBIS_COMPANION_UTIL_INVALID_POINTER; + } + + uint8_t* base = ctx->blob; + int flag = *reinterpret_cast(base + 0x178); + if (flag == 0) { + return ORBIS_COMPANION_UTIL_NO_EVENT; + } + + return ORBIS_COMPANION_UTIL_OK; +} + +s32 PS4_SYSV_ABI sceCompanionUtilGetEvent(sceCompanionUtilEvent* outEvent) { + sceCompanionUtilContext* ctx = nullptr; + u32 ret = getEvent(ctx, outEvent, 1); + + LOG_DEBUG(Lib_CompanionUtil, "(STUBBED) called ret: {}", ret); + return ret; +} + +s32 PS4_SYSV_ABI sceCompanionUtilGetRemoteOskEvent() { + LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCompanionUtilInitialize() { + LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCompanionUtilOptParamInitialize() { + LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceCompanionUtilTerminate() { + LOG_ERROR(Lib_CompanionUtil, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceCompanionUtil(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("cE5Msy11WhU", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1, + sceCompanionUtilGetEvent); + LIB_FUNCTION("MaVrz79mT5o", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1, + sceCompanionUtilGetRemoteOskEvent); + LIB_FUNCTION("xb1xlIhf0QY", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1, + sceCompanionUtilInitialize); + LIB_FUNCTION("IPN-FRSrafk", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1, + sceCompanionUtilOptParamInitialize); + LIB_FUNCTION("H1fYQd5lFAI", "libSceCompanionUtil", 1, "libSceCompanionUtil", 1, 1, + sceCompanionUtilTerminate); +}; + +} // namespace Libraries::CompanionUtil \ No newline at end of file diff --git a/src/core/libraries/companion/companion_util.h b/src/core/libraries/companion/companion_util.h new file mode 100644 index 000000000..921b5b21e --- /dev/null +++ b/src/core/libraries/companion/companion_util.h @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::CompanionUtil { + +constexpr u32 ORBIS_COMPANION_UTIL_OK = 0; + +struct sceCompanionUtilEvent { + std::uint8_t blob[0x104]{}; /// 0x104 bytes of data, dont know what it is exactly +}; + +struct sceCompanionUtilContext { + std::uint8_t blob[0x27B]{}; /// 0x27B bytes of data, dont know what it is exactly +}; + +u32 PS4_SYSV_ABI getEvent(sceCompanionUtilContext* ctx, sceCompanionUtilEvent* outEvent, + s32 param_3); +s32 PS4_SYSV_ABI sceCompanionUtilGetEvent(sceCompanionUtilEvent* outEvent); +s32 PS4_SYSV_ABI sceCompanionUtilGetRemoteOskEvent(); +s32 PS4_SYSV_ABI sceCompanionUtilInitialize(); +s32 PS4_SYSV_ABI sceCompanionUtilOptParamInitialize(); +s32 PS4_SYSV_ABI sceCompanionUtilTerminate(); + +void RegisterlibSceCompanionUtil(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::CompanionUtil \ No newline at end of file diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 2ab46d3a0..45b32846f 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -10,6 +10,7 @@ #include "core/libraries/avplayer/avplayer.h" #include "core/libraries/camera/camera.h" #include "core/libraries/companion/companion_httpd.h" +#include "core/libraries/companion/companion_util.h" #include "core/libraries/disc_map/disc_map.h" #include "core/libraries/game_live_streaming/gamelivestreaming.h" #include "core/libraries/gnmdriver/gnmdriver.h" @@ -126,6 +127,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::SigninDialog::RegisterlibSceSigninDialog(sym); Libraries::Camera::RegisterlibSceCamera(sym); Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym); + Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym); } } // namespace Libraries From 537394bd54ff663034c60499802ba2f46252591c Mon Sep 17 00:00:00 2001 From: georgemoralis Date: Thu, 29 May 2025 12:42:52 +0300 Subject: [PATCH 14/31] New Crowdin updates (#2982) * New translations en_us.ts (Spanish) * New translations en_us.ts (Spanish) * New translations en_us.ts (Turkish) --- src/qt_gui/translations/es_ES.ts | 4 ++-- src/qt_gui/translations/tr_TR.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/translations/es_ES.ts b/src/qt_gui/translations/es_ES.ts index e73386c96..9568388cc 100644 --- a/src/qt_gui/translations/es_ES.ts +++ b/src/qt_gui/translations/es_ES.ts @@ -26,7 +26,7 @@ Cheats/Patches are experimental.\nUse with caution.\n\nDownload cheats individually by selecting the repository and clicking the download button.\nIn the Patches tab, you can download all patches at once, choose which ones you want to use, and save your selection.\n\nSince we do not develop the Cheats/Patches,\nplease report issues to the cheat author.\n\nCreated a new cheat? Visit:\n - Los cheats/patches son experimentales.\nÚselos con precaución.\n\nDescargue los cheats individualmente seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Patches, puede descargar todos los patches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los Cheats/Patches,\npor favor informe los problemas al autor del cheat.\n\n¿Creaste un nuevo cheat? Visita:\n + Los trucos/parches son experimentales.\nÚselos con precaución.\n\nPuede descargar cada truco seleccionando el repositorio y haciendo clic en el botón de descarga.\nEn la pestaña Parches podrá descargar todos los parches a la vez, elegir cuáles desea usar y guardar la selección.\n\nComo no desarrollamos los trucos/parches,\ndebe informar de cualquier problema a sus autores correspondientes.\n\n¿Creaste un truco nuevo? Visita:\n No Image Available @@ -2048,7 +2048,7 @@ * Unsupported Vulkan Version - * Unsupported Vulkan Version + * Versión de Vulkan no soportada diff --git a/src/qt_gui/translations/tr_TR.ts b/src/qt_gui/translations/tr_TR.ts index 9539ca139..e61985e90 100644 --- a/src/qt_gui/translations/tr_TR.ts +++ b/src/qt_gui/translations/tr_TR.ts @@ -2048,7 +2048,7 @@ * Unsupported Vulkan Version - * Unsupported Vulkan Version + * Desteklenmeyen Vulkan Sürümü From 754639a1ef9e9da12998d4a7e81cd7231d44adc8 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Thu, 29 May 2025 10:56:03 -0500 Subject: [PATCH 15/31] Core: More Memory Cleanup & Fixes (#2997) * Only perform GPU memory mapping when GPU can access it This better aligns with hardware observations, and should also speed up unmaps and decommits, since they don't need to be compared with the GPU max address anymore. * Reserve fixes ReserveVirtualRange seems to follow the 0x200000000 base address like MemoryPoolReserve does. Both also need checks in their flags Fixed path to ensure we're mapping in-bounds. If we're not in mapping to our address space, we'll end up reserving and returning the wrong address, which could lead to weird memory issues in games. I'll need to test on real hardware to verify if such changes are appropriate. * Better sceKernelMmap Handles errors where we would previously throw exceptions. Also moves the file logic to MapFile, since that way all the possible errors are in one place. Also fixes some function parameters to align with our current standards. * Major refactor MapDirectMemory, MapFlexibleMemory, ReserveVirtualRange, and MemoryPoolReserve all internally use mmap to perform their mappings. Naturally, this means that all functions have similar behaviors, and a lot of duplicate code. This add necessary conditional behavior to MapMemory so MemoryPoolReserve and ReserveVirtualRange can use it, without disrupting the behavior of MapDirectMemory or MapFlexibleMemory calls. * Accurate phys_addr for non-direct mappings * Properly handle GPU access rights Since my first commit restricts GPU mappings to memory areas with GPU access permissions, we also need to be updating the GPU mappings appropriately during Protect calls too. * Update memory.cpp * Update memory.h * Update memory.cpp * Update memory.cpp * Update memory.cpp * Revert "Update memory.cpp" This reverts commit 2c55d014c0efbdfadee4121b01e1dcf5af60e63d. * Coalesce dmem map Aligns with hardware observations, hopefully shouldn't break anything since nothing should change hardware-wise when release dmem calls and unmap calls are performed? Either that or Windows breaks because Windows, will need to test. * Implement posix_mprotect Unity calls this Also fixes the names of sceKernelMprotect and sceKernelMtypeprotect, though that's more of a style change and can be reverted if requested. * Fix sceKernelSetVirtualRangeName Partially addresses a "regression" introduced when I fixed up some asserts. As noted in the code, this implementation is still slightly inaccurate, as handling this properly could cause regressions on Windows. * Unconditional assert in MapFile * Remove protect warning This is expected behavior, shouldn't need any logging. * Respect alignment Forgot to properly do this when updating ReserveVirtualRange and MemoryPoolReserve * Fix Mprotect on free memory On real hardware, this just does nothing. If something did get protected, there's no way to query that information. Therefore, it seems pretty safe to just behave like munmap and return size here. * Minor tidy-up No functional difference, but looks better. --- src/core/libraries/kernel/memory.cpp | 106 +++++++----- src/core/libraries/kernel/memory.h | 18 +- src/core/memory.cpp | 236 +++++++++++++-------------- src/core/memory.h | 14 +- 4 files changed, 193 insertions(+), 181 deletions(-) diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index cb41a664a..ce694dc1e 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -8,7 +8,6 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/singleton.h" -#include "core/file_sys/fs.h" #include "core/libraries/kernel/kernel.h" #include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/orbis_error.h" @@ -152,7 +151,8 @@ s32 PS4_SYSV_ABI sceKernelReserveVirtualRange(void** addr, u64 len, int flags, u const VAddr in_addr = reinterpret_cast(*addr); const auto map_flags = static_cast(flags); - s32 result = memory->Reserve(addr, in_addr, len, map_flags, alignment); + s32 result = memory->MapMemory(addr, in_addr, len, Core::MemoryProt::NoAccess, map_flags, + Core::VMAType::Reserved, "anon", false, -1, alignment); if (result == 0) { LOG_INFO(Kernel_Vmm, "out_addr = {}", fmt::ptr(*addr)); } @@ -263,13 +263,22 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** return memory->QueryProtection(std::bit_cast(addr), start, end, prot); } -int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot) { +s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) { Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryProt protection_flags = static_cast(prot); return memory_manager->Protect(std::bit_cast(addr), size, protection_flags); } -int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot) { +s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) { + s32 result = sceKernelMprotect(addr, size, prot); + if (result < 0) { + ErrSceToPosix(result); + return -1; + } + return result; +} + +s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) { Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryProt protection_flags = static_cast(prot); return memory_manager->Protect(std::bit_cast(addr), size, protection_flags); @@ -344,7 +353,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn break; } case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_PROTECT: { - result = sceKernelMProtect(entries[i].start, entries[i].length, entries[i].protection); + result = sceKernelMprotect(entries[i].start, entries[i].length, entries[i].protection); LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, entries[i].operation, entries[i].length, result); break; @@ -359,7 +368,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn break; } case MemoryOpTypes::ORBIS_KERNEL_MAP_OP_TYPE_PROTECT: { - result = sceKernelMTypeProtect(entries[i].start, entries[i].length, entries[i].type, + result = sceKernelMtypeprotect(entries[i].start, entries[i].length, entries[i].type, entries[i].protection); LOG_INFO(Kernel_Vmm, "entry = {}, operation = {}, len = {:#x}, result = {}", i, entries[i].operation, entries[i].length, result); @@ -380,7 +389,7 @@ s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEn return result; } -s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name) { +s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name) { if (name == nullptr) { LOG_ERROR(Kernel_Vmm, "name is invalid!"); return ORBIS_KERNEL_ERROR_EFAULT; @@ -396,8 +405,8 @@ s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, cons return ORBIS_OK; } -s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len, - size_t alignment, u64* physAddrOut) { +s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment, + u64* physAddrOut) { if (searchStart < 0 || searchEnd <= searchStart) { LOG_ERROR(Kernel_Vmm, "Provided address range is invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -439,10 +448,10 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_ return ORBIS_OK; } -s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags, - void** addrOut) { - LOG_INFO(Kernel_Vmm, "addrIn = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}", - fmt::ptr(addrIn), len, alignment, flags); +s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags, + void** addr_out) { + LOG_INFO(Kernel_Vmm, "addr_in = {}, len = {:#x}, alignment = {:#x}, flags = {:#x}", + fmt::ptr(addr_in), len, alignment, flags); if (len == 0 || !Common::Is2MBAligned(len)) { LOG_ERROR(Kernel_Vmm, "Map size is either zero or not 2MB aligned!"); @@ -456,14 +465,16 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t ali } auto* memory = Core::Memory::Instance(); - const VAddr in_addr = reinterpret_cast(addrIn); + const VAddr in_addr = reinterpret_cast(addr_in); const auto map_flags = static_cast(flags); - memory->PoolReserve(addrOut, in_addr, len, map_flags, alignment); + u64 map_alignment = alignment == 0 ? 2_MB : alignment; - return ORBIS_OK; + return memory->MapMemory(addr_out, std::bit_cast(addr_in), len, + Core::MemoryProt::NoAccess, map_flags, Core::VMAType::PoolReserved, + "anon", false, -1, map_alignment); } -s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags) { +s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags) { if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -482,7 +493,7 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int return memory->PoolCommit(in_addr, len, mem_prot); } -s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags) { +s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags) { if (addr == nullptr) { LOG_ERROR(Kernel_Vmm, "Address is invalid!"); return ORBIS_KERNEL_ERROR_EINVAL; @@ -523,12 +534,12 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* break; } case OrbisKernelMemoryPoolOpcode::Protect: { - result = sceKernelMProtect(entry.protect_params.addr, entry.protect_params.len, + result = sceKernelMprotect(entry.protect_params.addr, entry.protect_params.len, entry.protect_params.prot); break; } case OrbisKernelMemoryPoolOpcode::TypeProtect: { - result = sceKernelMTypeProtect( + result = sceKernelMtypeprotect( entry.type_protect_params.addr, entry.type_protect_params.len, entry.type_protect_params.type, entry.type_protect_params.prot); break; @@ -553,30 +564,48 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* return result; } -int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, - void** res) { - LOG_INFO(Kernel_Vmm, "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, offset = {}", - fmt::ptr(addr), len, prot, flags, fd, offset); - auto* h = Common::Singleton::Instance(); +void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr) { + LOG_INFO(Kernel_Vmm, + "called addr = {}, len = {}, prot = {}, flags = {}, fd = {}, phys_addr = {}", + fmt::ptr(addr), len, prot, flags, fd, phys_addr); + + void* addr_out; auto* memory = Core::Memory::Instance(); const auto mem_prot = static_cast(prot); const auto mem_flags = static_cast(flags); + + s32 result = ORBIS_OK; if (fd == -1) { - return memory->MapMemory(res, std::bit_cast(addr), len, mem_prot, mem_flags, - Core::VMAType::Flexible); + result = memory->MapMemory(&addr_out, std::bit_cast(addr), len, mem_prot, mem_flags, + Core::VMAType::Flexible); } else { - const uintptr_t handle = h->GetFile(fd)->f.GetFileMapping(); - return memory->MapFile(res, std::bit_cast(addr), len, mem_prot, mem_flags, handle, - offset); + result = memory->MapFile(&addr_out, std::bit_cast(addr), len, mem_prot, mem_flags, + fd, phys_addr); } + + if (result != ORBIS_OK) { + // If the memory mappings fail, mmap sets errno to the appropriate error code, + // then returns (void*)-1; + ErrSceToPosix(result); + return reinterpret_cast(-1); + } + + return addr_out; } -void* PS4_SYSV_ABI posix_mmap(void* addr, u64 len, int prot, int flags, int fd, u64 offset) { - void* ptr; - LOG_INFO(Kernel_Vmm, "posix mmap redirect to sceKernelMmap"); - int result = sceKernelMmap(addr, len, prot, flags, fd, offset, &ptr); - ASSERT(result == 0); - return ptr; +s32 PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, s32 prot, s32 flags, s32 fd, s64 phys_addr, + void** res) { + void* addr_out = posix_mmap(addr, len, prot, flags, fd, phys_addr); + + if (addr_out == reinterpret_cast(-1)) { + // posix_mmap failed, calculate and return the appropriate kernel error code using errno. + LOG_ERROR(Kernel_Fs, "error = {}", *__Error()); + return ErrnoToSceKernelError(*__Error()); + } + + // Set the outputted address + *res = addr_out; + return ORBIS_OK; } s32 PS4_SYSV_ABI sceKernelConfiguredFlexibleMemorySize(u64* sizeOut) { @@ -678,8 +707,9 @@ void RegisterMemory(Core::Loader::SymbolsResolver* sym) { LIB_FUNCTION("n1-v6FgU7MQ", "libkernel", 1, "libkernel", 1, 1, sceKernelConfiguredFlexibleMemorySize); - LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMTypeProtect); - LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMProtect); + LIB_FUNCTION("vSMAm3cxYTY", "libkernel", 1, "libkernel", 1, 1, sceKernelMprotect); + LIB_FUNCTION("YQOfxL4QfeU", "libScePosix", 1, "libkernel", 1, 1, posix_mprotect); + LIB_FUNCTION("9bfdLIyuwCY", "libkernel", 1, "libkernel", 1, 1, sceKernelMtypeprotect); // Memory pool LIB_FUNCTION("qCSfqDILlns", "libkernel", 1, "libkernel", 1, 1, sceKernelMemoryPoolExpand); diff --git a/src/core/libraries/kernel/memory.h b/src/core/libraries/kernel/memory.h index 92e158a00..6cefe0d07 100644 --- a/src/core/libraries/kernel/memory.h +++ b/src/core/libraries/kernel/memory.h @@ -147,9 +147,9 @@ s32 PS4_SYSV_ABI sceKernelMapFlexibleMemory(void** addr_in_out, std::size_t len, int flags); int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** end, u32* prot); -int PS4_SYSV_ABI sceKernelMProtect(const void* addr, size_t size, int prot); +s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot); -int PS4_SYSV_ABI sceKernelMTypeProtect(const void* addr, size_t size, int mtype, int prot); +s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot); int PS4_SYSV_ABI sceKernelDirectMemoryQuery(u64 offset, int flags, OrbisQueryInfo* query_info, size_t infoSize); @@ -165,14 +165,14 @@ s32 PS4_SYSV_ABI sceKernelBatchMap(OrbisKernelBatchMapEntry* entries, int numEnt s32 PS4_SYSV_ABI sceKernelBatchMap2(OrbisKernelBatchMapEntry* entries, int numEntries, int* numEntriesOut, int flags); -s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, size_t len, const char* name); +s32 PS4_SYSV_ABI sceKernelSetVirtualRangeName(const void* addr, u64 len, const char* name); -s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, size_t len, - size_t alignment, u64* physAddrOut); -s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addrIn, size_t len, size_t alignment, int flags, - void** addrOut); -s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, size_t len, int type, int prot, int flags); -s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags); +s32 PS4_SYSV_ABI sceKernelMemoryPoolExpand(u64 searchStart, u64 searchEnd, u64 len, u64 alignment, + u64* physAddrOut); +s32 PS4_SYSV_ABI sceKernelMemoryPoolReserve(void* addr_in, u64 len, u64 alignment, s32 flags, + void** addr_out); +s32 PS4_SYSV_ABI sceKernelMemoryPoolCommit(void* addr, u64 len, s32 type, s32 prot, s32 flags); +s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, u64 len, s32 flags); s32 PS4_SYSV_ABI sceKernelMemoryPoolBatch(const OrbisKernelMemoryPoolBatchEntry* entries, s32 count, s32* num_processed, s32 flags); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index ca6a0d6cd..ab59219b2 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -5,6 +5,7 @@ #include "common/assert.h" #include "common/config.h" #include "common/debug.h" +#include "core/file_sys/fs.h" #include "core/libraries/kernel/memory.h" #include "core/libraries/kernel/orbis_error.h" #include "core/libraries/kernel/process.h" @@ -181,6 +182,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size, auto& area = CarveDmemArea(mapping_start, size)->second; area.memory_type = memory_type; area.is_free = false; + MergeAdjacent(dmem_map, dmem_area); return mapping_start; } @@ -214,90 +216,6 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) { MergeAdjacent(dmem_map, dmem_area); } -int MemoryManager::PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, - MemoryMapFlags flags, u64 alignment) { - std::scoped_lock lk{mutex}; - alignment = alignment > 0 ? alignment : 2_MB; - VAddr min_address = Common::AlignUp(impl.SystemManagedVirtualBase(), alignment); - VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment); - - // Fixed mapping means the virtual address must exactly match the provided one. - if (True(flags & MemoryMapFlags::Fixed)) { - // Make sure we're mapping to a valid address - mapped_addr = mapped_addr > min_address ? mapped_addr : min_address; - auto vma = FindVMA(mapped_addr)->second; - size_t remaining_size = vma.base + vma.size - mapped_addr; - // If the VMA is mapped or there's not enough space, unmap the region first. - if (vma.IsMapped() || remaining_size < size) { - UnmapMemoryImpl(mapped_addr, size); - vma = FindVMA(mapped_addr)->second; - } - } - - if (False(flags & MemoryMapFlags::Fixed)) { - // When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0, - // search from address 0x200000000 instead. - mapped_addr = mapped_addr == 0 ? 0x200000000 : mapped_addr; - mapped_addr = SearchFree(mapped_addr, size, alignment); - if (mapped_addr == -1) { - // No suitable memory areas to map to - return ORBIS_KERNEL_ERROR_ENOMEM; - } - } - - // Add virtual memory area - const auto new_vma_handle = CarveVMA(mapped_addr, size); - auto& new_vma = new_vma_handle->second; - new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); - new_vma.prot = MemoryProt::NoAccess; - new_vma.name = "anon"; - new_vma.type = VMAType::PoolReserved; - - *out_addr = std::bit_cast(mapped_addr); - return ORBIS_OK; -} - -int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags, - u64 alignment) { - std::scoped_lock lk{mutex}; - - virtual_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; - alignment = alignment > 0 ? alignment : 16_KB; - VAddr mapped_addr = alignment > 0 ? Common::AlignUp(virtual_addr, alignment) : virtual_addr; - - // Fixed mapping means the virtual address must exactly match the provided one. - if (True(flags & MemoryMapFlags::Fixed)) { - auto vma = FindVMA(mapped_addr)->second; - size_t remaining_size = vma.base + vma.size - mapped_addr; - // If the VMA is mapped or there's not enough space, unmap the region first. - if (vma.IsMapped() || remaining_size < size) { - UnmapMemoryImpl(mapped_addr, size); - vma = FindVMA(mapped_addr)->second; - } - } - - // Find the first free area starting with provided virtual address. - if (False(flags & MemoryMapFlags::Fixed)) { - mapped_addr = SearchFree(mapped_addr, size, alignment); - if (mapped_addr == -1) { - // No suitable memory areas to map to - return ORBIS_KERNEL_ERROR_ENOMEM; - } - } - - // Add virtual memory area - const auto new_vma_handle = CarveVMA(mapped_addr, size); - auto& new_vma = new_vma_handle->second; - new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); - new_vma.prot = MemoryProt::NoAccess; - new_vma.name = "anon"; - new_vma.type = VMAType::Reserved; - MergeAdjacent(vma_map, new_vma_handle); - - *out_addr = std::bit_cast(mapped_addr); - return ORBIS_OK; -} - int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) { std::scoped_lock lk{mutex}; @@ -344,14 +262,17 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false); TRACK_ALLOC(out_addr, size, "VMEM"); - if (IsValidGpuMapping(mapped_addr, size)) { + if (prot >= MemoryProt::GpuRead) { + // PS4s only map to GPU memory when the protection includes GPU access. + // If the address to map to is too high, PS4s throw a page fault and crash. + ASSERT_MSG(IsValidGpuMapping(mapped_addr, size), "Invalid address for GPU mapping"); rasterizer->MapMemory(mapped_addr, size); } return ORBIS_OK; } -int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, +s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, VMAType type, std::string_view name, bool is_exec, PAddr phys_addr, u64 alignment) { std::scoped_lock lk{mutex}; @@ -366,17 +287,18 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; // Fixed mapping means the virtual address must exactly match the provided one. - if (True(flags & MemoryMapFlags::Fixed)) { + // On a PS4, the Fixed flag is ignored if address 0 is provided. + if (True(flags & MemoryMapFlags::Fixed) && virtual_addr != 0) { auto vma = FindVMA(mapped_addr)->second; - size_t remaining_size = vma.base + vma.size - mapped_addr; // There's a possible edge case where we're mapping to a partially reserved range. // To account for this, unmap any reserved areas within this mapping range first. auto unmap_addr = mapped_addr; auto unmap_size = size; + // If flag NoOverwrite is provided, don't overwrite mapped VMAs. // When it isn't provided, VMAs can be overwritten regardless of if they're mapped. while ((False(flags & MemoryMapFlags::NoOverwrite) || !vma.IsMapped()) && - unmap_addr < mapped_addr + size && remaining_size < size) { + unmap_addr < mapped_addr + size) { auto unmapped = UnmapBytesFromEntry(unmap_addr, vma, unmap_size); unmap_addr += unmapped; unmap_size -= unmapped; @@ -384,51 +306,69 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M } vma = FindVMA(mapped_addr)->second; - remaining_size = vma.base + vma.size - mapped_addr; + auto remaining_size = vma.base + vma.size - mapped_addr; if (vma.IsMapped() || remaining_size < size) { LOG_ERROR(Kernel_Vmm, "Unable to map {:#x} bytes at address {:#x}", size, mapped_addr); return ORBIS_KERNEL_ERROR_ENOMEM; } - } - - // Find the first free area starting with provided virtual address. - if (False(flags & MemoryMapFlags::Fixed)) { - // Provided address needs to be aligned before we can map. + } else { + // When MemoryMapFlags::Fixed is not specified, and mapped_addr is 0, + // search from address 0x200000000 instead. alignment = alignment > 0 ? alignment : 16_KB; - mapped_addr = SearchFree(Common::AlignUp(mapped_addr, alignment), size, alignment); + mapped_addr = virtual_addr == 0 ? 0x200000000 : mapped_addr; + mapped_addr = SearchFree(mapped_addr, size, alignment); if (mapped_addr == -1) { // No suitable memory areas to map to return ORBIS_KERNEL_ERROR_ENOMEM; } } - // Perform the mapping. - *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec); - TRACK_ALLOC(*out_addr, size, "VMEM"); + // Create a memory area representing this mapping. + const auto new_vma_handle = CarveVMA(mapped_addr, size); + auto& new_vma = new_vma_handle->second; - auto& new_vma = CarveVMA(mapped_addr, size)->second; - new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); - new_vma.prot = prot; - new_vma.name = name; - new_vma.type = type; - new_vma.is_exec = is_exec; - - if (type == VMAType::Direct) { - new_vma.phys_base = phys_addr; - } + // If type is Flexible, we need to track how much flexible memory is used here. if (type == VMAType::Flexible) { flexible_usage += size; } - if (IsValidGpuMapping(mapped_addr, size)) { + new_vma.disallow_merge = True(flags & MemoryMapFlags::NoCoalesce); + new_vma.prot = prot; + new_vma.name = name; + new_vma.type = type; + new_vma.phys_base = phys_addr == -1 ? 0 : phys_addr; + new_vma.is_exec = is_exec; + + if (type == VMAType::Reserved) { + // Technically this should be done for direct and flexible mappings too, + // But some Windows-specific limitations make that hard to accomplish. + MergeAdjacent(vma_map, new_vma_handle); + } + + if (prot >= MemoryProt::GpuRead) { + // PS4s only map to GPU memory when the protection includes GPU access. + // If the address to map to is too high, PS4s throw a page fault and crash. + ASSERT_MSG(IsValidGpuMapping(mapped_addr, size), "Invalid address for GPU mapping"); rasterizer->MapMemory(mapped_addr, size); } + if (type == VMAType::Reserved || type == VMAType::PoolReserved) { + // For Reserved/PoolReserved mappings, we don't perform any address space allocations. + // Just set out_addr to mapped_addr instead. + *out_addr = std::bit_cast(mapped_addr); + } else { + // Type is either Direct, Flexible, or Code, these need to be mapped in our address space. + *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec); + } + + TRACK_ALLOC(*out_addr, size, "VMEM"); return ORBIS_OK; } -int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, - MemoryMapFlags flags, uintptr_t fd, size_t offset) { +s32 MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, + MemoryMapFlags flags, s32 fd, s64 phys_addr) { + auto* h = Common::Singleton::Instance(); + VAddr mapped_addr = (virtual_addr == 0) ? impl.SystemManagedVirtualBase() : virtual_addr; const size_t size_aligned = Common::AlignUp(size, 16_KB); @@ -449,8 +389,19 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem vma.base, vma.base + vma.size, virtual_addr, virtual_addr + size); } - // Map the file. - impl.MapFile(mapped_addr, size_aligned, offset, std::bit_cast(prot), fd); + // Get the file to map + auto file = h->GetFile(fd); + if (file == nullptr) { + return ORBIS_KERNEL_ERROR_EBADF; + } + + const auto handle = file->f.GetFileMapping(); + + impl.MapFile(mapped_addr, size_aligned, phys_addr, std::bit_cast(prot), handle); + + if (prot >= MemoryProt::GpuRead) { + ASSERT_MSG(false, "Files cannot be mapped to GPU memory"); + } // Add virtual memory area auto& new_vma = CarveVMA(mapped_addr, size_aligned)->second; @@ -478,6 +429,7 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { const bool is_exec = vma_base.is_exec; const auto start_in_vma = virtual_addr - vma_base_addr; const auto type = vma_base.type; + const auto prot = vma_base.prot; if (type != VMAType::PoolReserved && type != VMAType::Pooled) { LOG_ERROR(Kernel_Vmm, "Attempting to decommit non-pooled memory!"); @@ -489,7 +441,8 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { pool_budget += size; } - if (IsValidGpuMapping(virtual_addr, size)) { + if (prot >= MemoryProt::GpuRead) { + // If this mapping has GPU access, unmap from GPU. rasterizer->UnmapMemory(virtual_addr, size); } @@ -528,6 +481,7 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma const auto adjusted_size = vma_base_size - start_in_vma < size ? vma_base_size - start_in_vma : size; const bool has_backing = type == VMAType::Direct || type == VMAType::File; + const auto prot = vma_base.prot; if (type == VMAType::Free) { return adjusted_size; @@ -536,8 +490,9 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma flexible_usage -= adjusted_size; } - if (IsValidGpuMapping(virtual_addr, adjusted_size)) { - rasterizer->UnmapMemory(virtual_addr, adjusted_size); + if (prot >= MemoryProt::GpuRead) { + // If this mapping has GPU access, unmap from GPU. + rasterizer->UnmapMemory(virtual_addr, size); } // Mark region as free and attempt to coalesce it with neighbours. @@ -605,8 +560,8 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s vma_base.size - start_in_vma < size ? vma_base.size - start_in_vma : size; if (vma_base.type == VMAType::Free) { - LOG_ERROR(Kernel_Vmm, "Cannot change protection on free memory region"); - return ORBIS_KERNEL_ERROR_EINVAL; + // On PS4, protecting freed memory does nothing. + return adjusted_size; } // Validate protection flags @@ -621,6 +576,18 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s return ORBIS_KERNEL_ERROR_EINVAL; } + if (vma_base.prot < MemoryProt::GpuRead && prot >= MemoryProt::GpuRead) { + // New protection will give the GPU access to this VMA, perform a rasterizer map + ASSERT_MSG(IsValidGpuMapping(addr, size), "Invalid address for GPU mapping"); + rasterizer->MapMemory(addr, size); + } + + if (vma_base.prot >= MemoryProt::GpuRead && prot < MemoryProt::GpuRead) { + // New protection will remove the GPU's access to this VMA, perform a rasterizer unmap + ASSERT_MSG(IsValidGpuMapping(addr, size), "Invalid address for GPU unmap"); + rasterizer->UnmapMemory(addr, size); + } + // Change protection vma_base.prot = prot; @@ -798,12 +765,31 @@ s32 MemoryManager::SetDirectMemoryType(s64 phys_addr, s32 memory_type) { return ORBIS_OK; } -void MemoryManager::NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name) { - auto it = FindVMA(virtual_addr); +void MemoryManager::NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name) { + // Sizes are aligned up to the nearest 16_KB + auto aligned_size = Common::AlignUp(size, 16_KB); + // Addresses are aligned down to the nearest 16_KB + auto aligned_addr = Common::AlignDown(virtual_addr, 16_KB); - ASSERT_MSG(it->second.Contains(virtual_addr, size), - "Range provided is not fully contained in vma"); - it->second.name = name; + auto it = FindVMA(aligned_addr); + s64 remaining_size = aligned_size; + auto current_addr = aligned_addr; + while (remaining_size > 0) { + // Nothing needs to be done to free VMAs + if (!it->second.IsFree()) { + if (remaining_size < it->second.size) { + // We should split VMAs here, but this could cause trouble for Windows. + // Instead log a warning and name the whole VMA. + // it = CarveVMA(current_addr, remaining_size); + LOG_WARNING(Kernel_Vmm, "Trying to partially name a range"); + } + auto& vma = it->second; + vma.name = name; + } + remaining_size -= it->second.size; + current_addr += it->second.size; + it = FindVMA(current_addr); + } } void MemoryManager::InvalidateMemory(const VAddr addr, const u64 size) const { @@ -824,6 +810,8 @@ VAddr MemoryManager::SearchFree(VAddr virtual_addr, size_t size, u32 alignment) ASSERT_MSG(virtual_addr <= max_search_address, "Input address {:#x} is out of bounds", virtual_addr); + // Align up the virtual_addr first. + virtual_addr = Common::AlignUp(virtual_addr, alignment); auto it = FindVMA(virtual_addr); // If the VMA is free and contains the requested mapping we are done. diff --git a/src/core/memory.h b/src/core/memory.h index 883b48854..b3ebe3c27 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -183,20 +183,14 @@ public: void Free(PAddr phys_addr, size_t size); - int PoolReserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags, - u64 alignment = 0); - - int Reserve(void** out_addr, VAddr virtual_addr, size_t size, MemoryMapFlags flags, - u64 alignment = 0); - int PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot); - int MapMemory(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, + s32 MapMemory(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, MemoryMapFlags flags, VMAType type, std::string_view name = "anon", bool is_exec = false, PAddr phys_addr = -1, u64 alignment = 0); - int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, - MemoryMapFlags flags, uintptr_t fd, size_t offset); + s32 MapFile(void** out_addr, VAddr virtual_addr, u64 size, MemoryProt prot, + MemoryMapFlags flags, s32 fd, s64 phys_addr); s32 PoolDecommit(VAddr virtual_addr, size_t size); @@ -221,7 +215,7 @@ public: s32 SetDirectMemoryType(s64 phys_addr, s32 memory_type); - void NameVirtualRange(VAddr virtual_addr, size_t size, std::string_view name); + void NameVirtualRange(VAddr virtual_addr, u64 size, std::string_view name); void InvalidateMemory(VAddr addr, u64 size) const; From 7a1dacc473b87b56c83b23cb7d387b17e0155486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Thu, 29 May 2025 21:20:16 +0200 Subject: [PATCH 16/31] Handle V_CVT_F64_U32 (#3008) --- src/shader_recompiler/frontend/translate/translate.h | 1 + src/shader_recompiler/frontend/translate/vector_alu.cpp | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/src/shader_recompiler/frontend/translate/translate.h b/src/shader_recompiler/frontend/translate/translate.h index 68d5e8dc8..7b4b03f27 100644 --- a/src/shader_recompiler/frontend/translate/translate.h +++ b/src/shader_recompiler/frontend/translate/translate.h @@ -183,6 +183,7 @@ public: void V_READFIRSTLANE_B32(const GcnInst& inst); void V_CVT_I32_F64(const GcnInst& inst); void V_CVT_F64_I32(const GcnInst& inst); + void V_CVT_F64_U32(const GcnInst& inst); void V_CVT_F32_I32(const GcnInst& inst); void V_CVT_F32_U32(const GcnInst& inst); void V_CVT_U32_F32(const GcnInst& inst); diff --git a/src/shader_recompiler/frontend/translate/vector_alu.cpp b/src/shader_recompiler/frontend/translate/vector_alu.cpp index 6171cca07..fb3f52c7f 100644 --- a/src/shader_recompiler/frontend/translate/vector_alu.cpp +++ b/src/shader_recompiler/frontend/translate/vector_alu.cpp @@ -110,6 +110,8 @@ void Translator::EmitVectorAlu(const GcnInst& inst) { return V_CVT_I32_F64(inst); case Opcode::V_CVT_F64_I32: return V_CVT_F64_I32(inst); + case Opcode::V_CVT_F64_U32: + return V_CVT_F64_U32(inst); case Opcode::V_CVT_F32_I32: return V_CVT_F32_I32(inst); case Opcode::V_CVT_F32_U32: @@ -684,6 +686,11 @@ void Translator::V_CVT_F64_I32(const GcnInst& inst) { SetDst64(inst.dst[0], ir.ConvertSToF(64, 32, src0)); } +void Translator::V_CVT_F64_U32(const GcnInst& inst) { + const IR::U32 src0{GetSrc(inst.src[0])}; + SetDst64(inst.dst[0], ir.ConvertUToF(64, 32, src0)); +} + void Translator::V_CVT_F32_I32(const GcnInst& inst) { const IR::U32 src0{GetSrc(inst.src[0])}; SetDst(inst.dst[0], ir.ConvertSToF(32, 32, src0)); From 2adcf18dfdc67e620c2f2a46b999e27c92278f91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 30 May 2025 01:56:24 +0200 Subject: [PATCH 17/31] Handle R128 bit in MIMG instructions (#3010) --- src/shader_recompiler/frontend/decode.cpp | 1 - .../frontend/translate/vector_memory.cpp | 5 +++++ src/shader_recompiler/info.h | 9 ++++++++- .../ir/passes/resource_tracking_pass.cpp | 1 + src/shader_recompiler/ir/reg.h | 1 + 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/shader_recompiler/frontend/decode.cpp b/src/shader_recompiler/frontend/decode.cpp index 20b78e869..37e8a0973 100644 --- a/src/shader_recompiler/frontend/decode.cpp +++ b/src/shader_recompiler/frontend/decode.cpp @@ -1032,7 +1032,6 @@ void GcnDecodeContext::decodeInstructionMIMG(uint64_t hexInstruction) { m_instruction.control.mimg = *reinterpret_cast(&hexInstruction); m_instruction.control.mimg.mod = getMimgModifier(m_instruction.opcode); - ASSERT(m_instruction.control.mimg.r128 == 0); } void GcnDecodeContext::decodeInstructionDS(uint64_t hexInstruction) { diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 5639bc56a..8c1946390 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -377,6 +377,7 @@ void Translator::IMAGE_LOAD(bool has_mip, const GcnInst& inst) { IR::TextureInstInfo info{}; info.has_lod.Assign(has_mip); info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); const IR::Value texel = ir.ImageRead(handle, body, {}, {}, info); for (u32 i = 0; i < 4; i++) { @@ -426,6 +427,7 @@ void Translator::IMAGE_GET_RESINFO(const GcnInst& inst) { IR::TextureInstInfo info{}; info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); const IR::Value size = ir.ImageQueryDimension(tsharp, lod, ir.Imm1(has_mips), info); @@ -451,6 +453,7 @@ void Translator::IMAGE_ATOMIC(AtomicOp op, const GcnInst& inst) { IR::TextureInstInfo info{}; info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); const IR::Value value = ir.GetVectorReg(val_reg); const IR::Value handle = ir.GetScalarReg(tsharp_reg); @@ -509,6 +512,7 @@ IR::Value EmitImageSample(IR::IREmitter& ir, const GcnInst& inst, const IR::Scal info.has_lod.Assign(flags.any(MimgModifier::Lod)); info.is_array.Assign(mimg.da); info.is_unnormalized.Assign(mimg.unrm); + info.is_r128.Assign(mimg.r128); if (gather) { info.gather_comp.Assign(std::bit_width(mimg.dmask) - 1); @@ -617,6 +621,7 @@ void Translator::IMAGE_GET_LOD(const GcnInst& inst) { IR::TextureInstInfo info{}; info.is_array.Assign(mimg.da); + info.is_r128.Assign(mimg.r128); const IR::Value handle = ir.GetScalarReg(tsharp_reg); const IR::Value body = ir.CompositeConstruct( diff --git a/src/shader_recompiler/info.h b/src/shader_recompiler/info.h index d349d7827..24e0741c1 100644 --- a/src/shader_recompiler/info.h +++ b/src/shader_recompiler/info.h @@ -84,6 +84,7 @@ struct ImageResource { bool is_atomic{}; bool is_array{}; bool is_written{}; + bool is_r128{}; [[nodiscard]] constexpr AmdGpu::Image GetSharp(const Info& info) const noexcept; }; @@ -293,7 +294,13 @@ constexpr AmdGpu::Buffer BufferResource::GetSharp(const Info& info) const noexce } constexpr AmdGpu::Image ImageResource::GetSharp(const Info& info) const noexcept { - const auto image = info.ReadUdSharp(sharp_idx); + AmdGpu::Image image{0}; + if (!is_r128) { + image = info.ReadUdSharp(sharp_idx); + } else { + AmdGpu::Buffer buf = info.ReadUdSharp(sharp_idx); + memcpy(&image, &buf, sizeof(buf)); + } if (!image.Valid()) { // Fall back to null image if unbound. return AmdGpu::Image::Null(); diff --git a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp index cc0bf83d3..18c77e600 100644 --- a/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp +++ b/src/shader_recompiler/ir/passes/resource_tracking_pass.cpp @@ -411,6 +411,7 @@ void PatchImageSharp(IR::Block& block, IR::Inst& inst, Info& info, Descriptors& .is_atomic = IsImageAtomicInstruction(inst), .is_array = bool(inst_info.is_array), .is_written = is_written, + .is_r128 = bool(inst_info.is_r128), }); IR::IREmitter ir{block, IR::Block::InstructionList::s_iterator_to(inst)}; diff --git a/src/shader_recompiler/ir/reg.h b/src/shader_recompiler/ir/reg.h index 622190cf0..82aa436a7 100644 --- a/src/shader_recompiler/ir/reg.h +++ b/src/shader_recompiler/ir/reg.h @@ -44,6 +44,7 @@ union TextureInstInfo { BitField<9, 1, u32> is_array; BitField<10, 1, u32> is_unnormalized; BitField<11, 1, u32> is_gather; + BitField<12, 1, u32> is_r128; }; union BufferInstInfo { From 529def845d47dcd210d07aea79d57d312611fdbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 30 May 2025 03:51:36 +0200 Subject: [PATCH 18/31] Misc opcodes fixes (#3009) --- src/shader_recompiler/frontend/translate/translate.cpp | 2 +- src/shader_recompiler/frontend/translate/vector_memory.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shader_recompiler/frontend/translate/translate.cpp b/src/shader_recompiler/frontend/translate/translate.cpp index e49f95d9a..5675adf3c 100644 --- a/src/shader_recompiler/frontend/translate/translate.cpp +++ b/src/shader_recompiler/frontend/translate/translate.cpp @@ -380,7 +380,7 @@ T Translator::GetSrc64(const InstOperand& operand) { break; case OperandField::VccLo: if constexpr (is_float) { - UNREACHABLE(); + value = ir.PackDouble2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi())); } else { value = ir.PackUint2x32(ir.CompositeConstruct(ir.GetVccLo(), ir.GetVccHi())); } diff --git a/src/shader_recompiler/frontend/translate/vector_memory.cpp b/src/shader_recompiler/frontend/translate/vector_memory.cpp index 8c1946390..5c972c607 100644 --- a/src/shader_recompiler/frontend/translate/vector_memory.cpp +++ b/src/shader_recompiler/frontend/translate/vector_memory.cpp @@ -152,6 +152,7 @@ void Translator::EmitVectorMemory(const GcnInst& inst) { // Image gather operations case Opcode::IMAGE_GATHER4: + case Opcode::IMAGE_GATHER4_L: case Opcode::IMAGE_GATHER4_LZ: case Opcode::IMAGE_GATHER4_C: case Opcode::IMAGE_GATHER4_O: From e59f3c8da239737c6977e6eaef28caf892a6058a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Fri, 30 May 2025 21:04:31 +0200 Subject: [PATCH 19/31] Provide custom border color to samplers (#3014) --- src/video_core/amdgpu/liverpool.h | 15 ++++++++- .../renderer_vulkan/vk_rasterizer.cpp | 2 +- src/video_core/texture_cache/sampler.cpp | 31 +++++++++++++++++-- src/video_core/texture_cache/sampler.h | 3 +- .../texture_cache/texture_cache.cpp | 6 ++-- src/video_core/texture_cache/texture_cache.h | 4 ++- 6 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/video_core/amdgpu/liverpool.h b/src/video_core/amdgpu/liverpool.h index a62141099..245e34d35 100644 --- a/src/video_core/amdgpu/liverpool.h +++ b/src/video_core/amdgpu/liverpool.h @@ -608,6 +608,16 @@ struct Liverpool { } }; + struct BorderColorBufferBase { + u32 base_addr_lo; + BitField<0, 8, u32> base_addr_hi; + + template + T Address() const { + return std::bit_cast(u64(base_addr_hi) << 40 | u64(base_addr_lo) << 8); + } + }; + struct IndexBufferBase { BitField<0, 8, u32> base_addr_hi; u32 base_addr_lo; @@ -1299,7 +1309,9 @@ struct Liverpool { Scissor screen_scissor; INSERT_PADDING_WORDS(0xA010 - 0xA00C - 2); DepthBuffer depth_buffer; - INSERT_PADDING_WORDS(0xA080 - 0xA018); + INSERT_PADDING_WORDS(8); + BorderColorBufferBase ta_bc_base; + INSERT_PADDING_WORDS(0xA080 - 0xA020 - 2); WindowOffset window_offset; ViewportScissor window_scissor; INSERT_PADDING_WORDS(0xA08E - 0xA081 - 2); @@ -1626,6 +1638,7 @@ static_assert(GFX6_3D_REG_INDEX(depth_htile_data_base) == 0xA005); static_assert(GFX6_3D_REG_INDEX(screen_scissor) == 0xA00C); static_assert(GFX6_3D_REG_INDEX(depth_buffer.z_info) == 0xA010); static_assert(GFX6_3D_REG_INDEX(depth_buffer.depth_slice) == 0xA017); +static_assert(GFX6_3D_REG_INDEX(ta_bc_base) == 0xA020); static_assert(GFX6_3D_REG_INDEX(window_offset) == 0xA080); static_assert(GFX6_3D_REG_INDEX(window_scissor) == 0xA081); static_assert(GFX6_3D_REG_INDEX(color_target_mask) == 0xA08E); diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index 4c2053ac4..d3a292d0b 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -734,7 +734,7 @@ void Rasterizer::BindTextures(const Shader::Info& stage, Shader::Backend::Bindin ssharp.max_aniso.Assign(AmdGpu::AnisoRatio::One); } } - const auto vk_sampler = texture_cache.GetSampler(ssharp); + const auto vk_sampler = texture_cache.GetSampler(ssharp, liverpool->regs.ta_bc_base); image_infos.emplace_back(vk_sampler, VK_NULL_HANDLE, vk::ImageLayout::eGeneral); set_writes.push_back({ .dstSet = VK_NULL_HANDLE, diff --git a/src/video_core/texture_cache/sampler.cpp b/src/video_core/texture_cache/sampler.cpp index 8dbdd2912..e18c79a59 100644 --- a/src/video_core/texture_cache/sampler.cpp +++ b/src/video_core/texture_cache/sampler.cpp @@ -9,7 +9,8 @@ namespace VideoCore { -Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler) { +Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler, + const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base) { if (sampler.force_degamma) { LOG_WARNING(Render_Vulkan, "Texture requires gamma correction"); } @@ -20,7 +21,33 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample const float maxAnisotropy = anisotropyEnable ? std::clamp(sampler.MaxAniso(), 1.0f, instance.MaxSamplerAnisotropy()) : 1.0f; + auto borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type); + if (!instance.IsCustomBorderColorSupported()) { + LOG_WARNING(Render_Vulkan, "Custom border color is not supported, falling back to black"); + borderColor = vk::BorderColor::eFloatOpaqueBlack; + } + + const auto customColor = [&]() -> std::optional { + if (borderColor == vk::BorderColor::eFloatCustomEXT) { + const auto borderColorIndex = sampler.border_color_ptr.Value(); + const auto borderColorBuffer = border_color_base.Address*>(); + const auto customBorderColorArray = borderColorBuffer[borderColorIndex]; + + const vk::SamplerCustomBorderColorCreateInfoEXT ret{ + .customBorderColor = + vk::ClearColorValue{ + .float32 = customBorderColorArray, + }, + .format = vk::Format::eR32G32B32A32Sfloat, + }; + return ret; + } else { + return std::nullopt; + } + }(); + const vk::SamplerCreateInfo sampler_ci = { + .pNext = customColor ? &*customColor : nullptr, .magFilter = LiverpoolToVK::Filter(sampler.xy_mag_filter), .minFilter = LiverpoolToVK::Filter(sampler.xy_min_filter), .mipmapMode = LiverpoolToVK::MipFilter(sampler.mip_filter), @@ -34,7 +61,7 @@ Sampler::Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sample .compareOp = LiverpoolToVK::DepthCompare(sampler.depth_compare_func), .minLod = sampler.MinLod(), .maxLod = sampler.MaxLod(), - .borderColor = LiverpoolToVK::BorderColor(sampler.border_color_type), + .borderColor = borderColor, .unnormalizedCoordinates = false, // Handled in shader due to Vulkan limitations. }; auto [sampler_result, smplr] = instance.GetDevice().createSamplerUnique(sampler_ci); diff --git a/src/video_core/texture_cache/sampler.h b/src/video_core/texture_cache/sampler.h index 856d39763..28ba0f67b 100644 --- a/src/video_core/texture_cache/sampler.h +++ b/src/video_core/texture_cache/sampler.h @@ -14,7 +14,8 @@ namespace VideoCore { class Sampler { public: - explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler); + explicit Sampler(const Vulkan::Instance& instance, const AmdGpu::Sampler& sampler, + const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base); ~Sampler(); Sampler(const Sampler&) = delete; diff --git a/src/video_core/texture_cache/texture_cache.cpp b/src/video_core/texture_cache/texture_cache.cpp index ddb4ea799..63cfc4431 100644 --- a/src/video_core/texture_cache/texture_cache.cpp +++ b/src/video_core/texture_cache/texture_cache.cpp @@ -636,9 +636,11 @@ void TextureCache::RefreshImage(Image& image, Vulkan::Scheduler* custom_schedule image.flags &= ~ImageFlagBits::Dirty; } -vk::Sampler TextureCache::GetSampler(const AmdGpu::Sampler& sampler) { +vk::Sampler TextureCache::GetSampler( + const AmdGpu::Sampler& sampler, + const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base) { const u64 hash = XXH3_64bits(&sampler, sizeof(sampler)); - const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler); + const auto [it, new_sampler] = samplers.try_emplace(hash, instance, sampler, border_color_base); return it->second.Handle(); } diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index b6bf88958..ccfeb36b2 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h @@ -139,7 +139,9 @@ public: void RefreshImage(Image& image, Vulkan::Scheduler* custom_scheduler = nullptr); /// Retrieves the sampler that matches the provided S# descriptor. - [[nodiscard]] vk::Sampler GetSampler(const AmdGpu::Sampler& sampler); + [[nodiscard]] vk::Sampler GetSampler( + const AmdGpu::Sampler& sampler, + const AmdGpu::Liverpool::BorderColorBufferBase& border_color_base); /// Retrieves the image with the specified id. [[nodiscard]] Image& GetImage(ImageId id) { From ac0a82369bc706514b1094bd1ab300bc063f5d13 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sat, 31 May 2025 09:35:52 -0500 Subject: [PATCH 20/31] Revert new GPU map logic (#3019) Something's wrong somewhere, and there's just too many places that somewhere could be for me to debug it right now. --- src/core/memory.cpp | 51 ++++++++++++++------------------------------- 1 file changed, 16 insertions(+), 35 deletions(-) diff --git a/src/core/memory.cpp b/src/core/memory.cpp index ab59219b2..bf9d1cabd 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -262,10 +262,7 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot) void* out_addr = impl.Map(mapped_addr, size, alignment, -1, false); TRACK_ALLOC(out_addr, size, "VMEM"); - if (prot >= MemoryProt::GpuRead) { - // PS4s only map to GPU memory when the protection includes GPU access. - // If the address to map to is too high, PS4s throw a page fault and crash. - ASSERT_MSG(IsValidGpuMapping(mapped_addr, size), "Invalid address for GPU mapping"); + if (IsValidGpuMapping(mapped_addr, size)) { rasterizer->MapMemory(mapped_addr, size); } @@ -345,19 +342,15 @@ s32 MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, u64 size, Memo MergeAdjacent(vma_map, new_vma_handle); } - if (prot >= MemoryProt::GpuRead) { - // PS4s only map to GPU memory when the protection includes GPU access. - // If the address to map to is too high, PS4s throw a page fault and crash. - ASSERT_MSG(IsValidGpuMapping(mapped_addr, size), "Invalid address for GPU mapping"); - rasterizer->MapMemory(mapped_addr, size); - } - if (type == VMAType::Reserved || type == VMAType::PoolReserved) { // For Reserved/PoolReserved mappings, we don't perform any address space allocations. // Just set out_addr to mapped_addr instead. *out_addr = std::bit_cast(mapped_addr); } else { - // Type is either Direct, Flexible, or Code, these need to be mapped in our address space. + // If this is not a reservation, then map to GPU and address space + if (IsValidGpuMapping(mapped_addr, size)) { + rasterizer->MapMemory(mapped_addr, size); + } *out_addr = impl.Map(mapped_addr, size, alignment, phys_addr, is_exec); } @@ -429,7 +422,6 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { const bool is_exec = vma_base.is_exec; const auto start_in_vma = virtual_addr - vma_base_addr; const auto type = vma_base.type; - const auto prot = vma_base.prot; if (type != VMAType::PoolReserved && type != VMAType::Pooled) { LOG_ERROR(Kernel_Vmm, "Attempting to decommit non-pooled memory!"); @@ -437,15 +429,15 @@ s32 MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { } if (type == VMAType::Pooled) { + // We always map PoolCommitted memory to GPU, so unmap when decomitting. + if (IsValidGpuMapping(virtual_addr, size)) { + rasterizer->UnmapMemory(virtual_addr, size); + } + // Track how much pooled memory is decommitted pool_budget += size; } - if (prot >= MemoryProt::GpuRead) { - // If this mapping has GPU access, unmap from GPU. - rasterizer->UnmapMemory(virtual_addr, size); - } - // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, size); auto& vma = new_it->second; @@ -486,15 +478,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma if (type == VMAType::Free) { return adjusted_size; } + if (type == VMAType::Flexible) { flexible_usage -= adjusted_size; } - if (prot >= MemoryProt::GpuRead) { - // If this mapping has GPU access, unmap from GPU. - rasterizer->UnmapMemory(virtual_addr, size); - } - // Mark region as free and attempt to coalesce it with neighbours. const auto new_it = CarveVMA(virtual_addr, adjusted_size); auto& vma = new_it->second; @@ -507,6 +495,11 @@ u64 MemoryManager::UnmapBytesFromEntry(VAddr virtual_addr, VirtualMemoryArea vma auto& post_merge_vma = post_merge_it->second; bool readonly_file = post_merge_vma.prot == MemoryProt::CpuRead && type == VMAType::File; if (type != VMAType::Reserved && type != VMAType::PoolReserved) { + // If this mapping has GPU access, unmap from GPU. + if (IsValidGpuMapping(virtual_addr, size)) { + rasterizer->UnmapMemory(virtual_addr, size); + } + // Unmap the memory region. impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + adjusted_size, phys_base, is_exec, has_backing, readonly_file); @@ -576,18 +569,6 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s return ORBIS_KERNEL_ERROR_EINVAL; } - if (vma_base.prot < MemoryProt::GpuRead && prot >= MemoryProt::GpuRead) { - // New protection will give the GPU access to this VMA, perform a rasterizer map - ASSERT_MSG(IsValidGpuMapping(addr, size), "Invalid address for GPU mapping"); - rasterizer->MapMemory(addr, size); - } - - if (vma_base.prot >= MemoryProt::GpuRead && prot < MemoryProt::GpuRead) { - // New protection will remove the GPU's access to this VMA, perform a rasterizer unmap - ASSERT_MSG(IsValidGpuMapping(addr, size), "Invalid address for GPU unmap"); - rasterizer->UnmapMemory(addr, size); - } - // Change protection vma_base.prot = prot; From 5f6092b8612bfae23aaca51f2938f6f2bd0a66e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=A5IGA?= <164882787+Xphalnos@users.noreply.github.com> Date: Sun, 1 Jun 2025 13:52:09 +0200 Subject: [PATCH 21/31] Qt: Set Minimum Icon Size List to 48 (#3018) --- src/qt_gui/main_window.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 1966aa52b..77b088b78 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -387,9 +387,9 @@ void MainWindow::CreateConnects() { connect(ui->sizeSlider, &QSlider::valueChanged, this, [this](int value) { if (isTableList) { m_game_list_frame->icon_size = - 36 + value; // 36 is the minimum icon size to use due to text disappearing. - m_game_list_frame->ResizeIcons(36 + value); - Config::setIconSize(36 + value); + 48 + value; // 48 is the minimum icon size to use due to text disappearing. + m_game_list_frame->ResizeIcons(48 + value); + Config::setIconSize(48 + value); Config::setSliderPosition(value); } else { m_game_grid_frame->icon_size = 69 + value; From a085a6792897ff5ac4f8eca6156e5a1bfbb04d1e Mon Sep 17 00:00:00 2001 From: WujekFoliarz <72314465+WujekFoliarz@users.noreply.github.com> Date: Sun, 1 Jun 2025 18:13:02 +0200 Subject: [PATCH 22/31] Fix touchpad handling and change gyro calculation (#3006) * Change touchpad handling and orientation calculation * remove unnecessary includes in pad.cpp * remove the cmake command arguments * remove the weird file * try to fix formatting * limit new gyro and touchpad logic to controller 1 * remove cout * fix formatting and add the handle check to scePadRead * swap y and z back --- src/core/libraries/pad/pad.cpp | 137 +++++++++++++++++++++++++++++--- src/input/controller.cpp | 141 ++++++++++++++++++++------------- src/input/controller.h | 22 +++++ 3 files changed, 232 insertions(+), 68 deletions(-) diff --git a/src/core/libraries/pad/pad.cpp b/src/core/libraries/pad/pad.cpp index 5dfc68e90..42582783b 100644 --- a/src/core/libraries/pad/pad.cpp +++ b/src/core/libraries/pad/pad.cpp @@ -316,22 +316,79 @@ int PS4_SYSV_ABI scePadRead(s32 handle, OrbisPadData* pData, s32 num) { pData[i].angularVelocity.y = states[i].angularVelocity.y; pData[i].angularVelocity.z = states[i].angularVelocity.z; pData[i].orientation = {0.0f, 0.0f, 0.0f, 1.0f}; - if (engine) { + pData[i].acceleration.x = states[i].acceleration.x * 0.098; + pData[i].acceleration.y = states[i].acceleration.y * 0.098; + pData[i].acceleration.z = states[i].acceleration.z * 0.098; + pData[i].angularVelocity.x = states[i].angularVelocity.x; + pData[i].angularVelocity.y = states[i].angularVelocity.y; + pData[i].angularVelocity.z = states[i].angularVelocity.z; + + if (engine && handle == 1) { const auto gyro_poll_rate = engine->GetAccelPollRate(); if (gyro_poll_rate != 0.0f) { - GameController::CalculateOrientation(pData[i].acceleration, - pData[i].angularVelocity, - 1.0f / gyro_poll_rate, pData[i].orientation); + auto now = std::chrono::steady_clock::now(); + float deltaTime = std::chrono::duration_cast( + now - controller->GetLastUpdate()) + .count() / + 1000000.0f; + controller->SetLastUpdate(now); + Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation(); + Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f}; + GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, + deltaTime, lastOrientation, outputOrientation); + pData[i].orientation = outputOrientation; + controller->SetLastOrientation(outputOrientation); } } + pData[i].touchData.touchNum = (states[i].touchpad[0].state ? 1 : 0) + (states[i].touchpad[1].state ? 1 : 0); + + if (handle == 1) { + if (controller->GetTouchCount() >= 127) { + controller->SetTouchCount(0); + } + + if (controller->GetSecondaryTouchCount() >= 127) { + controller->SetSecondaryTouchCount(0); + } + + if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) { + controller->SetTouchCount(controller->GetTouchCount() + 1); + controller->SetSecondaryTouchCount(controller->GetTouchCount()); + } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) { + controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1); + } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) { + if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) { + controller->SetTouchCount(controller->GetSecondaryTouchCount()); + } else { + if (controller->WasSecondaryTouchReset()) { + controller->SetTouchCount(controller->GetSecondaryTouchCount()); + controller->UnsetSecondaryTouchResetBool(); + } + } + } + + controller->SetPreviousTouchNum(pData->touchData.touchNum); + + if (pData->touchData.touchNum == 1) { + states[i].touchpad[0].ID = controller->GetTouchCount(); + states[i].touchpad[1].ID = 0; + } else if (pData->touchData.touchNum == 2) { + states[i].touchpad[0].ID = controller->GetTouchCount(); + states[i].touchpad[1].ID = controller->GetSecondaryTouchCount(); + } + } else { + states[i].touchpad[0].ID = 1; + states[i].touchpad[1].ID = 2; + } + pData[i].touchData.touch[0].x = states[i].touchpad[0].x; pData[i].touchData.touch[0].y = states[i].touchpad[0].y; - pData[i].touchData.touch[0].id = 1; + pData[i].touchData.touch[0].id = states[i].touchpad[0].ID; pData[i].touchData.touch[1].x = states[i].touchpad[1].x; pData[i].touchData.touch[1].y = states[i].touchpad[1].y; - pData[i].touchData.touch[1].id = 2; + pData[i].touchData.touch[1].id = states[i].touchpad[1].ID; pData[i].connected = connected; pData[i].timestamp = states[i].time; pData[i].connectedCount = connected_count; @@ -376,31 +433,85 @@ int PS4_SYSV_ABI scePadReadState(s32 handle, OrbisPadData* pData) { pData->leftStick.x = state.axes[static_cast(Input::Axis::LeftX)]; pData->leftStick.y = state.axes[static_cast(Input::Axis::LeftY)]; pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)]; + pData->rightStick.x = state.axes[static_cast(Input::Axis::RightX)]; pData->rightStick.y = state.axes[static_cast(Input::Axis::RightY)]; pData->analogButtons.l2 = state.axes[static_cast(Input::Axis::TriggerLeft)]; pData->analogButtons.r2 = state.axes[static_cast(Input::Axis::TriggerRight)]; - pData->acceleration.x = state.acceleration.x; - pData->acceleration.y = state.acceleration.y; - pData->acceleration.z = state.acceleration.z; + pData->acceleration.x = state.acceleration.x * 0.098; + pData->acceleration.y = state.acceleration.y * 0.098; + pData->acceleration.z = state.acceleration.z * 0.098; pData->angularVelocity.x = state.angularVelocity.x; pData->angularVelocity.y = state.angularVelocity.y; pData->angularVelocity.z = state.angularVelocity.z; pData->orientation = {0.0f, 0.0f, 0.0f, 1.0f}; - if (engine) { + + // Only do this on handle 1 for now + if (engine && handle == 1) { const auto gyro_poll_rate = engine->GetAccelPollRate(); if (gyro_poll_rate != 0.0f) { + auto now = std::chrono::steady_clock::now(); + float deltaTime = std::chrono::duration_cast( + now - controller->GetLastUpdate()) + .count() / + 1000000.0f; + controller->SetLastUpdate(now); + Libraries::Pad::OrbisFQuaternion lastOrientation = controller->GetLastOrientation(); + Libraries::Pad::OrbisFQuaternion outputOrientation = {0.0f, 0.0f, 0.0f, 1.0f}; GameController::CalculateOrientation(pData->acceleration, pData->angularVelocity, - 1.0f / gyro_poll_rate, pData->orientation); + deltaTime, lastOrientation, outputOrientation); + pData->orientation = outputOrientation; + controller->SetLastOrientation(outputOrientation); } } pData->touchData.touchNum = (state.touchpad[0].state ? 1 : 0) + (state.touchpad[1].state ? 1 : 0); + + // Only do this on handle 1 for now + if (handle == 1) { + if (controller->GetTouchCount() >= 127) { + controller->SetTouchCount(0); + } + + if (controller->GetSecondaryTouchCount() >= 127) { + controller->SetSecondaryTouchCount(0); + } + + if (pData->touchData.touchNum == 1 && controller->GetPreviousTouchNum() == 0) { + controller->SetTouchCount(controller->GetTouchCount() + 1); + controller->SetSecondaryTouchCount(controller->GetTouchCount()); + } else if (pData->touchData.touchNum == 2 && controller->GetPreviousTouchNum() == 1) { + controller->SetSecondaryTouchCount(controller->GetSecondaryTouchCount() + 1); + } else if (pData->touchData.touchNum == 0 && controller->GetPreviousTouchNum() > 0) { + if (controller->GetTouchCount() < controller->GetSecondaryTouchCount()) { + controller->SetTouchCount(controller->GetSecondaryTouchCount()); + } else { + if (controller->WasSecondaryTouchReset()) { + controller->SetTouchCount(controller->GetSecondaryTouchCount()); + controller->UnsetSecondaryTouchResetBool(); + } + } + } + + controller->SetPreviousTouchNum(pData->touchData.touchNum); + + if (pData->touchData.touchNum == 1) { + state.touchpad[0].ID = controller->GetTouchCount(); + state.touchpad[1].ID = 0; + } else if (pData->touchData.touchNum == 2) { + state.touchpad[0].ID = controller->GetTouchCount(); + state.touchpad[1].ID = controller->GetSecondaryTouchCount(); + } + } else { + state.touchpad[0].ID = 1; + state.touchpad[1].ID = 2; + } + pData->touchData.touch[0].x = state.touchpad[0].x; pData->touchData.touch[0].y = state.touchpad[0].y; - pData->touchData.touch[0].id = 1; + pData->touchData.touch[0].id = state.touchpad[0].ID; pData->touchData.touch[1].x = state.touchpad[1].x; pData->touchData.touch[1].y = state.touchpad[1].y; - pData->touchData.touch[1].id = 2; + pData->touchData.touch[1].id = state.touchpad[1].ID; pData->timestamp = state.time; pData->connected = true; // isConnected; //TODO fix me proper pData->connectedCount = 1; // connectedCount; diff --git a/src/input/controller.cpp b/src/input/controller.cpp index bb8db9a7c..42cabb837 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include @@ -165,69 +165,37 @@ void GameController::Acceleration(int id, const float acceleration[3]) { AddState(state); } -// Stolen from -// https://github.com/xioTechnologies/Open-Source-AHRS-With-x-IMU/blob/master/x-IMU%20IMU%20and%20AHRS%20Algorithms/x-IMU%20IMU%20and%20AHRS%20Algorithms/AHRS/MahonyAHRS.cs -float eInt[3] = {0.0f, 0.0f, 0.0f}; // Integral error terms -const float Kp = 50.0f; // Proportional gain -const float Ki = 1.0f; // Integral gain -Libraries::Pad::OrbisFQuaternion o = {1, 0, 0, 0}; void GameController::CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, + Libraries::Pad::OrbisFQuaternion& lastOrientation, Libraries::Pad::OrbisFQuaternion& orientation) { - float ax = acceleration.x, ay = acceleration.y, az = acceleration.z; - float gx = angularVelocity.x, gy = angularVelocity.y, gz = angularVelocity.z; + Libraries::Pad::OrbisFQuaternion q = lastOrientation; + Libraries::Pad::OrbisFQuaternion ω = {angularVelocity.x, angularVelocity.y, angularVelocity.z, + 0.0f}; - float q1 = o.w, q2 = o.x, q3 = o.y, q4 = o.z; + Libraries::Pad::OrbisFQuaternion qω = {q.w * ω.x + q.x * ω.w + q.y * ω.z - q.z * ω.y, + q.w * ω.y + q.y * ω.w + q.z * ω.x - q.x * ω.z, + q.w * ω.z + q.z * ω.w + q.x * ω.y - q.y * ω.x, + q.w * ω.w - q.x * ω.x - q.y * ω.y - q.z * ω.z}; - // Normalize accelerometer measurement - float norm = std::sqrt(ax * ax + ay * ay + az * az); - if (norm == 0.0f || deltaTime == 0.0f) - return; // Handle NaN - norm = 1.0f / norm; - ax *= norm; - ay *= norm; - az *= norm; + Libraries::Pad::OrbisFQuaternion qDot = {0.5f * qω.x, 0.5f * qω.y, 0.5f * qω.z, 0.5f * qω.w}; - // Estimated direction of gravity - float vx = 2.0f * (q2 * q4 - q1 * q3); - float vy = 2.0f * (q1 * q2 + q3 * q4); - float vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4; + q.x += qDot.x * deltaTime; + q.y += qDot.y * deltaTime; + q.z += qDot.z * deltaTime; + q.w += qDot.w * deltaTime; - // Error is cross product between estimated direction and measured direction of gravity - float ex = (ay * vz - az * vy); - float ey = (az * vx - ax * vz); - float ez = (ax * vy - ay * vx); - if (Ki > 0.0f) { - eInt[0] += ex * deltaTime; // Accumulate integral error - eInt[1] += ey * deltaTime; - eInt[2] += ez * deltaTime; - } else { - eInt[0] = eInt[1] = eInt[2] = 0.0f; // Prevent integral wind-up - } + float norm = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w); + q.x /= norm; + q.y /= norm; + q.z /= norm; + q.w /= norm; - // Apply feedback terms - gx += Kp * ex + Ki * eInt[0]; - gy += Kp * ey + Ki * eInt[1]; - gz += Kp * ez + Ki * eInt[2]; - - //// Integrate rate of change of quaternion - q1 += (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * deltaTime); - q2 += (q1 * gx + q3 * gz - q4 * gy) * (0.5f * deltaTime); - q3 += (q1 * gy - q2 * gz + q4 * gx) * (0.5f * deltaTime); - q4 += (q1 * gz + q2 * gy - q3 * gx) * (0.5f * deltaTime); - - // Normalize quaternion - norm = std::sqrt(q1 * q1 + q2 * q2 + q3 * q3 + q4 * q4); - norm = 1.0f / norm; - orientation.w = q1 * norm; - orientation.x = q2 * norm; - orientation.y = q3 * norm; - orientation.z = q4 * norm; - o.w = q1 * norm; - o.x = q2 * norm; - o.y = q3 * norm; - o.z = q4 * norm; + orientation.x = q.x; + orientation.y = q.y; + orientation.z = q.z; + orientation.w = q.w; LOG_DEBUG(Lib_Pad, "Calculated orientation: {:.2f} {:.2f} {:.2f} {:.2f}", orientation.x, orientation.y, orientation.z, orientation.w); } @@ -260,6 +228,69 @@ void GameController::SetTouchpadState(int touchIndex, bool touchDown, float x, f } } +u8 GameController::GetTouchCount() { + std::scoped_lock lock{m_mutex}; + return m_touch_count; +} + +void GameController::SetTouchCount(u8 touchCount) { + std::scoped_lock lock{m_mutex}; + m_touch_count = touchCount; +} + +u8 GameController::GetSecondaryTouchCount() { + std::scoped_lock lock{m_mutex}; + return m_secondary_touch_count; +} + +void GameController::SetSecondaryTouchCount(u8 touchCount) { + std::scoped_lock lock{m_mutex}; + m_secondary_touch_count = touchCount; + if (touchCount == 0) { + m_was_secondary_reset = true; + } +} + +u8 GameController::GetPreviousTouchNum() { + std::scoped_lock lock{m_mutex}; + return m_previous_touchnum; +} + +void GameController::SetPreviousTouchNum(u8 touchNum) { + std::scoped_lock lock{m_mutex}; + m_previous_touchnum = touchNum; +} + +bool GameController::WasSecondaryTouchReset() { + std::scoped_lock lock{m_mutex}; + return m_was_secondary_reset; +} + +void GameController::UnsetSecondaryTouchResetBool() { + std::scoped_lock lock{m_mutex}; + m_was_secondary_reset = false; +} + +void GameController::SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation) { + std::scoped_lock lock{m_mutex}; + m_orientation = orientation; +} + +Libraries::Pad::OrbisFQuaternion GameController::GetLastOrientation() { + std::scoped_lock lock{m_mutex}; + return m_orientation; +} + +std::chrono::steady_clock::time_point GameController::GetLastUpdate() { + std::scoped_lock lock{m_mutex}; + return m_last_update; +} + +void GameController::SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate) { + std::scoped_lock lock{m_mutex}; + m_last_update = lastUpdate; +} + void GameController::SetEngine(std::unique_ptr engine) { std::scoped_lock _{m_mutex}; m_engine = std::move(engine); diff --git a/src/input/controller.h b/src/input/controller.h index bbaed75ea..f427a55ec 100644 --- a/src/input/controller.h +++ b/src/input/controller.h @@ -23,6 +23,7 @@ enum class Axis { }; struct TouchpadEntry { + u8 ID = 0; bool state{}; u16 x{}; u16 y{}; @@ -82,9 +83,23 @@ public: Engine* GetEngine(); u32 Poll(); + u8 GetTouchCount(); + void SetTouchCount(u8 touchCount); + u8 GetSecondaryTouchCount(); + void SetSecondaryTouchCount(u8 touchCount); + u8 GetPreviousTouchNum(); + void SetPreviousTouchNum(u8 touchNum); + bool WasSecondaryTouchReset(); + void UnsetSecondaryTouchResetBool(); + + void SetLastOrientation(Libraries::Pad::OrbisFQuaternion& orientation); + Libraries::Pad::OrbisFQuaternion GetLastOrientation(); + std::chrono::steady_clock::time_point GetLastUpdate(); + void SetLastUpdate(std::chrono::steady_clock::time_point lastUpdate); static void CalculateOrientation(Libraries::Pad::OrbisFVector3& acceleration, Libraries::Pad::OrbisFVector3& angularVelocity, float deltaTime, + Libraries::Pad::OrbisFQuaternion& lastOrientation, Libraries::Pad::OrbisFQuaternion& orientation); private: @@ -98,8 +113,15 @@ private: int m_connected_count = 0; u32 m_states_num = 0; u32 m_first_state = 0; + u8 m_touch_count = 0; + u8 m_secondary_touch_count = 0; + u8 m_previous_touch_count = 0; + u8 m_previous_touchnum = 0; + bool m_was_secondary_reset = false; std::array m_states; std::array m_private; + std::chrono::steady_clock::time_point m_last_update = {}; + Libraries::Pad::OrbisFQuaternion m_orientation = {0.0f, 0.0f, 0.0f, 1.0f}; std::unique_ptr m_engine = nullptr; }; From 6f2b81c9b9f20b349d3099f1e368e2ba3370394e Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Sun, 1 Jun 2025 11:30:03 -0500 Subject: [PATCH 23/31] Libs: libSceVoice stubs (#3022) * voice lib stubs Primarily for GTA V * Clang --- CMakeLists.txt | 2 + src/common/logging/filter.cpp | 1 + src/common/logging/types.h | 1 + src/core/libraries/libs.cpp | 2 + src/core/libraries/voice/voice.cpp | 203 +++++++++++++++++++++++++++++ src/core/libraries/voice/voice.h | 56 ++++++++ 6 files changed, 265 insertions(+) create mode 100644 src/core/libraries/voice/voice.cpp create mode 100644 src/core/libraries/voice/voice.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f85d22b6c..20d33ac95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,6 +296,8 @@ set(AJM_LIB src/core/libraries/ajm/ajm.cpp set(AUDIO_LIB src/core/libraries/audio/audioin.cpp src/core/libraries/audio/audioin.h + src/core/libraries/voice/voice.cpp + src/core/libraries/voice/voice.h src/core/libraries/audio/audioout.cpp src/core/libraries/audio/audioout.h src/core/libraries/audio/audioout_backend.h diff --git a/src/common/logging/filter.cpp b/src/common/logging/filter.cpp index fd0614e1b..05935fbdc 100644 --- a/src/common/logging/filter.cpp +++ b/src/common/logging/filter.cpp @@ -141,6 +141,7 @@ bool ParseFilterRule(Filter& instance, Iterator begin, Iterator end) { SUB(Lib, Camera) \ SUB(Lib, CompanionHttpd) \ SUB(Lib, CompanionUtil) \ + SUB(Lib, Voice) \ CLS(Frontend) \ CLS(Render) \ SUB(Render, Vulkan) \ diff --git a/src/common/logging/types.h b/src/common/logging/types.h index bbfd7455b..1da84b219 100644 --- a/src/common/logging/types.h +++ b/src/common/logging/types.h @@ -98,6 +98,7 @@ enum class Class : u8 { Lib_Fiber, ///< The LibSceFiber implementation. Lib_Vdec2, ///< The LibSceVideodec2 implementation. Lib_Videodec, ///< The LibSceVideodec implementation. + Lib_Voice, ///< The LibSceVoice implementation. Lib_RazorCpu, ///< The LibRazorCpu implementation. Lib_Mouse, ///< The LibSceMouse implementation Lib_WebBrowserDialog, ///< The LibSceWebBrowserDialog implementation diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 45b32846f..762c1e762 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -60,6 +60,7 @@ #include "core/libraries/videodec/videodec.h" #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" +#include "core/libraries/voice/voice.h" #include "core/libraries/web_browser_dialog/webbrowserdialog.h" #include "core/libraries/zlib/zlib_sce.h" #include "fiber/fiber.h" @@ -128,6 +129,7 @@ void InitHLELibs(Core::Loader::SymbolsResolver* sym) { Libraries::Camera::RegisterlibSceCamera(sym); Libraries::CompanionHttpd::RegisterlibSceCompanionHttpd(sym); Libraries::CompanionUtil::RegisterlibSceCompanionUtil(sym); + Libraries::Voice::RegisterlibSceVoice(sym); } } // namespace Libraries diff --git a/src/core/libraries/voice/voice.cpp b/src/core/libraries/voice/voice.cpp new file mode 100644 index 000000000..caa16431a --- /dev/null +++ b/src/core/libraries/voice/voice.cpp @@ -0,0 +1,203 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "common/logging/log.h" +#include "core/libraries/error_codes.h" +#include "core/libraries/libs.h" +#include "core/libraries/voice/voice.h" + +namespace Libraries::Voice { + +s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceCreatePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceDeletePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceEnd() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate) { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + *bitrate = 48000; + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetMuteFlag() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetPortAttr() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info) { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + info->port_type = 0; + info->state = 3; + info->byte_count = 0; + info->frame_size = 1; + info->edge_count = 0; + info->reserved = 0; + + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetResourceInfo() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceGetVolume() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceInit() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceInitHQ() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoicePausePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoicePausePortAll() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceReadFromOPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceResetPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceResumePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceResumePortAll() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetBitRate() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetMuteFlag() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetThreadsParams() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceSetVolume() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceStart() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceStop() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceUpdatePort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceVADAdjustment() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceVADSetVersion() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +s32 PS4_SYSV_ABI sceVoiceWriteToIPort() { + LOG_ERROR(Lib_Voice, "(STUBBED) called"); + return ORBIS_OK; +} + +void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym) { + LIB_FUNCTION("oV9GAdJ23Gw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceConnectIPortToOPort); + LIB_FUNCTION("nXpje5yNpaE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceCreatePort); + LIB_FUNCTION("b7kJI+nx2hg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceDeletePort); + LIB_FUNCTION("ajVj3QG2um4", "libSceVoice", 1, "libSceVoice", 0, 0, + sceVoiceDisconnectIPortFromOPort); + LIB_FUNCTION("Oo0S5PH7FIQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceEnd); + LIB_FUNCTION("cJLufzou6bc", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetBitRate); + LIB_FUNCTION("Pc4z1QjForU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetMuteFlag); + LIB_FUNCTION("elcxZTEfHZM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortAttr); + LIB_FUNCTION("CrLqDwWLoXM", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetPortInfo); + LIB_FUNCTION("Z6QV6j7igvE", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetResourceInfo); + LIB_FUNCTION("jjkCjneOYSs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceGetVolume); + LIB_FUNCTION("9TrhuGzberQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInit); + LIB_FUNCTION("IPHvnM5+g04", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceInitHQ); + LIB_FUNCTION("x0slGBQW+wY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePort); + LIB_FUNCTION("Dinob0yMRl8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoicePausePortAll); + LIB_FUNCTION("cQ6DGsQEjV4", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceReadFromOPort); + LIB_FUNCTION("udAxvCePkUs", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResetPort); + LIB_FUNCTION("gAgN+HkiEzY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePort); + LIB_FUNCTION("jbkJFmOZ9U0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceResumePortAll); + LIB_FUNCTION("TexwmOHQsDg", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetBitRate); + LIB_FUNCTION("gwUynkEgNFY", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlag); + LIB_FUNCTION("oUha0S-Ij9Q", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetMuteFlagAll); + LIB_FUNCTION("clyKUyi3RYU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetThreadsParams); + LIB_FUNCTION("QBFoAIjJoXQ", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceSetVolume); + LIB_FUNCTION("54phPH2LZls", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStart); + LIB_FUNCTION("Ao2YNSA7-Qo", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceStop); + LIB_FUNCTION("jSZNP7xJrcw", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceUpdatePort); + LIB_FUNCTION("hg9T73LlRiU", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADAdjustment); + LIB_FUNCTION("wFeAxEeEi-8", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceVADSetVersion); + LIB_FUNCTION("YeJl6yDlhW0", "libSceVoice", 1, "libSceVoice", 0, 0, sceVoiceWriteToIPort); +}; + +} // namespace Libraries::Voice \ No newline at end of file diff --git a/src/core/libraries/voice/voice.h b/src/core/libraries/voice/voice.h new file mode 100644 index 000000000..8f008f2cc --- /dev/null +++ b/src/core/libraries/voice/voice.h @@ -0,0 +1,56 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} + +namespace Libraries::Voice { + +struct OrbisVoicePortInfo { + s32 port_type; + s32 state; + u32* edge; + u32 byte_count; + u32 frame_size; + u16 edge_count; + u16 reserved; +}; + +s32 PS4_SYSV_ABI sceVoiceConnectIPortToOPort(); +s32 PS4_SYSV_ABI sceVoiceCreatePort(); +s32 PS4_SYSV_ABI sceVoiceDeletePort(); +s32 PS4_SYSV_ABI sceVoiceDisconnectIPortFromOPort(); +s32 PS4_SYSV_ABI sceVoiceEnd(); +s32 PS4_SYSV_ABI sceVoiceGetBitRate(u32 port_id, u32* bitrate); +s32 PS4_SYSV_ABI sceVoiceGetMuteFlag(); +s32 PS4_SYSV_ABI sceVoiceGetPortAttr(); +s32 PS4_SYSV_ABI sceVoiceGetPortInfo(u32 port_id, OrbisVoicePortInfo* info); +s32 PS4_SYSV_ABI sceVoiceGetResourceInfo(); +s32 PS4_SYSV_ABI sceVoiceGetVolume(); +s32 PS4_SYSV_ABI sceVoiceInit(); +s32 PS4_SYSV_ABI sceVoiceInitHQ(); +s32 PS4_SYSV_ABI sceVoicePausePort(); +s32 PS4_SYSV_ABI sceVoicePausePortAll(); +s32 PS4_SYSV_ABI sceVoiceReadFromOPort(); +s32 PS4_SYSV_ABI sceVoiceResetPort(); +s32 PS4_SYSV_ABI sceVoiceResumePort(); +s32 PS4_SYSV_ABI sceVoiceResumePortAll(); +s32 PS4_SYSV_ABI sceVoiceSetBitRate(); +s32 PS4_SYSV_ABI sceVoiceSetMuteFlag(); +s32 PS4_SYSV_ABI sceVoiceSetMuteFlagAll(); +s32 PS4_SYSV_ABI sceVoiceSetThreadsParams(); +s32 PS4_SYSV_ABI sceVoiceSetVolume(); +s32 PS4_SYSV_ABI sceVoiceStart(); +s32 PS4_SYSV_ABI sceVoiceStop(); +s32 PS4_SYSV_ABI sceVoiceUpdatePort(); +s32 PS4_SYSV_ABI sceVoiceVADAdjustment(); +s32 PS4_SYSV_ABI sceVoiceVADSetVersion(); +s32 PS4_SYSV_ABI sceVoiceWriteToIPort(); + +void RegisterlibSceVoice(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Voice \ No newline at end of file From 62c87a64c97fae732ae1c4f8150dcb798ccd9345 Mon Sep 17 00:00:00 2001 From: kalaposfos13 <153381648+kalaposfos13@users.noreply.github.com> Date: Mon, 2 Jun 2025 18:02:37 +0200 Subject: [PATCH 24/31] Fix SSH remote links (#3025) --- src/common/scm_rev.cpp.in | 23 +++++++++++++++++++++++ src/common/scm_rev.h | 4 ++++ src/emulator.cpp | 10 +--------- src/qt_gui/main_window.cpp | 10 +--------- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/common/scm_rev.cpp.in b/src/common/scm_rev.cpp.in index 71c4c2d0a..0b113eb31 100644 --- a/src/common/scm_rev.cpp.in +++ b/src/common/scm_rev.cpp.in @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "common/scm_rev.h" namespace Common { @@ -15,5 +17,26 @@ constexpr char g_scm_remote_name[] = "@GIT_REMOTE_NAME@"; constexpr char g_scm_remote_url[] = "@GIT_REMOTE_URL@"; constexpr char g_scm_date[] = "@BUILD_DATE@"; +const std::string GetRemoteNameFromLink() { + std::string remote_url(Common::g_scm_remote_url); + std::string remote_host; + try { + if (remote_url.starts_with("http")) { + if (*remote_url.rbegin() == '/') { + remote_url.pop_back(); + } + remote_host = remote_url.substr(19, remote_url.rfind('/') - 19); + } else if (remote_url.starts_with("git@")) { + auto after_comma_pos = remote_url.find(':') + 1, slash_pos = remote_url.find('/'); + remote_host = remote_url.substr(after_comma_pos, slash_pos - after_comma_pos); + } else { + remote_host = "unknown"; + } + } catch (...) { + remote_host = "unknown"; + } + return remote_host; +} + } // namespace diff --git a/src/common/scm_rev.h b/src/common/scm_rev.h index 36b844e94..2f6d770bb 100644 --- a/src/common/scm_rev.h +++ b/src/common/scm_rev.h @@ -3,6 +3,8 @@ #pragma once +#include + namespace Common { extern const char g_version[]; @@ -15,4 +17,6 @@ extern const char g_scm_remote_name[]; extern const char g_scm_remote_url[]; extern const char g_scm_date[]; +const std::string GetRemoteNameFromLink(); + } // namespace Common diff --git a/src/emulator.cpp b/src/emulator.cpp index 2ad8446ab..4fcb0cfc2 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -194,15 +194,7 @@ void Emulator::Run(const std::filesystem::path& file, const std::vector", id, title, app_version); std::string window_title = ""; std::string remote_url(Common::g_scm_remote_url); - std::string remote_host; - try { - if (*remote_url.rbegin() == '/') { - remote_url.pop_back(); - } - remote_host = remote_url.substr(19, remote_url.rfind('/') - 19); - } catch (...) { - remote_host = "unknown"; - } + std::string remote_host = Common::GetRemoteNameFromLink(); if (Common::g_is_release) { if (remote_host == "shadps4-emu" || remote_url.length() == 0) { window_title = fmt::format("shadPS4 v{} | {}", Common::g_version, game_title); diff --git a/src/qt_gui/main_window.cpp b/src/qt_gui/main_window.cpp index 77b088b78..0f6773751 100644 --- a/src/qt_gui/main_window.cpp +++ b/src/qt_gui/main_window.cpp @@ -56,15 +56,7 @@ bool MainWindow::Init() { setMinimumSize(720, 405); std::string window_title = ""; std::string remote_url(Common::g_scm_remote_url); - std::string remote_host; - try { - if (*remote_url.rbegin() == '/') { - remote_url.pop_back(); - } - remote_host = remote_url.substr(19, remote_url.rfind('/') - 19); - } catch (...) { - remote_host = "unknown"; - } + std::string remote_host = Common::GetRemoteNameFromLink(); if (Common::g_is_release) { if (remote_host == "shadps4-emu" || remote_url.length() == 0) { window_title = fmt::format("shadPS4 v{}", Common::g_version); From a8fd5ba5c7a86fdfbfb7f36108d8428784801497 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Tue, 3 Jun 2025 00:02:51 -0500 Subject: [PATCH 25/31] Emulate libSceGnmDriver's init behavior (#3024) This time, perform it after the LoadSharedLibraries call, which places it after the various init memory mappings GFD engine titles perform. --- src/core/linker.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/core/linker.cpp b/src/core/linker.cpp index 3e6d8c22e..c50b03a8f 100644 --- a/src/core/linker.cpp +++ b/src/core/linker.cpp @@ -117,6 +117,18 @@ void Linker::Execute(const std::vector args) { Common::SetCurrentThreadName("GAME_MainThread"); LoadSharedLibraries(); + // Simulate libSceGnmDriver initialization, which maps a chunk of direct memory. + // Some games fail without accurately emulating this behavior. + s64 phys_addr{}; + s32 result = Libraries::Kernel::sceKernelAllocateDirectMemory( + 0, Libraries::Kernel::sceKernelGetDirectMemorySize(), 0x10000, 0x10000, 3, &phys_addr); + if (result == 0) { + void* addr{reinterpret_cast(0xfe0000000)}; + result = Libraries::Kernel::sceKernelMapNamedDirectMemory( + &addr, 0x10000, 0x13, 0, phys_addr, 0x10000, "SceGnmDriver"); + } + ASSERT_MSG(result == 0, "Unable to emulate libSceGnmDriver initialization"); + // Start main module. EntryParams params{}; params.argc = 1; From 91de8b574db2753697bf1c263b51dd5f7071ea42 Mon Sep 17 00:00:00 2001 From: mailwl Date: Tue, 3 Jun 2025 09:11:18 +0300 Subject: [PATCH 26/31] Network Play: set user signed in (#2944) * Network Play: set user signed in * get signedIn status from config --------- Co-authored-by: georgemoralis --- src/common/config.cpp | 14 +++++++- src/common/config.h | 4 ++- src/core/libraries/np_manager/np_manager.cpp | 36 ++++++++++++++------ src/core/libraries/np_manager/np_manager.h | 9 ++++- 4 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/common/config.cpp b/src/common/config.cpp index 6bccd0f37..6565ab82a 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -69,7 +69,7 @@ static bool vkGuestMarkers = false; static bool rdocEnable = false; static bool isFpsColor = true; static bool isSeparateLogFilesEnabled = false; -static s16 cursorState = HideCursorState::Idle; +static int cursorState = HideCursorState::Idle; static int cursorHideTimeout = 5; // 5 seconds (default) static double trophyNotificationDuration = 6.0; static bool useUnifiedInputConfig = true; @@ -78,6 +78,7 @@ static int controllerCustomColorRGB[3] = {0, 0, 255}; static bool compatibilityData = false; static bool checkCompatibilityOnStartup = false; static std::string trophyKey; +static bool isPSNSignedIn = false; // Gui static bool load_game_size = true; @@ -730,6 +731,14 @@ void setShowBackgroundImage(bool show) { showBackgroundImage = show; } +bool getPSNSignedIn() { + return isPSNSignedIn; +} + +void setPSNSignedIn(bool sign) { + isPSNSignedIn = sign; +} + void load(const std::filesystem::path& path) { // If the configuration file does not exist, create it and return std::error_code error; @@ -754,6 +763,7 @@ void load(const std::filesystem::path& path) { isNeo = toml::find_or(general, "isPS4Pro", false); isDevKit = toml::find_or(general, "isDevKit", false); + isPSNSignedIn = toml::find_or(general, "isPSNSignedIn", false); playBGM = toml::find_or(general, "playBGM", false); isTrophyPopupDisabled = toml::find_or(general, "isTrophyPopupDisabled", false); trophyNotificationDuration = @@ -953,6 +963,7 @@ void save(const std::filesystem::path& path) { data["General"]["isPS4Pro"] = isNeo; data["General"]["isDevKit"] = isDevKit; + data["General"]["isPSNSignedIn"] = isPSNSignedIn; data["General"]["isTrophyPopupDisabled"] = isTrophyPopupDisabled; data["General"]["trophyNotificationDuration"] = trophyNotificationDuration; data["General"]["playBGM"] = playBGM; @@ -1098,6 +1109,7 @@ void setDefaultValues() { isHDRAllowed = false; isNeo = false; isDevKit = false; + isPSNSignedIn = false; isFullscreen = false; isTrophyPopupDisabled = false; playBGM = false; diff --git a/src/common/config.h b/src/common/config.h index aba23621c..404854ae2 100644 --- a/src/common/config.h +++ b/src/common/config.h @@ -14,7 +14,7 @@ struct GameInstallDir { bool enabled; }; -enum HideCursorState : s16 { Never, Idle, Always }; +enum HideCursorState : int { Never, Idle, Always }; void load(const std::filesystem::path& path); void save(const std::filesystem::path& path); @@ -39,6 +39,7 @@ bool getCompatibilityEnabled(); bool getCheckCompatibilityOnStartup(); int getBackgroundImageOpacity(); bool getShowBackgroundImage(); +bool getPSNSignedIn(); std::string getLogFilter(); std::string getLogType(); @@ -111,6 +112,7 @@ void setCompatibilityEnabled(bool use); void setCheckCompatibilityOnStartup(bool use); void setBackgroundImageOpacity(int opacity); void setShowBackgroundImage(bool show); +void setPSNSignedIn(bool sign); void setCursorState(s16 cursorState); void setCursorHideTimeout(int newcursorHideTimeout); diff --git a/src/core/libraries/np_manager/np_manager.cpp b/src/core/libraries/np_manager/np_manager.cpp index a60dcd86f..bc920b5a9 100644 --- a/src/core/libraries/np_manager/np_manager.cpp +++ b/src/core/libraries/np_manager/np_manager.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include "common/config.h" #include "common/logging/log.h" #include "core/libraries/error_codes.h" #include "core/libraries/libs.h" @@ -10,6 +11,8 @@ namespace Libraries::NpManager { +#define SIGNEDIN_STATUS (Config::getPSNSignedIn() ? ORBIS_OK : ORBIS_NP_ERROR_SIGNED_OUT) + int PS4_SYSV_ABI Func_EF4378573542A508() { LOG_ERROR(Lib_NpManager, "(STUBBED) called"); return ORBIS_OK; @@ -921,9 +924,16 @@ int PS4_SYSV_ABI sceNpGetAccountCountry() { return ORBIS_OK; } -int PS4_SYSV_ABI sceNpGetAccountCountryA() { - LOG_ERROR(Lib_NpManager, "(STUBBED) called"); - return ORBIS_OK; +int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id, + OrbisNpCountryCode* country_code) { + LOG_INFO(Lib_NpManager, "(STUBBED) called, user_id = {}", user_id); + if (country_code == nullptr) { + return ORBIS_NP_ERROR_INVALID_ARGUMENT; + } + ::memset(country_code, 0, sizeof(OrbisNpCountryCode)); + // TODO: get NP country code from config + ::memcpy(country_code->country_code, "us", 2); + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetAccountDateOfBirth() { @@ -941,8 +951,8 @@ int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id) if (online_id == nullptr || account_id == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - *account_id = 0; - return ORBIS_NP_ERROR_SIGNED_OUT; + *account_id = 0xFEEDFACE; + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account_id) { @@ -950,8 +960,8 @@ int PS4_SYSV_ABI sceNpGetAccountIdA(OrbisUserServiceUserId user_id, u64* account if (account_id == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - *account_id = 0; - return ORBIS_NP_ERROR_SIGNED_OUT; + *account_id = 0xFEEDFACE; + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetAccountLanguage() { @@ -984,7 +994,9 @@ int PS4_SYSV_ABI sceNpGetNpId(OrbisUserServiceUserId user_id, OrbisNpId* np_id) if (np_id == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - return ORBIS_NP_ERROR_SIGNED_OUT; + memset(np_id, 0, sizeof(OrbisNpId)); + strncpy(np_id->handle.data, Config::getUserName().c_str(), sizeof(np_id->handle.data)); + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetNpReachabilityState() { @@ -997,7 +1009,9 @@ int PS4_SYSV_ABI sceNpGetOnlineId(OrbisUserServiceUserId user_id, OrbisNpOnlineI if (online_id == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - return ORBIS_NP_ERROR_SIGNED_OUT; + memset(online_id, 0, sizeof(OrbisNpOnlineId)); + strncpy(online_id->data, Config::getUserName().c_str(), sizeof(online_id->data)); + return SIGNEDIN_STATUS; } int PS4_SYSV_ABI sceNpGetParentalControlInfo() { @@ -1014,8 +1028,8 @@ int PS4_SYSV_ABI sceNpGetState(OrbisUserServiceUserId user_id, OrbisNpState* sta if (state == nullptr) { return ORBIS_NP_ERROR_INVALID_ARGUMENT; } - *state = OrbisNpState::SignedOut; - LOG_DEBUG(Lib_NpManager, "Signed out"); + *state = Config::getPSNSignedIn() ? OrbisNpState::SignedIn : OrbisNpState::SignedOut; + LOG_DEBUG(Lib_NpManager, "Signed {}", Config::getPSNSignedIn() ? "in" : "out"); return ORBIS_OK; } diff --git a/src/core/libraries/np_manager/np_manager.h b/src/core/libraries/np_manager/np_manager.h index 02a1a32f6..1078a9f3e 100644 --- a/src/core/libraries/np_manager/np_manager.h +++ b/src/core/libraries/np_manager/np_manager.h @@ -32,6 +32,12 @@ struct OrbisNpId { u8 reserved[8]; }; +struct OrbisNpCountryCode { + char country_code[2]; + char end; + char pad; +}; + int PS4_SYSV_ABI Func_EF4378573542A508(); int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromKernel(); int PS4_SYSV_ABI _sceNpIpcCreateMemoryFromPool(); @@ -215,7 +221,8 @@ int PS4_SYSV_ABI sceNpCreateRequest(); int PS4_SYSV_ABI sceNpDeleteRequest(int reqId); int PS4_SYSV_ABI sceNpGetAccountAge(); int PS4_SYSV_ABI sceNpGetAccountCountry(); -int PS4_SYSV_ABI sceNpGetAccountCountryA(); +int PS4_SYSV_ABI sceNpGetAccountCountryA(OrbisUserServiceUserId user_id, + OrbisNpCountryCode* country_code); int PS4_SYSV_ABI sceNpGetAccountDateOfBirth(); int PS4_SYSV_ABI sceNpGetAccountDateOfBirthA(); int PS4_SYSV_ABI sceNpGetAccountId(OrbisNpOnlineId* online_id, u64* account_id); From 58b417e98ea1e782fe5785eef2d5824737dc262d Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Tue, 3 Jun 2025 01:29:25 -0500 Subject: [PATCH 27/31] Core: Protect fixes (#3029) * Swap do-while to while If we use a do-while loop, we waste time if `aligned_size = 0`. This is also still accurate to FreeBSD behavior, where it returns success if `start == end` during mprotect. This also effectively prevents the memory assert seen in updated versions of RESIDENT EVIL 2 (CUSA09193) * Move prot validation outside loop The prot variable shouldn't change during a mprotect call, so we can check the flags before protecting instead. Also cleans up the code for prot validation. This should improve performance, and is more accurate to FreeBSD code. * Add logging for protect calls This will help in debugging future problems --- src/core/libraries/kernel/memory.cpp | 4 ++++ src/core/memory.cpp | 32 +++++++++++++++------------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/core/libraries/kernel/memory.cpp b/src/core/libraries/kernel/memory.cpp index ce694dc1e..18676cbdf 100644 --- a/src/core/libraries/kernel/memory.cpp +++ b/src/core/libraries/kernel/memory.cpp @@ -264,6 +264,8 @@ int PS4_SYSV_ABI sceKernelQueryMemoryProtection(void* addr, void** start, void** } s32 PS4_SYSV_ABI sceKernelMprotect(const void* addr, u64 size, s32 prot) { + LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size, + prot); Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryProt protection_flags = static_cast(prot); return memory_manager->Protect(std::bit_cast(addr), size, protection_flags); @@ -279,6 +281,8 @@ s32 PS4_SYSV_ABI posix_mprotect(const void* addr, u64 size, s32 prot) { } s32 PS4_SYSV_ABI sceKernelMtypeprotect(const void* addr, u64 size, s32 mtype, s32 prot) { + LOG_INFO(Kernel_Vmm, "called addr = {}, size = {:#x}, prot = {:#x}", fmt::ptr(addr), size, + prot); Core::MemoryManager* memory_manager = Core::Memory::Instance(); Core::MemoryProt protection_flags = static_cast(prot); return memory_manager->Protect(std::bit_cast(addr), size, protection_flags); diff --git a/src/core/memory.cpp b/src/core/memory.cpp index bf9d1cabd..8550ece17 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -557,18 +557,6 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s return adjusted_size; } - // Validate protection flags - constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead | - MemoryProt::CpuReadWrite | MemoryProt::GpuRead | - MemoryProt::GpuWrite | MemoryProt::GpuReadWrite; - - MemoryProt invalid_flags = prot & ~valid_flags; - if (u32(invalid_flags) != 0 && u32(invalid_flags) != u32(MemoryProt::NoAccess)) { - LOG_ERROR(Kernel_Vmm, "Invalid protection flags: prot = {:#x}, invalid flags = {:#x}", - u32(prot), u32(invalid_flags)); - return ORBIS_KERNEL_ERROR_EINVAL; - } - // Change protection vma_base.prot = prot; @@ -598,11 +586,25 @@ s64 MemoryManager::ProtectBytes(VAddr addr, VirtualMemoryArea vma_base, size_t s s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { std::scoped_lock lk{mutex}; - s64 protected_bytes = 0; + // Validate protection flags + constexpr static MemoryProt valid_flags = MemoryProt::NoAccess | MemoryProt::CpuRead | + MemoryProt::CpuReadWrite | MemoryProt::GpuRead | + MemoryProt::GpuWrite | MemoryProt::GpuReadWrite; + + MemoryProt invalid_flags = prot & ~valid_flags; + if (invalid_flags != MemoryProt::NoAccess) { + LOG_ERROR(Kernel_Vmm, "Invalid protection flags"); + return ORBIS_KERNEL_ERROR_EINVAL; + } + + // Align addr and size to the nearest page boundary. auto aligned_addr = Common::AlignDown(addr, 16_KB); auto aligned_size = Common::AlignUp(size + addr - aligned_addr, 16_KB); - do { + + // Protect all VMAs between aligned_addr and aligned_addr + aligned_size. + s64 protected_bytes = 0; + while (protected_bytes < aligned_size) { auto it = FindVMA(aligned_addr + protected_bytes); auto& vma_base = it->second; ASSERT_MSG(vma_base.Contains(addr + protected_bytes, 0), "Address {:#x} is out of bounds", @@ -615,7 +617,7 @@ s32 MemoryManager::Protect(VAddr addr, size_t size, MemoryProt prot) { return result; } protected_bytes += result; - } while (protected_bytes < aligned_size); + } return ORBIS_OK; } From 68ceff3801da583395974b344c4894f12720d0db Mon Sep 17 00:00:00 2001 From: Schweeeeeeeeeeeeeeee <88291316+Schweeeeeeeeeeeeeeee@users.noreply.github.com> Date: Tue, 3 Jun 2025 08:40:31 +0200 Subject: [PATCH 28/31] Add missing dependency for Fedora (#3005) Propablly missing because of fedora 42? --- documents/building-linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documents/building-linux.md b/documents/building-linux.md index cdc8ba12f..bd07b2eff 100644 --- a/documents/building-linux.md +++ b/documents/building-linux.md @@ -29,7 +29,7 @@ sudo dnf install clang git cmake libatomic alsa-lib-devel \ openssl-devel libevdev-devel libudev-devel libXext-devel \ qt6-qtbase-devel qt6-qtbase-private-devel \ qt6-qtmultimedia-devel qt6-qtsvg-devel qt6-qttools-devel \ - vulkan-devel vulkan-validation-layers libpng-devel + vulkan-devel vulkan-validation-layers libpng-devel libuuid-devel ``` #### Arch Linux From 9794700ef680390ef3ae033a467d2d59bb8e0a6e Mon Sep 17 00:00:00 2001 From: Fire Cube Date: Tue, 3 Jun 2025 12:34:29 +0200 Subject: [PATCH 29/31] Add support for game folder and fail early if eboot.bin is missing or corrupt (#3027) --- src/emulator.cpp | 19 +++++++++++++++++-- src/emulator.h | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/emulator.cpp b/src/emulator.cpp index 4fcb0cfc2..bb50b8686 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include #include #include @@ -62,8 +63,13 @@ Emulator::~Emulator() { Config::saveMainWindow(config_dir / "config.toml"); } -void Emulator::Run(const std::filesystem::path& file, const std::vector args) { +void Emulator::Run(std::filesystem::path file, const std::vector args) { + if (std::filesystem::is_directory(file)) { + file /= "eboot.bin"; + } + const auto eboot_name = file.filename().string(); + auto game_folder = file.parent_path(); if (const auto game_folder_name = game_folder.filename().string(); game_folder_name.ends_with("-UPDATE") || game_folder_name.ends_with("-patch")) { @@ -114,6 +120,11 @@ void Emulator::Run(const std::filesystem::path& file, const std::vectorGetHostPath("/app0/" + eboot_name); - linker->LoadModule(eboot_path); + if (linker->LoadModule(eboot_path) == -1) { + LOG_CRITICAL(Loader, "Failed to load game's eboot.bin: {}", + std::filesystem::absolute(eboot_path).string()); + std::quick_exit(0); + } // check if we have system modules to load LoadSystemModules(game_info.game_serial); diff --git a/src/emulator.h b/src/emulator.h index 08c2807a1..257ccd694 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -25,7 +25,7 @@ public: Emulator(); ~Emulator(); - void Run(const std::filesystem::path& file, const std::vector args = {}); + void Run(std::filesystem::path file, const std::vector args = {}); void UpdatePlayTime(const std::string& serial); private: From 997d04ce8b2c19e6a4bb240eeceab5e982a090c6 Mon Sep 17 00:00:00 2001 From: Stephen Miller <56742918+StevenMiller123@users.noreply.github.com> Date: Tue, 3 Jun 2025 05:43:56 -0500 Subject: [PATCH 30/31] Core: Pthread affinity fixups (#3021) * posix_pthread_attr_getschedparam * Fixes for scePthreadGetAffinity and scePthreadSetAffinity Looking at FreeBSD source, and our other pthread functions, we should be using our FindThread function to get the appropriate thread if thread != g_curthread. --- src/core/libraries/kernel/threads/pthread.cpp | 31 ++++++++++++++++--- .../libraries/kernel/threads/pthread_attr.cpp | 2 ++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/core/libraries/kernel/threads/pthread.cpp b/src/core/libraries/kernel/threads/pthread.cpp index 61310bfb5..59b427d22 100644 --- a/src/core/libraries/kernel/threads/pthread.cpp +++ b/src/core/libraries/kernel/threads/pthread.cpp @@ -576,8 +576,19 @@ int PS4_SYSV_ABI posix_pthread_getaffinity_np(PthreadT thread, size_t cpusetsize if (thread == nullptr || cpusetp == nullptr) { return POSIX_EINVAL; } + + auto* thread_state = ThrState::Instance(); + if (thread == g_curthread) { + g_curthread->lock.lock(); + } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) { + return ret; + } + auto* attr_ptr = &thread->attr; - return posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp); + auto ret = posix_pthread_attr_getaffinity_np(&attr_ptr, cpusetsize, cpusetp); + + thread->lock.unlock(); + return ret; } int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize, @@ -585,11 +596,23 @@ int PS4_SYSV_ABI posix_pthread_setaffinity_np(PthreadT thread, size_t cpusetsize if (thread == nullptr || cpusetp == nullptr) { return POSIX_EINVAL; } - auto* attr_ptr = &thread->attr; - if (const auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp)) { + + auto* thread_state = ThrState::Instance(); + if (thread == g_curthread) { + g_curthread->lock.lock(); + } else if (auto ret = thread_state->FindThread(thread, /*include dead*/ 0); ret != 0) { return ret; } - return thread->SetAffinity(thread->attr.cpuset); + + auto* attr_ptr = &thread->attr; + auto ret = posix_pthread_attr_setaffinity_np(&attr_ptr, cpusetsize, cpusetp); + + if (ret == ORBIS_OK) { + ret = thread->SetAffinity(thread->attr.cpuset); + } + + thread->lock.unlock(); + return ret; } int PS4_SYSV_ABI scePthreadGetaffinity(PthreadT thread, u64* mask) { diff --git a/src/core/libraries/kernel/threads/pthread_attr.cpp b/src/core/libraries/kernel/threads/pthread_attr.cpp index 71f6438a6..e098b00a4 100644 --- a/src/core/libraries/kernel/threads/pthread_attr.cpp +++ b/src/core/libraries/kernel/threads/pthread_attr.cpp @@ -306,6 +306,8 @@ void RegisterThreadAttr(Core::Loader::SymbolsResolver* sym) { posix_pthread_attr_getdetachstate); LIB_FUNCTION("JKyG3SWyA10", "libScePosix", 1, "libkernel", 1, 1, posix_pthread_attr_setguardsize); + LIB_FUNCTION("qlk9pSLsUmM", "libScePosix", 1, "libkernel", 1, 1, + posix_pthread_attr_getschedparam); // Orbis LIB_FUNCTION("4+h9EzwKF4I", "libkernel", 1, "libkernel", 1, 1, From da38e0c2f3a26fba3fd40093cb5af2146b38b55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczyk?= Date: Tue, 3 Jun 2025 19:39:21 +0100 Subject: [PATCH 31/31] Move conditional rendering calls closer to actual draws --- .../renderer_vulkan/vk_rasterizer.cpp | 21 ++++++++++++------- .../renderer_vulkan/vk_rasterizer.h | 2 +- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp index d3a292d0b..5765950b5 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp +++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp @@ -320,6 +320,9 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); + if (active_predication) { + cmdbuf.beginConditionalRenderingEXT(&*active_predication); + } if (is_indexed) { cmdbuf.drawIndexed(regs.num_indices, regs.num_instances.NumInstances(), 0, s32(vertex_offset), instance_offset); @@ -327,7 +330,9 @@ void Rasterizer::Draw(bool is_indexed, u32 index_offset) { cmdbuf.draw(regs.num_indices, regs.num_instances.NumInstances(), vertex_offset, instance_offset); } - + if (active_predication) { + cmdbuf.endConditionalRenderingEXT(); + } ResetBindings(); } @@ -372,6 +377,9 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 const auto cmdbuf = scheduler.CommandBuffer(); cmdbuf.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline->Handle()); + if (active_predication) { + cmdbuf.beginConditionalRenderingEXT(&*active_predication); + } if (is_indexed) { ASSERT(sizeof(VkDrawIndexedIndirectCommand) == stride); @@ -391,7 +399,9 @@ void Rasterizer::DrawIndirect(bool is_indexed, VAddr arg_address, u32 offset, u3 cmdbuf.drawIndirect(buffer->Handle(), base, max_count, stride); } } - + if (active_predication) { + cmdbuf.endConditionalRenderingEXT(); + } ResetBindings(); } @@ -1334,9 +1344,8 @@ void Rasterizer::StartPredication(VAddr addr, bool draw_if_visible, bool wait_fo .flags = draw_if_visible ? vk::ConditionalRenderingFlagBitsEXT::eInverted : vk::ConditionalRenderingFlagsEXT(), }; - cmdbuf.beginConditionalRenderingEXT(&conditional_rendering_info); - active_predication = true; + active_predication = conditional_rendering_info; } void Rasterizer::EndPredication() { @@ -1347,10 +1356,8 @@ void Rasterizer::EndPredication() { LOG_DEBUG(Render_Vulkan, ""); scheduler.EndRendering(); - const auto cmdbuf = scheduler.CommandBuffer(); - cmdbuf.endConditionalRenderingEXT(); ScopeMarkerEnd(); - active_predication = false; + active_predication = std::nullopt; } void Rasterizer::StartOcclusionQuery(VAddr addr) { diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h index 9883a606f..b0de5e903 100644 --- a/src/video_core/renderer_vulkan/vk_rasterizer.h +++ b/src/video_core/renderer_vulkan/vk_rasterizer.h @@ -131,7 +131,7 @@ private: u32 occlusion_current_index{}; std::map occlusion_index_mapping; VideoCore::Buffer occlusion_query_buffer; - bool active_predication; + std::optional active_predication; boost::container::static_vector< std::pair, 8>