From 34ae2ec644f49b04d6c6b82742812b6a8a3ef8b5 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 8 Jan 2018 21:30:22 -0500 Subject: [PATCH] NV: Expose the nvdisp_disp0 device and a weak reference to the nvdrv:a service. NVFlinger will call into the nvdisp_disp0 device to perform screen flips, bypassing the ioctl interface. We now have the address of the framebuffer to draw, we just need to actually put it on the screen. --- src/core/hle/service/nvdrv/nvdrv.cpp | 8 +- src/core/hle/service/nvdrv/nvdrv.h | 94 +++++++ src/core/hle/service/nvdrv/nvdrv_a.cpp | 338 +++++++++++-------------- src/core/hle/service/nvdrv/nvdrv_a.h | 14 +- src/core/hle/service/vi/vi.cpp | 15 +- src/core/hle/service/vi/vi.h | 3 +- 6 files changed, 276 insertions(+), 196 deletions(-) diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index a2d55eaeec..7923e1c0d3 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -8,9 +8,13 @@ namespace Service { namespace NVDRV { +std::weak_ptr nvdrv_a; + void InstallInterfaces(SM::ServiceManager& service_manager) { - std::make_shared()->InstallAsService(service_manager); + auto nvdrv = std::make_shared(); + nvdrv->InstallAsService(service_manager); + nvdrv_a = nvdrv; } -} // namespace nvdrv +} // namespace NVDRV } // namespace Service diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index a8f305d331..fd59c1dba7 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -4,6 +4,8 @@ #pragma once +#include +#include #include #include "common/common_types.h" #include "core/hle/service/service.h" @@ -18,6 +20,98 @@ public: virtual u32 ioctl(u32 command, const std::vector& input, std::vector& output) = 0; }; +class nvmap : public nvdevice { +public: + /// Returns the allocated address of an nvmap object given its handle. + VAddr GetObjectAddress(u32 handle) const; + + u32 ioctl(u32 command, const std::vector& input, std::vector& output) override; +private: + // Represents an nvmap object. + struct Object { + enum class Status { Created, Allocated }; + u32 id; + u32 size; + u32 flags; + u32 align; + u8 kind; + VAddr addr; + Status status; + }; + + u32 next_handle = 1; + u32 next_id = 1; + std::unordered_map> handles; + + enum IoctlCommands { + IocCreateCommand = 0xC0080101, + IocFromIdCommand = 0xC0080103, + IocAllocCommand = 0xC0200104, + IocParamCommand = 0xC00C0109, + IocGetIdCommand = 0xC008010E + }; + + struct IocCreateParams { + // Input + u32_le size; + // Output + u32_le handle; + }; + + struct IocAllocParams { + // Input + u32_le handle; + u32_le heap_mask; + u32_le flags; + u32_le align; + u8 kind; + INSERT_PADDING_BYTES(7); + u64_le addr; + }; + + struct IocGetIdParams { + // Output + u32_le id; + // Input + u32_le handle; + }; + + struct IocFromIdParams { + // Input + u32_le id; + // Output + u32_le handle; + }; + + struct IocParamParams { + // Input + u32_le handle; + u32_le type; + // Output + u32_le value; + }; + + u32 IocCreate(const std::vector& input, std::vector& output); + u32 IocAlloc(const std::vector& input, std::vector& output); + u32 IocGetId(const std::vector& input, std::vector& output); + u32 IocFromId(const std::vector& input, std::vector& output); + u32 IocParam(const std::vector& input, std::vector& output); +}; + +class nvdisp_disp0 : public nvdevice { +public: + nvdisp_disp0(std::shared_ptr nvmap_dev) : nvdevice(), nvmap_dev(std::move(nvmap_dev)) {} + ~nvdisp_disp0() = default; + + u32 ioctl(u32 command, const std::vector& input, std::vector& output) 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); + +private: + std::shared_ptr nvmap_dev; +}; + /// Registers all NVDRV services with the specified service manager. void InstallInterfaces(SM::ServiceManager& service_manager); diff --git a/src/core/hle/service/nvdrv/nvdrv_a.cpp b/src/core/hle/service/nvdrv/nvdrv_a.cpp index af6b7f7aaa..cede4a883f 100644 --- a/src/core/hle/service/nvdrv/nvdrv_a.cpp +++ b/src/core/hle/service/nvdrv/nvdrv_a.cpp @@ -18,202 +18,156 @@ public: } }; -class nvmap : public nvdevice { -public: - u32 ioctl(u32 command, const std::vector& input, std::vector& output) override { - switch (command) { - case IocCreateCommand: - return IocCreate(input, output); - case IocAllocCommand: - return IocAlloc(input, output); - case IocGetIdCommand: - return IocGetId(input, output); - case IocFromIdCommand: - return IocFromId(input, output); - case IocParamCommand: - return IocParam(input, output); - } +VAddr nvmap::GetObjectAddress(u32 handle) const { + auto itr = handles.find(handle); + ASSERT(itr != handles.end()); + auto object = itr->second; + ASSERT(object->status == Object::Status::Allocated); + return object->addr; +} + +u32 nvmap::ioctl(u32 command, const std::vector& input, std::vector& output) { + switch (command) { + case IocCreateCommand: + return IocCreate(input, output); + case IocAllocCommand: + return IocAlloc(input, output); + case IocGetIdCommand: + return IocGetId(input, output); + case IocFromIdCommand: + return IocFromId(input, output); + case IocParamCommand: + return IocParam(input, output); + } + + ASSERT(false, "Unimplemented"); +} + +u32 nvmap::IocCreate(const std::vector& input, std::vector& output) { + IocCreateParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + // Create a new nvmap object and obtain a handle to it. + auto object = std::make_shared(); + object->id = next_id++; + object->size = params.size; + object->status = Object::Status::Created; + + u32 handle = next_handle++; + handles[handle] = std::move(object); + + LOG_WARNING(Service, "(STUBBED) size 0x%08X", params.size); + + params.handle = handle; + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +u32 nvmap::IocAlloc(const std::vector& input, std::vector& output) { + IocAllocParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + auto itr = handles.find(params.handle); + ASSERT(itr != handles.end()); + + auto object = itr->second; + object->flags = params.flags; + object->align = params.align; + object->kind = params.kind; + object->addr = params.addr; + object->status = Object::Status::Allocated; + + LOG_WARNING(Service, "(STUBBED) Allocated address 0x%llx", params.addr); + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +u32 nvmap::IocGetId(const std::vector& input, std::vector& output) { + IocGetIdParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + LOG_WARNING(Service, "called"); + + auto itr = handles.find(params.handle); + ASSERT(itr != handles.end()); + + params.id = itr->second->id; + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +u32 nvmap::IocFromId(const std::vector& input, std::vector& output) { + IocFromIdParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + LOG_WARNING(Service, "(STUBBED) called"); + + auto itr = std::find_if(handles.begin(), handles.end(), + [&](const auto& entry) { return entry.second->id == params.id; }); + ASSERT(itr != handles.end()); + + // Make a new handle for the object + u32 handle = next_handle++; + handles[handle] = itr->second; + + params.handle = handle; + + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} + +u32 nvmap::IocParam(const std::vector& input, std::vector& output) { + enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; + + IocParamParams params; + std::memcpy(¶ms, input.data(), sizeof(params)); + + LOG_WARNING(Service, "(STUBBED) called type=%u", params.type); + + auto itr = handles.find(params.handle); + ASSERT(itr != handles.end()); + + auto object = itr->second; + ASSERT(object->status == Object::Status::Allocated); + + switch (static_cast(params.type)) { + case ParamTypes::Size: + params.value = object->size; + break; + case ParamTypes::Alignment: + params.value = object->align; + break; + case ParamTypes::Heap: + // TODO(Subv): Seems to be a hardcoded value? + params.value = 0x40000000; + break; + case ParamTypes::Kind: + params.value = object->kind; + break; + default: ASSERT(false, "Unimplemented"); } -private: - // Represents an nvmap object. - struct Object { - enum class Status { Created, Allocated }; - u32 id; - u32 size; - u32 flags; - u32 align; - u8 kind; - u64 addr; - Status status; - }; + std::memcpy(output.data(), ¶ms, sizeof(params)); + return 0; +} - u32 next_handle = 1; - u32 next_id = 1; - std::unordered_map> handles; +u32 nvdisp_disp0::ioctl(u32 command, const std::vector& input, std::vector& output) { + ASSERT(false, "Unimplemented"); + return 0; +} - enum IoctlCommands { - IocCreateCommand = 0xC0080101, - IocFromIdCommand = 0xC0080103, - IocAllocCommand = 0xC0200104, - IocParamCommand = 0xC00C0109, - IocGetIdCommand = 0xC008010E - }; - - struct IocCreateParams { - // Input - u32_le size; - // Output - u32_le handle; - }; - - struct IocAllocParams { - // Input - u32_le handle; - u32_le heap_mask; - u32_le flags; - u32_le align; - u8 kind; - INSERT_PADDING_BYTES(7); - u64_le addr; - }; - - struct IocGetIdParams { - // Output - u32_le id; - // Input - u32_le handle; - }; - - struct IocFromIdParams { - // Input - u32_le id; - // Output - u32_le handle; - }; - - struct IocParamParams { - // Input - u32_le handle; - u32_le type; - // Output - u32_le value; - }; - - u32 IocCreate(const std::vector& input, std::vector& output) { - IocCreateParams params; - std::memcpy(¶ms, input.data(), sizeof(params)); - - // Create a new nvmap object and obtain a handle to it. - auto object = std::make_shared(); - object->id = next_id++; - object->size = params.size; - object->status = Object::Status::Created; - - u32 handle = next_handle++; - handles[handle] = std::move(object); - - LOG_WARNING(Service, "(STUBBED) size 0x%08X", params.size); - - params.handle = handle; - - std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; - } - - u32 IocAlloc(const std::vector& input, std::vector& output) { - IocAllocParams params; - std::memcpy(¶ms, input.data(), sizeof(params)); - - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); - - auto object = itr->second; - object->flags = params.flags; - object->align = params.align; - object->kind = params.kind; - object->addr = params.addr; - object->status = Object::Status::Allocated; - - LOG_WARNING(Service, "(STUBBED) Allocated address 0x%llx", params.addr); - - std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; - } - - u32 IocGetId(const std::vector& input, std::vector& output) { - IocGetIdParams params; - std::memcpy(¶ms, input.data(), sizeof(params)); - - LOG_WARNING(Service, "called"); - - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); - - params.id = itr->second->id; - - std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; - } - - u32 IocFromId(const std::vector& input, std::vector& output) { - IocFromIdParams params; - std::memcpy(¶ms, input.data(), sizeof(params)); - - LOG_WARNING(Service, "(STUBBED) called"); - - auto itr = std::find_if(handles.begin(), handles.end(), - [&](const auto& entry) { return entry.second->id == params.id; }); - ASSERT(itr != handles.end()); - - // Make a new handle for the object - u32 handle = next_handle++; - handles[handle] = itr->second; - - params.handle = handle; - - std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; - } - - u32 IocParam(const std::vector& input, std::vector& output) { - enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 }; - - IocParamParams params; - std::memcpy(¶ms, input.data(), sizeof(params)); - - LOG_WARNING(Service, "(STUBBED) called type=%u", params.type); - - auto itr = handles.find(params.handle); - ASSERT(itr != handles.end()); - - auto object = itr->second; - ASSERT(object->status == Object::Status::Allocated); - - switch (static_cast(params.type)) { - case ParamTypes::Size: - params.value = object->size; - break; - case ParamTypes::Alignment: - params.value = object->align; - break; - case ParamTypes::Heap: - // TODO(Subv): Seems to be a hardcoded value? - params.value = 0x40000000; - break; - case ParamTypes::Kind: - params.value = object->kind; - break; - default: - ASSERT(false, "Unimplemented"); - } - - std::memcpy(output.data(), ¶ms, sizeof(params)); - return 0; - } -}; +void nvdisp_disp0::flip(u32 buffer_handle, u32 offset, u32 format, u32 width, u32 height, + u32 stride) { + VAddr addr = nvmap_dev->GetObjectAddress(buffer_handle); + LOG_WARNING(Service, + "Drawing from address %llx offset %08X Width %u Height %u Stride %u Format %u", + addr, offset, width, height, stride, format); +} void NVDRV_A::Open(Kernel::HLERequestContext& ctx) { LOG_WARNING(Service, "(STUBBED) called"); @@ -275,8 +229,10 @@ NVDRV_A::NVDRV_A() : ServiceFramework("nvdrv:a") { }; RegisterHandlers(functions); + auto nvmap_dev = std::make_shared(); devices["/dev/nvhost-as-gpu"] = std::make_shared(); - devices["/dev/nvmap"] = std::make_shared(); + devices["/dev/nvmap"] = nvmap_dev; + devices["/dev/nvdisp_disp0"] = std::make_shared(nvmap_dev); } } // namespace NVDRV diff --git a/src/core/hle/service/nvdrv/nvdrv_a.h b/src/core/hle/service/nvdrv/nvdrv_a.h index 09522a4869..af10178816 100644 --- a/src/core/hle/service/nvdrv/nvdrv_a.h +++ b/src/core/hle/service/nvdrv/nvdrv_a.h @@ -4,8 +4,9 @@ #pragma once -#include "core/hle/service/service.h" #include +#include "core/hle/service/service.h" +#include "core/hle/service/nvdrv/nvdrv.h" namespace Service { namespace NVDRV { @@ -15,6 +16,15 @@ public: NVDRV_A(); ~NVDRV_A() = default; + /// Returns a pointer to one of the available devices, identified by its name. + template + std::shared_ptr GetDevice(std::string name) { + auto itr = devices.find(name); + if (itr == devices.end()) + return nullptr; + return std::static_pointer_cast(itr->second); + } + private: void Open(Kernel::HLERequestContext& ctx); void Ioctl(Kernel::HLERequestContext& ctx); @@ -26,5 +36,7 @@ private: std::unordered_map> devices; }; +extern std::weak_ptr nvdrv_a; + } // namespace NVDRV } // namespace Service diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp index 67d82c2bf7..56aafe6bf2 100644 --- a/src/core/hle/service/vi/vi.cpp +++ b/src/core/hle/service/vi/vi.cpp @@ -6,6 +6,7 @@ #include "common/scope_exit.h" #include "core/core_timing.h" #include "core/hle/ipc_helpers.h" +#include "core/hle/service/nvdrv/nvdrv_a.h" #include "core/hle/service/vi/vi.h" #include "core/hle/service/vi/vi_m.h" @@ -743,7 +744,19 @@ void NVFlinger::Compose() { continue; } - // TODO(Subv): Send the buffer to the GPU for drawing. + auto& igbp_buffer = buffer->igbp_buffer; + + // Now send the buffer to the GPU for drawing. + auto nvdrv = NVDRV::nvdrv_a.lock(); + ASSERT(nvdrv); + + // TODO(Subv): Support more than just disp0. The display device selection is probably based + // on which display we're drawing (Default, Internal, External, etc) + auto nvdisp = nvdrv->GetDevice("/dev/nvdisp_disp0"); + ASSERT(nvdisp); + + nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format, + igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride); buffer_queue->ReleaseBuffer(buffer->slot); } diff --git a/src/core/hle/service/vi/vi.h b/src/core/hle/service/vi/vi.h index 576c4ce32b..9604bd1c29 100644 --- a/src/core/hle/service/vi/vi.h +++ b/src/core/hle/service/vi/vi.h @@ -25,7 +25,8 @@ struct IGBPBuffer { u32_le gpu_buffer_id; INSERT_PADDING_WORDS(17); u32_le nvmap_handle; - INSERT_PADDING_WORDS(61); + u32_le offset; + INSERT_PADDING_WORDS(60); }; static_assert(sizeof(IGBPBuffer) == 0x16C, "IGBPBuffer has wrong size");