diff options
Diffstat (limited to 'src/core/hle/service/nvdrv')
24 files changed, 480 insertions, 120 deletions
diff --git a/src/core/hle/service/nvdrv/devices/nvdevice.h b/src/core/hle/service/nvdrv/devices/nvdevice.h index 4f6042b00..5b8248433 100644 --- a/src/core/hle/service/nvdrv/devices/nvdevice.h +++ b/src/core/hle/service/nvdrv/devices/nvdevice.h @@ -8,6 +8,11 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nvdrv/nvdata.h" + +namespace Core { +class System; +} namespace Service::Nvidia::Devices { @@ -15,7 +20,7 @@ namespace Service::Nvidia::Devices { /// implement the ioctl interface. class nvdevice { public: - nvdevice() = default; + explicit nvdevice(Core::System& system) : system{system} {}; virtual ~nvdevice() = default; union Ioctl { u32_le raw; @@ -33,7 +38,11 @@ public: * @param output A buffer where the output data will be written to. * @returns The result code of the ioctl. */ - virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) = 0; + virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) = 0; + +protected: + Core::System& system; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp index 20c7c39aa..76494f0b7 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.cpp @@ -13,10 +13,12 @@ namespace Service::Nvidia::Devices { -nvdisp_disp0::nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +nvdisp_disp0::nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) + : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvdisp_disp0 ::~nvdisp_disp0() = default; -u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; } @@ -34,9 +36,8 @@ void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u3 addr, offset, width, height, stride, static_cast<PixelFormat>(format), transform, crop_rect}; - auto& instance = Core::System::GetInstance(); - instance.GetPerfStats().EndGameFrame(); - instance.GPU().SwapBuffers(framebuffer); + system.GetPerfStats().EndGameFrame(); + system.GPU().SwapBuffers(framebuffer); } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h index 12f3ef825..e79e490ff 100644 --- a/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h +++ b/src/core/hle/service/nvdrv/devices/nvdisp_disp0.h @@ -17,10 +17,11 @@ class nvmap; class nvdisp_disp0 final : public nvdevice { public: - explicit nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev); + explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvdisp_disp0() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; /// Performs a screen flip, drawing the buffer pointed to by the handle. void flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, u32 stride, diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp index af62d33d2..24ab3f2e9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp @@ -22,10 +22,12 @@ enum { }; } -nvhost_as_gpu::nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev) + : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {} nvhost_as_gpu::~nvhost_as_gpu() = default; -u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -65,7 +67,7 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& LOG_DEBUG(Service_NVDRV, "called, pages={:X}, page_size={:X}, flags={:X}", params.pages, params.page_size, params.flags); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); const u64 size{static_cast<u64>(params.pages) * static_cast<u64>(params.page_size)}; if (params.flags & 1) { params.offset = gpu.MemoryManager().AllocateSpace(params.offset, size, 1); @@ -85,7 +87,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) std::vector<IoctlRemapEntry> entries(num_entries); std::memcpy(entries.data(), input.data(), input.size()); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); for (const auto& entry : entries) { LOG_WARNING(Service_NVDRV, "remap entry, offset=0x{:X} handle=0x{:X} pages=0x{:X}", entry.offset, entry.nvmap_handle, entry.pages); @@ -136,7 +138,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou // case to prevent unexpected behavior. ASSERT(object->id == params.nvmap_handle); - auto& gpu = Core::System::GetInstance().GPU(); + auto& gpu = system.GPU(); if (params.flags & 1) { params.offset = gpu.MemoryManager().MapBufferEx(object->addr, params.offset, object->size); @@ -173,8 +175,7 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou return 0; } - params.offset = Core::System::GetInstance().GPU().MemoryManager().UnmapBuffer(params.offset, - itr->second.size); + params.offset = system.GPU().MemoryManager().UnmapBuffer(params.offset, itr->second.size); buffer_mappings.erase(itr->second.offset); std::memcpy(output.data(), ¶ms, output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h index eb14b1da8..30ca5f4c3 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h @@ -17,10 +17,11 @@ class nvmap; class nvhost_as_gpu final : public nvdevice { public: - explicit nvhost_as_gpu(std::shared_ptr<nvmap> nvmap_dev); + explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_as_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index b39fb9ef9..9a66a5f88 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -7,14 +7,20 @@ #include "common/assert.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvhost_ctrl.h" +#include "video_core/gpu.h" namespace Service::Nvidia::Devices { -nvhost_ctrl::nvhost_ctrl() = default; +nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface) + : nvdevice(system), events_interface{events_interface} {} nvhost_ctrl::~nvhost_ctrl() = default; -u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -22,11 +28,15 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector< case IoctlCommand::IocGetConfigCommand: return NvOsGetConfigU32(input, output); case IoctlCommand::IocCtrlEventWaitCommand: - return IocCtrlEventWait(input, output, false); + return IocCtrlEventWait(input, output, false, ctrl); case IoctlCommand::IocCtrlEventWaitAsyncCommand: - return IocCtrlEventWait(input, output, true); + return IocCtrlEventWait(input, output, true, ctrl); case IoctlCommand::IocCtrlEventRegisterCommand: return IocCtrlEventRegister(input, output); + case IoctlCommand::IocCtrlEventUnregisterCommand: + return IocCtrlEventUnregister(input, output); + case IoctlCommand::IocCtrlEventSignalCommand: + return IocCtrlEventSignal(input, output); } UNIMPLEMENTED_MSG("Unimplemented ioctl"); return 0; @@ -41,23 +51,137 @@ u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& } u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, - bool is_async) { + bool is_async, IoctlCtrl& ctrl) { IocCtrlEventWaitParams params{}; std::memcpy(¶ms, input.data(), sizeof(params)); - LOG_WARNING(Service_NVDRV, - "(STUBBED) called, syncpt_id={}, threshold={}, timeout={}, is_async={}", - params.syncpt_id, params.threshold, params.timeout, is_async); + LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}", + params.syncpt_id, params.threshold, params.timeout, is_async); - // TODO(Subv): Implement actual syncpt waiting. - params.value = 0; + if (params.syncpt_id >= MaxSyncPoints) { + return NvResult::BadParameter; + } + + auto& gpu = system.GPU(); + // This is mostly to take into account unimplemented features. As synced + // gpu is always synced. + if (!gpu.IsAsync()) { + return NvResult::Success; + } + auto lock = gpu.LockSync(); + const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id); + const s32 diff = current_syncpoint_value - params.threshold; + if (diff >= 0) { + params.value = current_syncpoint_value; + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Success; + } + const u32 target_value = current_syncpoint_value - diff; + + if (!is_async) { + params.value = 0; + } + + if (params.timeout == 0) { + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Timeout; + } + + u32 event_id; + if (is_async) { + event_id = params.value & 0x00FF; + if (event_id >= MaxNvEvents) { + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::BadParameter; + } + } else { + if (ctrl.fresh_call) { + const auto result = events_interface.GetFreeEvent(); + if (result) { + event_id = *result; + } else { + LOG_CRITICAL(Service_NVDRV, "No Free Events available!"); + event_id = params.value & 0x00FF; + } + } else { + event_id = ctrl.event_id; + } + } + + EventState status = events_interface.status[event_id]; + if (event_id < MaxNvEvents || status == EventState::Free || status == EventState::Registered) { + events_interface.SetEventStatus(event_id, EventState::Waiting); + events_interface.assigned_syncpt[event_id] = params.syncpt_id; + events_interface.assigned_value[event_id] = target_value; + if (is_async) { + params.value = params.syncpt_id << 4; + } else { + params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000; + } + params.value |= event_id; + events_interface.events[event_id].writable->Clear(); + gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value); + if (!is_async && ctrl.fresh_call) { + ctrl.must_delay = true; + ctrl.timeout = params.timeout; + ctrl.event_id = event_id; + return NvResult::Timeout; + } + std::memcpy(output.data(), ¶ms, sizeof(params)); + return NvResult::Timeout; + } std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; + return NvResult::BadParameter; } u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) { - LOG_WARNING(Service_NVDRV, "(STUBBED) called"); - // TODO(bunnei): Implement this. - return 0; + IocCtrlEventRegisterParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + const u32 event_id = params.user_event_id & 0x00FF; + LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (events_interface.registered[event_id]) { + return NvResult::BadParameter; + } + events_interface.RegisterEvent(event_id); + return NvResult::Success; +} + +u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventUnregisterParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + const u32 event_id = params.user_event_id & 0x00FF; + LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (!events_interface.registered[event_id]) { + return NvResult::BadParameter; + } + events_interface.UnregisterEvent(event_id); + return NvResult::Success; +} + +u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) { + IocCtrlEventSignalParams params{}; + std::memcpy(¶ms, input.data(), sizeof(params)); + // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization + // It is believed from RE to cancel the GPU Event. However, better research is required + u32 event_id = params.user_event_id & 0x00FF; + LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id); + if (event_id >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (events_interface.status[event_id] == EventState::Waiting) { + auto& gpu = system.GPU(); + if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id], + events_interface.assigned_value[event_id])) { + events_interface.LiberateEvent(event_id); + events_interface.events[event_id].writable->Signal(); + } + } + return NvResult::Success; } } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 6d0de2212..14e6e7e57 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -8,15 +8,17 @@ #include <vector> #include "common/common_types.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +#include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia::Devices { class nvhost_ctrl final : public nvdevice { public: - nvhost_ctrl(); + explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface); ~nvhost_ctrl() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { @@ -132,9 +134,16 @@ private: u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output); - u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async); + u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async, + IoctlCtrl& ctrl); u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output); + + u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output); + + EventInterface& events_interface; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index 0e28755bd..988effd90 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -12,10 +12,11 @@ namespace Service::Nvidia::Devices { -nvhost_ctrl_gpu::nvhost_ctrl_gpu() = default; +nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {} nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; -u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -185,7 +186,7 @@ u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& o IoctlGetGpuTime params{}; std::memcpy(¶ms, input.data(), input.size()); - const auto ns = Core::Timing::CyclesToNs(Core::System::GetInstance().CoreTiming().GetTicks()); + const auto ns = Core::Timing::CyclesToNs(system.CoreTiming().GetTicks()); params.gpu_time = static_cast<u64_le>(ns.count()); std::memcpy(output.data(), ¶ms, output.size()); return 0; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h index 240435eea..2b035ae3f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h @@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices { class nvhost_ctrl_gpu final : public nvdevice { public: - nvhost_ctrl_gpu(); + explicit nvhost_ctrl_gpu(Core::System& system); ~nvhost_ctrl_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index 8ce7bc7a5..241dac881 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -13,10 +13,12 @@ namespace Service::Nvidia::Devices { -nvhost_gpu::nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev) : nvmap_dev(std::move(nvmap_dev)) {} +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() = default; -u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); @@ -119,8 +121,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); - params.fence_out.id = 0; - params.fence_out.value = 0; + auto& gpu = system.GPU(); + params.fence_out.id = assigned_syncpoints; + params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints); + assigned_syncpoints++; std::memcpy(output.data(), ¶ms, output.size()); return 0; } @@ -143,7 +147,7 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", - params.address, params.num_entries, params.flags); + params.address, params.num_entries, params.flags.raw); ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) + params.num_entries * sizeof(Tegra::CommandListHeader), @@ -153,10 +157,18 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)], params.num_entries * sizeof(Tegra::CommandListHeader)); - Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); + 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)); - params.fence_out.id = 0; - params.fence_out.value = 0; std::memcpy(output.data(), ¶ms, sizeof(IoctlSubmitGpfifo)); return 0; } @@ -168,16 +180,24 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output) IoctlSubmitGpfifo params{}; std::memcpy(¶ms, input.data(), sizeof(IoctlSubmitGpfifo)); LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}", - params.address, params.num_entries, params.flags); + params.address, params.num_entries, params.flags.raw); Tegra::CommandList entries(params.num_entries); Memory::ReadBlock(params.address, entries.data(), params.num_entries * sizeof(Tegra::CommandListHeader)); - Core::System::GetInstance().GPU().PushGPUEntries(std::move(entries)); + 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)); - params.fence_out.id = 0; - params.fence_out.value = 0; std::memcpy(output.data(), ¶ms, output.size()); return 0; } diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h index 62beb5c0c..d2e8fbae9 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" +#include "core/hle/service/nvdrv/nvdata.h" namespace Service::Nvidia::Devices { @@ -20,10 +21,11 @@ constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b); class nvhost_gpu final : public nvdevice { public: - explicit nvhost_gpu(std::shared_ptr<nvmap> nvmap_dev); + explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev); ~nvhost_gpu() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { @@ -113,11 +115,7 @@ private: static_assert(sizeof(IoctlGetErrorNotification) == 16, "IoctlGetErrorNotification is incorrect size"); - struct IoctlFence { - u32_le id; - u32_le value; - }; - static_assert(sizeof(IoctlFence) == 8, "IoctlFence is incorrect size"); + static_assert(sizeof(Fence) == 8, "Fence is incorrect size"); struct IoctlAllocGpfifoEx { u32_le num_entries; @@ -132,13 +130,13 @@ private: static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size"); struct IoctlAllocGpfifoEx2 { - u32_le num_entries; // in - u32_le flags; // in - u32_le unk0; // in (1 works) - IoctlFence fence_out; // out - u32_le unk1; // in - u32_le unk2; // in - u32_le unk3; // in + u32_le num_entries; // in + u32_le flags; // in + u32_le unk0; // in (1 works) + Fence fence_out; // out + u32_le unk1; // in + u32_le unk2; // in + u32_le unk3; // in }; static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size"); @@ -153,10 +151,16 @@ private: struct IoctlSubmitGpfifo { u64_le address; // pointer to gpfifo entry structs u32_le num_entries; // number of fence objects being submitted - u32_le flags; - IoctlFence fence_out; // returned new fence object for others to wait on - }; - static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(IoctlFence), + union { + 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<8, 1, u32_le> increment; // increment the returned fence + } flags; + Fence fence_out; // returned new fence object for others to wait on + }; + static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence), "IoctlSubmitGpfifo is incorrect size"); struct IoctlGetWaitbase { @@ -184,6 +188,7 @@ private: u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output); std::shared_ptr<nvmap> nvmap_dev; + u32 assigned_syncpoints{}; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index f5e8ea7c3..f572ad30f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -10,10 +10,11 @@ namespace Service::Nvidia::Devices { -nvhost_nvdec::nvhost_nvdec() = default; +nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {} nvhost_nvdec::~nvhost_nvdec() = default; -u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index 0e7b284f8..2710f0511 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices { class nvhost_nvdec final : public nvdevice { public: - nvhost_nvdec(); + explicit nvhost_nvdec(Core::System& system); ~nvhost_nvdec() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp index 3e0951ab0..38282956f 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp @@ -10,10 +10,11 @@ namespace Service::Nvidia::Devices { -nvhost_nvjpg::nvhost_nvjpg() = default; +nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {} nvhost_nvjpg::~nvhost_nvjpg() = default; -u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h index 89fd5e95e..379766693 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.h @@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices { class nvhost_nvjpg final : public nvdevice { public: - nvhost_nvjpg(); + explicit nvhost_nvjpg(Core::System& system); ~nvhost_nvjpg() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index d544f0f31..70e8091db 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -10,10 +10,11 @@ namespace Service::Nvidia::Devices { -nvhost_vic::nvhost_vic() = default; +nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {} nvhost_vic::~nvhost_vic() = default; -u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}", command.raw, input.size(), output.size()); diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index fc24c3f9c..7d111977e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -13,10 +13,11 @@ namespace Service::Nvidia::Devices { class nvhost_vic final : public nvdevice { public: - nvhost_vic(); + explicit nvhost_vic(Core::System& system); ~nvhost_vic() override; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; private: enum class IoctlCommand : u32_le { diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp index 1ec796fc6..223b496b7 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.cpp +++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp @@ -18,7 +18,7 @@ enum { }; } -nvmap::nvmap() = default; +nvmap::nvmap(Core::System& system) : nvdevice(system) {} nvmap::~nvmap() = default; VAddr nvmap::GetObjectAddress(u32 handle) const { @@ -28,7 +28,8 @@ VAddr nvmap::GetObjectAddress(u32 handle) const { return object->addr; } -u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { switch (static_cast<IoctlCommand>(command.raw)) { case IoctlCommand::Create: return IocCreate(input, output); diff --git a/src/core/hle/service/nvdrv/devices/nvmap.h b/src/core/hle/service/nvdrv/devices/nvmap.h index 396230c19..bf4a101c2 100644 --- a/src/core/hle/service/nvdrv/devices/nvmap.h +++ b/src/core/hle/service/nvdrv/devices/nvmap.h @@ -16,13 +16,14 @@ namespace Service::Nvidia::Devices { class nvmap final : public nvdevice { public: - nvmap(); + explicit nvmap(Core::System& system); ~nvmap() override; /// Returns the allocated address of an nvmap object given its handle. VAddr GetObjectAddress(u32 handle) const; - u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override; + u32 ioctl(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) override; /// Represents an nvmap object. struct Object { diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp index b60fc748b..d5be64ed2 100644 --- a/src/core/hle/service/nvdrv/interface.cpp +++ b/src/core/hle/service/nvdrv/interface.cpp @@ -8,12 +8,18 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/thread.h" #include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/interface.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/nvdrv/nvdrv.h" namespace Service::Nvidia { +void NVDRV::SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value) { + nvdrv->SignalSyncpt(syncpoint_id, value); +} + void NVDRV::Open(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_NVDRV, "called"); @@ -36,11 +42,31 @@ void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) { std::vector<u8> output(ctx.GetWriteBufferSize()); + IoctlCtrl ctrl{}; + + u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output, ctrl); + + if (ctrl.must_delay) { + ctrl.fresh_call = false; + ctx.SleepClientThread( + "NVServices::DelayedResponse", ctrl.timeout, + [=](Kernel::SharedPtr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx, + Kernel::ThreadWakeupReason reason) { + IoctlCtrl ctrl2{ctrl}; + std::vector<u8> output2 = output; + u32 result = nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output2, ctrl2); + ctx.WriteBuffer(output2); + IPC::ResponseBuilder rb{ctx, 3}; + rb.Push(RESULT_SUCCESS); + rb.Push(result); + }, + nvdrv->GetEventWriteable(ctrl.event_id)); + } else { + ctx.WriteBuffer(output); + } IPC::ResponseBuilder rb{ctx, 3}; rb.Push(RESULT_SUCCESS); - rb.Push(nvdrv->Ioctl(fd, command, ctx.ReadBuffer(), output)); - - ctx.WriteBuffer(output); + rb.Push(result); } void NVDRV::Close(Kernel::HLERequestContext& ctx) { @@ -66,13 +92,19 @@ void NVDRV::Initialize(Kernel::HLERequestContext& ctx) { void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) { IPC::RequestParser rp{ctx}; u32 fd = rp.Pop<u32>(); - u32 event_id = rp.Pop<u32>(); + // TODO(Blinkhawk): Figure the meaning of the flag at bit 16 + u32 event_id = rp.Pop<u32>() & 0x000000FF; LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id); IPC::ResponseBuilder rb{ctx, 3, 1}; rb.Push(RESULT_SUCCESS); - rb.PushCopyObjects(query_event.readable); - rb.Push<u32>(0); + if (event_id < MaxNvEvents) { + rb.PushCopyObjects(nvdrv->GetEvent(event_id)); + rb.Push<u32>(NvResult::Success); + } else { + rb.Push<u32>(0); + rb.Push<u32>(NvResult::BadParameter); + } } void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) { @@ -127,10 +159,6 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name) {13, &NVDRV::FinishInitialize, "FinishInitialize"}, }; RegisterHandlers(functions); - - auto& kernel = Core::System::GetInstance().Kernel(); - query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Automatic, - "NVDRV::query_event"); } NVDRV::~NVDRV() = default; diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h index 5b4889910..10a0ecd52 100644 --- a/src/core/hle/service/nvdrv/interface.h +++ b/src/core/hle/service/nvdrv/interface.h @@ -19,6 +19,8 @@ public: NVDRV(std::shared_ptr<Module> nvdrv, const char* name); ~NVDRV() override; + void SignalGPUInterruptSyncpt(const u32 syncpoint_id, const u32 value); + private: void Open(Kernel::HLERequestContext& ctx); void Ioctl(Kernel::HLERequestContext& ctx); @@ -33,8 +35,6 @@ private: std::shared_ptr<Module> nvdrv; u64 pid{}; - - Kernel::EventPair query_event; }; } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdata.h b/src/core/hle/service/nvdrv/nvdata.h new file mode 100644 index 000000000..ac03cbc23 --- /dev/null +++ b/src/core/hle/service/nvdrv/nvdata.h @@ -0,0 +1,48 @@ +#pragma once + +#include <array> +#include "common/common_types.h" + +namespace Service::Nvidia { + +constexpr u32 MaxSyncPoints = 192; +constexpr u32 MaxNvEvents = 64; + +struct Fence { + s32 id; + u32 value; +}; + +static_assert(sizeof(Fence) == 8, "Fence has wrong size"); + +struct MultiFence { + u32 num_fences; + std::array<Fence, 4> fences; +}; + +enum NvResult : u32 { + Success = 0, + BadParameter = 4, + Timeout = 5, + ResourceError = 15, +}; + +enum class EventState { + Free = 0, + Registered = 1, + Waiting = 2, + Busy = 3, +}; + +struct IoctlCtrl { + // First call done to the servioce for services that call itself again after a call. + bool fresh_call{true}; + // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep + bool must_delay{}; + // Timeout for the delay + s64 timeout{}; + // NV Event Id + s32 event_id{-1}; +}; + +} // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index 6e4b8f2c6..2011a226a 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -4,7 +4,10 @@ #include <utility> +#include <fmt/format.h> #include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/readable_event.h" +#include "core/hle/kernel/writable_event.h" #include "core/hle/service/nvdrv/devices/nvdevice.h" #include "core/hle/service/nvdrv/devices/nvdisp_disp0.h" #include "core/hle/service/nvdrv/devices/nvhost_as_gpu.h" @@ -22,8 +25,9 @@ namespace Service::Nvidia { -void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger) { - auto module_ = std::make_shared<Module>(); +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, + Core::System& system) { + auto module_ = std::make_shared<Module>(system); std::make_shared<NVDRV>(module_, "nvdrv")->InstallAsService(service_manager); std::make_shared<NVDRV>(module_, "nvdrv:a")->InstallAsService(service_manager); std::make_shared<NVDRV>(module_, "nvdrv:s")->InstallAsService(service_manager); @@ -32,17 +36,25 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger nvflinger.SetNVDrvInstance(module_); } -Module::Module() { - auto nvmap_dev = std::make_shared<Devices::nvmap>(); - devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(nvmap_dev); - devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(nvmap_dev); - devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(); +Module::Module(Core::System& system) { + 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, Kernel::ResetType::Automatic, 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-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>(nvmap_dev); - devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(); - devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(); - devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(); - devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(); + 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-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system); + devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system); + devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system); } Module::~Module() = default; @@ -59,12 +71,13 @@ u32 Module::Open(const std::string& device_name) { return fd; } -u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output) { +u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl) { auto itr = open_files.find(fd); ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device"); auto& device = itr->second; - return device->ioctl({command}, input, output); + return device->ioctl({command}, input, output, ctrl); } ResultCode Module::Close(u32 fd) { @@ -77,4 +90,22 @@ ResultCode Module::Close(u32 fd) { return RESULT_SUCCESS; } +void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { + for (u32 i = 0; i < MaxNvEvents; i++) { + if (events_interface.assigned_syncpt[i] == syncpoint_id && + events_interface.assigned_value[i] == value) { + events_interface.LiberateEvent(i); + events_interface.events[i].writable->Signal(); + } + } +} + +Kernel::SharedPtr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const { + return events_interface.events[event_id].readable; +} + +Kernel::SharedPtr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const { + return events_interface.events[event_id].writable; +} + } // namespace Service::Nvidia diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 53564f696..a339ab672 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -8,8 +8,14 @@ #include <unordered_map> #include <vector> #include "common/common_types.h" +#include "core/hle/kernel/writable_event.h" +#include "core/hle/service/nvdrv/nvdata.h" #include "core/hle/service/service.h" +namespace Core { +class System; +} + namespace Service::NVFlinger { class NVFlinger; } @@ -20,16 +26,72 @@ namespace Devices { class nvdevice; } -struct IoctlFence { - u32 id; - u32 value; +struct EventInterface { + // Mask representing currently busy events + u64 events_mask{}; + // Each kernel event associated to an NV event + std::array<Kernel::EventPair, MaxNvEvents> events; + // The status of the current NVEvent + std::array<EventState, MaxNvEvents> status{}; + // Tells if an NVEvent is registered or not + std::array<bool, MaxNvEvents> registered{}; + // When an NVEvent is waiting on GPU interrupt, this is the sync_point + // associated with it. + std::array<u32, MaxNvEvents> assigned_syncpt{}; + // This is the value of the GPU interrupt for which the NVEvent is waiting + // for. + std::array<u32, MaxNvEvents> assigned_value{}; + // Constant to denote an unasigned syncpoint. + static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; + std::optional<u32> GetFreeEvent() const { + u64 mask = events_mask; + for (u32 i = 0; i < MaxNvEvents; i++) { + const bool is_free = (mask & 0x1) == 0; + if (is_free) { + if (status[i] == EventState::Registered || status[i] == EventState::Free) { + return {i}; + } + } + mask = mask >> 1; + } + return {}; + } + void SetEventStatus(const u32 event_id, EventState new_status) { + EventState old_status = status[event_id]; + if (old_status == new_status) { + return; + } + status[event_id] = new_status; + if (new_status == EventState::Registered) { + registered[event_id] = true; + } + if (new_status == EventState::Waiting || new_status == EventState::Busy) { + events_mask |= (1ULL << event_id); + } + } + void RegisterEvent(const u32 event_id) { + registered[event_id] = true; + if (status[event_id] == EventState::Free) { + status[event_id] = EventState::Registered; + } + } + void UnregisterEvent(const u32 event_id) { + registered[event_id] = false; + if (status[event_id] == EventState::Registered) { + status[event_id] = EventState::Free; + } + } + void LiberateEvent(const u32 event_id) { + status[event_id] = registered[event_id] ? EventState::Registered : EventState::Free; + events_mask &= ~(1ULL << event_id); + assigned_syncpt[event_id] = unassigned_syncpt; + assigned_value[event_id] = 0; + } }; -static_assert(sizeof(IoctlFence) == 8, "IoctlFence has wrong size"); - class Module final { public: - Module(); + Module(Core::System& system); ~Module(); /// Returns a pointer to one of the available devices, identified by its name. @@ -44,10 +106,17 @@ public: /// Opens a device node and returns a file descriptor to it. u32 Open(const std::string& device_name); /// Sends an ioctl command to the specified file descriptor. - u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output); + u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, std::vector<u8>& output, + IoctlCtrl& ctrl); /// Closes a device file descriptor and returns operation success. ResultCode Close(u32 fd); + void SignalSyncpt(const u32 syncpoint_id, const u32 value); + + Kernel::SharedPtr<Kernel::ReadableEvent> GetEvent(u32 event_id) const; + + Kernel::SharedPtr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const; + private: /// Id to use for the next open file descriptor. u32 next_fd = 1; @@ -57,9 +126,12 @@ private: /// Mapping of device node names to their implementation. std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices; + + EventInterface events_interface; }; /// Registers all NVDRV services with the specified service manager. -void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger); +void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, + Core::System& system); } // namespace Service::Nvidia |