Merge pull request #4869 from bunnei/improve-gpu-sync
Improvements to GPU synchronization & various refactoring
This commit is contained in:
		| @@ -15,8 +15,9 @@ | ||||
|  | ||||
| namespace Service::Nvidia::Devices { | ||||
|  | ||||
| nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface) | ||||
|     : nvdevice(system), events_interface{events_interface} {} | ||||
| nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface, | ||||
|                          SyncpointManager& syncpoint_manager) | ||||
|     : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {} | ||||
| nvhost_ctrl::~nvhost_ctrl() = default; | ||||
|  | ||||
| u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, | ||||
| @@ -70,19 +71,33 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& | ||||
|         return NvResult::BadParameter; | ||||
|     } | ||||
|  | ||||
|     if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { | ||||
|         params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id); | ||||
|         std::memcpy(output.data(), ¶ms, sizeof(params)); | ||||
|         return NvResult::Success; | ||||
|     } | ||||
|  | ||||
|     if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id); | ||||
|         syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) { | ||||
|         params.value = new_value; | ||||
|         std::memcpy(output.data(), ¶ms, sizeof(params)); | ||||
|         return NvResult::Success; | ||||
|     } | ||||
|  | ||||
|     auto event = events_interface.events[event_id]; | ||||
|     auto& gpu = system.GPU(); | ||||
|  | ||||
|     // This is mostly to take into account unimplemented features. As synced | ||||
|     // gpu is always synced. | ||||
|     if (!gpu.IsAsync()) { | ||||
|         event.writable->Signal(); | ||||
|         event.event.writable->Signal(); | ||||
|         return NvResult::Success; | ||||
|     } | ||||
|     auto lock = gpu.LockSync(); | ||||
|     const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id); | ||||
|     const u32 current_syncpoint_value = event.fence.value; | ||||
|     const s32 diff = current_syncpoint_value - params.threshold; | ||||
|     if (diff >= 0) { | ||||
|         event.writable->Signal(); | ||||
|         event.event.writable->Signal(); | ||||
|         params.value = current_syncpoint_value; | ||||
|         std::memcpy(output.data(), ¶ms, sizeof(params)); | ||||
|         return NvResult::Success; | ||||
| @@ -109,7 +124,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& | ||||
|             params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; | ||||
|         } | ||||
|         params.value |= event_id; | ||||
|         event.writable->Clear(); | ||||
|         event.event.writable->Clear(); | ||||
|         gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); | ||||
|         if (!is_async && ctrl.fresh_call) { | ||||
|             ctrl.must_delay = true; | ||||
| @@ -157,15 +172,19 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto | ||||
| u32 nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     IocCtrlEventSignalParams params{}; | ||||
|     std::memcpy(¶ms, input.data(), sizeof(params)); | ||||
|  | ||||
|     u32 event_id = params.event_id & 0x00FF; | ||||
|     LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id); | ||||
|  | ||||
|     if (event_id >= MaxNvEvents) { | ||||
|         return NvResult::BadParameter; | ||||
|     } | ||||
|     if (events_interface.status[event_id] == EventState::Waiting) { | ||||
|         events_interface.LiberateEvent(event_id); | ||||
|         events_interface.events[event_id].writable->Signal(); | ||||
|     } | ||||
|  | ||||
|     syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id); | ||||
|  | ||||
|     return NvResult::Success; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -14,7 +14,8 @@ namespace Service::Nvidia::Devices { | ||||
|  | ||||
| class nvhost_ctrl final : public nvdevice { | ||||
| public: | ||||
|     explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); | ||||
|     explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface, | ||||
|                          SyncpointManager& syncpoint_manager); | ||||
|     ~nvhost_ctrl() override; | ||||
|  | ||||
|     u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, | ||||
| @@ -145,6 +146,7 @@ private: | ||||
|     u32 IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output); | ||||
|  | ||||
|     EventInterface& events_interface; | ||||
|     SyncpointManager& syncpoint_manager; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::Nvidia::Devices | ||||
|   | ||||
| @@ -7,14 +7,20 @@ | ||||
| #include "common/logging/log.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/service/nvdrv/devices/nvhost_gpu.h" | ||||
| #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||||
| #include "core/memory.h" | ||||
| #include "video_core/gpu.h" | ||||
| #include "video_core/memory_manager.h" | ||||
|  | ||||
| namespace Service::Nvidia::Devices { | ||||
|  | ||||
| nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) | ||||
|     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} | ||||
| nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, | ||||
|                        SyncpointManager& syncpoint_manager) | ||||
|     : nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager{syncpoint_manager} { | ||||
|     channel_fence.id = syncpoint_manager.AllocateSyncpoint(); | ||||
|     channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id); | ||||
| } | ||||
|  | ||||
| nvhost_gpu::~nvhost_gpu() = default; | ||||
|  | ||||
| u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, | ||||
| @@ -126,10 +132,10 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou | ||||
|                 params.num_entries, params.flags, params.unk0, params.unk1, params.unk2, | ||||
|                 params.unk3); | ||||
|  | ||||
|     auto& gpu = system.GPU(); | ||||
|     params.fence_out.id = assigned_syncpoints; | ||||
|     params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints); | ||||
|     assigned_syncpoints++; | ||||
|     channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id); | ||||
|  | ||||
|     params.fence_out = channel_fence; | ||||
|  | ||||
|     std::memcpy(output.data(), ¶ms, output.size()); | ||||
|     return 0; | ||||
| } | ||||
| @@ -145,37 +151,98 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector< | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) { | ||||
|     return { | ||||
|         Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, | ||||
|                                   Tegra::SubmissionMode::Increasing), | ||||
|         {fence.value}, | ||||
|         Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, | ||||
|                                   Tegra::SubmissionMode::Increasing), | ||||
|         Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Acquire, fence.id), | ||||
|     }; | ||||
| } | ||||
|  | ||||
| static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) { | ||||
|     std::vector<Tegra::CommandHeader> result{ | ||||
|         Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1, | ||||
|                                   Tegra::SubmissionMode::Increasing), | ||||
|         {}}; | ||||
|  | ||||
|     for (u32 count = 0; count < add_increment; ++count) { | ||||
|         result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1, | ||||
|                                                       Tegra::SubmissionMode::Increasing)); | ||||
|         result.emplace_back( | ||||
|             Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Increment, fence.id)); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence, | ||||
|                                                                           u32 add_increment) { | ||||
|     std::vector<Tegra::CommandHeader> result{ | ||||
|         Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1, | ||||
|                                   Tegra::SubmissionMode::Increasing), | ||||
|         {}}; | ||||
|     const std::vector<Tegra::CommandHeader> increment{ | ||||
|         BuildIncrementCommandList(fence, add_increment)}; | ||||
|  | ||||
|     result.insert(result.end(), increment.begin(), increment.end()); | ||||
|  | ||||
|     return result; | ||||
| } | ||||
|  | ||||
| u32 nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, | ||||
|                                  Tegra::CommandList&& entries) { | ||||
|     LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, | ||||
|               params.num_entries, params.flags.raw); | ||||
|  | ||||
|     auto& gpu = system.GPU(); | ||||
|  | ||||
|     params.fence_out.id = channel_fence.id; | ||||
|  | ||||
|     if (params.flags.add_wait.Value() && | ||||
|         !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) { | ||||
|         gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)}); | ||||
|     } | ||||
|  | ||||
|     if (params.flags.add_increment.Value() || params.flags.increment.Value()) { | ||||
|         const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0; | ||||
|         params.fence_out.value = syncpoint_manager.IncreaseSyncpoint( | ||||
|             params.fence_out.id, params.AddIncrementValue() + increment_value); | ||||
|     } else { | ||||
|         params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id); | ||||
|     } | ||||
|  | ||||
|     entries.RefreshIntegrityChecks(gpu); | ||||
|     gpu.PushGPUEntries(std::move(entries)); | ||||
|  | ||||
|     if (params.flags.add_increment.Value()) { | ||||
|         if (params.flags.suppress_wfi) { | ||||
|             gpu.PushGPUEntries(Tegra::CommandList{ | ||||
|                 BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())}); | ||||
|         } else { | ||||
|             gpu.PushGPUEntries(Tegra::CommandList{ | ||||
|                 BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())}); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|     if (input.size() < sizeof(IoctlSubmitGpfifo)) { | ||||
|         UNIMPLEMENTED(); | ||||
|     } | ||||
|     IoctlSubmitGpfifo params{}; | ||||
|     std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | ||||
|     LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, | ||||
|               params.num_entries, params.flags.raw); | ||||
|  | ||||
|     ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + | ||||
|                                    params.num_entries * sizeof(Tegra::CommandListHeader), | ||||
|                "Incorrect input size"); | ||||
|  | ||||
|     Tegra::CommandList entries(params.num_entries); | ||||
|     std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], | ||||
|     std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)], | ||||
|                 params.num_entries * sizeof(Tegra::CommandListHeader)); | ||||
|  | ||||
|     UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); | ||||
|     UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); | ||||
|  | ||||
|     auto& gpu = system.GPU(); | ||||
|     u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id); | ||||
|     if (params.flags.increment.Value()) { | ||||
|         params.fence_out.value += current_syncpoint_value; | ||||
|     } else { | ||||
|         params.fence_out.value = current_syncpoint_value; | ||||
|     } | ||||
|     gpu.PushGPUEntries(std::move(entries)); | ||||
|  | ||||
|     std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); | ||||
|     return 0; | ||||
|     return SubmitGPFIFOImpl(params, output, std::move(entries)); | ||||
| } | ||||
|  | ||||
| u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, | ||||
| @@ -185,31 +252,17 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, | ||||
|     } | ||||
|     IoctlSubmitGpfifo params{}; | ||||
|     std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); | ||||
|     LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address, | ||||
|               params.num_entries, params.flags.raw); | ||||
|  | ||||
|     Tegra::CommandList entries(params.num_entries); | ||||
|     if (version == IoctlVersion::Version2) { | ||||
|         std::memcpy(entries.data(), input2.data(), | ||||
|         std::memcpy(entries.command_lists.data(), input2.data(), | ||||
|                     params.num_entries * sizeof(Tegra::CommandListHeader)); | ||||
|     } else { | ||||
|         system.Memory().ReadBlock(params.address, entries.data(), | ||||
|         system.Memory().ReadBlock(params.address, entries.command_lists.data(), | ||||
|                                   params.num_entries * sizeof(Tegra::CommandListHeader)); | ||||
|     } | ||||
|     UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0); | ||||
|     UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0); | ||||
|  | ||||
|     auto& gpu = system.GPU(); | ||||
|     u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id); | ||||
|     if (params.flags.increment.Value()) { | ||||
|         params.fence_out.value += current_syncpoint_value; | ||||
|     } else { | ||||
|         params.fence_out.value = current_syncpoint_value; | ||||
|     } | ||||
|     gpu.PushGPUEntries(std::move(entries)); | ||||
|  | ||||
|     std::memcpy(output.data(), ¶ms, output.size()); | ||||
|     return 0; | ||||
|     return SubmitGPFIFOImpl(params, output, std::move(entries)); | ||||
| } | ||||
|  | ||||
| u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) { | ||||
|   | ||||
| @@ -11,6 +11,11 @@ | ||||
| #include "common/swap.h" | ||||
| #include "core/hle/service/nvdrv/devices/nvdevice.h" | ||||
| #include "core/hle/service/nvdrv/nvdata.h" | ||||
| #include "video_core/dma_pusher.h" | ||||
|  | ||||
| namespace Service::Nvidia { | ||||
| class SyncpointManager; | ||||
| } | ||||
|  | ||||
| namespace Service::Nvidia::Devices { | ||||
|  | ||||
| @@ -21,7 +26,8 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b); | ||||
|  | ||||
| class nvhost_gpu final : public nvdevice { | ||||
| public: | ||||
|     explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); | ||||
|     explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev, | ||||
|                         SyncpointManager& syncpoint_manager); | ||||
|     ~nvhost_gpu() override; | ||||
|  | ||||
|     u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2, | ||||
| @@ -162,10 +168,15 @@ private: | ||||
|             u32_le raw; | ||||
|             BitField<0, 1, u32_le> add_wait;      // append a wait sync_point to the list | ||||
|             BitField<1, 1, u32_le> add_increment; // append an increment to the list | ||||
|             BitField<2, 1, u32_le> new_hw_format; // Mostly ignored | ||||
|             BitField<2, 1, u32_le> new_hw_format; // mostly ignored | ||||
|             BitField<4, 1, u32_le> suppress_wfi;  // suppress wait for interrupt | ||||
|             BitField<8, 1, u32_le> increment;     // increment the returned fence | ||||
|         } flags; | ||||
|         Fence fence_out; // returned new fence object for others to wait on | ||||
|  | ||||
|         u32 AddIncrementValue() const { | ||||
|             return flags.add_increment.Value() << 1; | ||||
|         } | ||||
|     }; | ||||
|     static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), | ||||
|                   "IoctlSubmitGpfifo is incorrect size"); | ||||
| @@ -190,6 +201,8 @@ private: | ||||
|     u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output); | ||||
|     u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output); | ||||
|     u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output); | ||||
|     u32 SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output, | ||||
|                          Tegra::CommandList&& entries); | ||||
|     u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output); | ||||
|     u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output, | ||||
|                   const std::vector<u8>& input2, IoctlVersion version); | ||||
| @@ -198,7 +211,8 @@ private: | ||||
|     u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output); | ||||
|  | ||||
|     std::shared_ptr<nvmap> nvmap_dev; | ||||
|     u32 assigned_syncpoints{}; | ||||
|     SyncpointManager& syncpoint_manager; | ||||
|     Fence channel_fence; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::Nvidia::Devices | ||||
|   | ||||
| @@ -21,6 +21,7 @@ | ||||
| #include "core/hle/service/nvdrv/interface.h" | ||||
| #include "core/hle/service/nvdrv/nvdrv.h" | ||||
| #include "core/hle/service/nvdrv/nvmemp.h" | ||||
| #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||||
| #include "core/hle/service/nvflinger/nvflinger.h" | ||||
|  | ||||
| namespace Service::Nvidia { | ||||
| @@ -36,21 +37,23 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger | ||||
|     nvflinger.SetNVDrvInstance(module_); | ||||
| } | ||||
|  | ||||
| Module::Module(Core::System& system) { | ||||
| Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} { | ||||
|     auto& kernel = system.Kernel(); | ||||
|     for (u32 i = 0; i < MaxNvEvents; i++) { | ||||
|         std::string event_label = fmt::format("NVDRV::NvEvent_{}", i); | ||||
|         events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label); | ||||
|         events_interface.events[i] = {Kernel::WritableEvent::CreateEventPair(kernel, event_label)}; | ||||
|         events_interface.status[i] = EventState::Free; | ||||
|         events_interface.registered[i] = false; | ||||
|     } | ||||
|     auto nvmap_dev = std::make_shared<Devices::nvmap>(system); | ||||
|     devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev); | ||||
|     devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev); | ||||
|     devices["/dev/nvhost-gpu"] = | ||||
|         std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager); | ||||
|     devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system); | ||||
|     devices["/dev/nvmap"] = nvmap_dev; | ||||
|     devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev); | ||||
|     devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface); | ||||
|     devices["/dev/nvhost-ctrl"] = | ||||
|         std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager); | ||||
|     devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev); | ||||
|     devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); | ||||
|     devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev); | ||||
| @@ -95,17 +98,17 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { | ||||
|         if (events_interface.assigned_syncpt[i] == syncpoint_id && | ||||
|             events_interface.assigned_value[i] == value) { | ||||
|             events_interface.LiberateEvent(i); | ||||
|             events_interface.events[i].writable->Signal(); | ||||
|             events_interface.events[i].event.writable->Signal(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const { | ||||
|     return events_interface.events[event_id].readable; | ||||
|     return events_interface.events[event_id].event.readable; | ||||
| } | ||||
|  | ||||
| std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const { | ||||
|     return events_interface.events[event_id].writable; | ||||
|     return events_interface.events[event_id].event.writable; | ||||
| } | ||||
|  | ||||
| } // namespace Service::Nvidia | ||||
|   | ||||
| @@ -10,6 +10,7 @@ | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/writable_event.h" | ||||
| #include "core/hle/service/nvdrv/nvdata.h" | ||||
| #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||||
| #include "core/hle/service/service.h" | ||||
|  | ||||
| namespace Core { | ||||
| @@ -22,15 +23,23 @@ class NVFlinger; | ||||
|  | ||||
| namespace Service::Nvidia { | ||||
|  | ||||
| class SyncpointManager; | ||||
|  | ||||
| namespace Devices { | ||||
| class nvdevice; | ||||
| } | ||||
|  | ||||
| /// Represents an Nvidia event | ||||
| struct NvEvent { | ||||
|     Kernel::EventPair event; | ||||
|     Fence fence{}; | ||||
| }; | ||||
|  | ||||
| struct EventInterface { | ||||
|     // Mask representing currently busy events | ||||
|     u64 events_mask{}; | ||||
|     // Each kernel event associated to an NV event | ||||
|     std::array<Kernel::EventPair, MaxNvEvents> events; | ||||
|     std::array<NvEvent, MaxNvEvents> events; | ||||
|     // The status of the current NVEvent | ||||
|     std::array<EventState, MaxNvEvents> status{}; | ||||
|     // Tells if an NVEvent is registered or not | ||||
| @@ -119,6 +128,9 @@ public: | ||||
|     std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const; | ||||
|  | ||||
| private: | ||||
|     /// Manages syncpoints on the host | ||||
|     SyncpointManager syncpoint_manager; | ||||
|  | ||||
|     /// Id to use for the next open file descriptor. | ||||
|     u32 next_fd = 1; | ||||
|  | ||||
|   | ||||
							
								
								
									
										39
									
								
								src/core/hle/service/nvdrv/syncpoint_manager.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/core/hle/service/nvdrv/syncpoint_manager.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| // Copyright 2020 yuzu emulator team | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "core/hle/service/nvdrv/syncpoint_manager.h" | ||||
| #include "video_core/gpu.h" | ||||
|  | ||||
| namespace Service::Nvidia { | ||||
|  | ||||
| SyncpointManager::SyncpointManager(Tegra::GPU& gpu) : gpu{gpu} {} | ||||
|  | ||||
| SyncpointManager::~SyncpointManager() = default; | ||||
|  | ||||
| u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) { | ||||
|     syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id); | ||||
|     return GetSyncpointMin(syncpoint_id); | ||||
| } | ||||
|  | ||||
| u32 SyncpointManager::AllocateSyncpoint() { | ||||
|     for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) { | ||||
|         if (!syncpoints[syncpoint_id].is_allocated) { | ||||
|             syncpoints[syncpoint_id].is_allocated = true; | ||||
|             return syncpoint_id; | ||||
|         } | ||||
|     } | ||||
|     UNREACHABLE_MSG("No more available syncpoints!"); | ||||
|     return {}; | ||||
| } | ||||
|  | ||||
| u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) { | ||||
|     for (u32 index = 0; index < value; ++index) { | ||||
|         syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed); | ||||
|     } | ||||
|  | ||||
|     return GetSyncpointMax(syncpoint_id); | ||||
| } | ||||
|  | ||||
| } // namespace Service::Nvidia | ||||
							
								
								
									
										85
									
								
								src/core/hle/service/nvdrv/syncpoint_manager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/core/hle/service/nvdrv/syncpoint_manager.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | ||||
| // Copyright 2020 yuzu emulator team | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <atomic> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/service/nvdrv/nvdata.h" | ||||
|  | ||||
| namespace Tegra { | ||||
| class GPU; | ||||
| } | ||||
|  | ||||
| namespace Service::Nvidia { | ||||
|  | ||||
| class SyncpointManager final { | ||||
| public: | ||||
|     explicit SyncpointManager(Tegra::GPU& gpu); | ||||
|     ~SyncpointManager(); | ||||
|  | ||||
|     /** | ||||
|      * Returns true if the specified syncpoint is expired for the given value. | ||||
|      * @param syncpoint_id Syncpoint ID to check. | ||||
|      * @param value Value to check against the specified syncpoint. | ||||
|      * @returns True if the specified syncpoint is expired for the given value, otherwise False. | ||||
|      */ | ||||
|     bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const { | ||||
|         return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the lower bound for the specified syncpoint. | ||||
|      * @param syncpoint_id Syncpoint ID to get the lower bound for. | ||||
|      * @returns The lower bound for the specified syncpoint. | ||||
|      */ | ||||
|     u32 GetSyncpointMin(u32 syncpoint_id) const { | ||||
|         return syncpoints[syncpoint_id].min.load(std::memory_order_relaxed); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the uper bound for the specified syncpoint. | ||||
|      * @param syncpoint_id Syncpoint ID to get the upper bound for. | ||||
|      * @returns The upper bound for the specified syncpoint. | ||||
|      */ | ||||
|     u32 GetSyncpointMax(u32 syncpoint_id) const { | ||||
|         return syncpoints[syncpoint_id].max.load(std::memory_order_relaxed); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Refreshes the minimum value for the specified syncpoint. | ||||
|      * @param syncpoint_id Syncpoint ID to be refreshed. | ||||
|      * @returns The new syncpoint minimum value. | ||||
|      */ | ||||
|     u32 RefreshSyncpoint(u32 syncpoint_id); | ||||
|  | ||||
|     /** | ||||
|      * Allocates a new syncoint. | ||||
|      * @returns The syncpoint ID for the newly allocated syncpoint. | ||||
|      */ | ||||
|     u32 AllocateSyncpoint(); | ||||
|  | ||||
|     /** | ||||
|      * Increases the maximum value for the specified syncpoint. | ||||
|      * @param syncpoint_id Syncpoint ID to be increased. | ||||
|      * @param value Value to increase the specified syncpoint by. | ||||
|      * @returns The new syncpoint maximum value. | ||||
|      */ | ||||
|     u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value); | ||||
|  | ||||
| private: | ||||
|     struct Syncpoint { | ||||
|         std::atomic<u32> min; | ||||
|         std::atomic<u32> max; | ||||
|         std::atomic<bool> is_allocated; | ||||
|     }; | ||||
|  | ||||
|     std::array<Syncpoint, MaxSyncPoints> syncpoints{}; | ||||
|  | ||||
|     Tegra::GPU& gpu; | ||||
| }; | ||||
|  | ||||
| } // namespace Service::Nvidia | ||||
| @@ -242,6 +242,10 @@ void NVFlinger::Compose() { | ||||
|  | ||||
|         const auto& igbp_buffer = buffer->get().igbp_buffer; | ||||
|  | ||||
|         if (!system.IsPoweredOn()) { | ||||
|             return; // We are likely shutting down | ||||
|         } | ||||
|  | ||||
|         auto& gpu = system.GPU(); | ||||
|         const auto& multi_fence = buffer->get().multi_fence; | ||||
|         guard->unlock(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Chloe
					Chloe