From 34ae2ec644f49b04d6c6b82742812b6a8a3ef8b5 Mon Sep 17 00:00:00 2001
From: Subv <subv2112@gmail.com>
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 a2d55eaee..7923e1c0d 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> nvdrv_a;
+
 void InstallInterfaces(SM::ServiceManager& service_manager) {
-    std::make_shared<NVDRV_A>()->InstallAsService(service_manager);
+    auto nvdrv = std::make_shared<NVDRV_A>();
+    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 a8f305d33..fd59c1dba 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -4,6 +4,8 @@
 
 #pragma once
 
+#include <memory>
+#include <unordered_map>
 #include <vector>
 #include "common/common_types.h"
 #include "core/hle/service/service.h"
@@ -18,6 +20,98 @@ public:
     virtual u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& 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<u8>& input, std::vector<u8>& 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<u32, std::shared_ptr<Object>> 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<u8>& input, std::vector<u8>& output);
+    u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
+    u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
+    u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
+    u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
+};
+
+class nvdisp_disp0 : public nvdevice {
+public:
+    nvdisp_disp0(std::shared_ptr<nvmap> nvmap_dev) : nvdevice(), nvmap_dev(std::move(nvmap_dev)) {}
+    ~nvdisp_disp0() = default;
+
+    u32 ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& 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> 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 af6b7f7aa..cede4a883 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<u8>& input, std::vector<u8>& 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<u8>& input, std::vector<u8>& 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<u8>& input, std::vector<u8>& output) {
+    IocCreateParams params;
+    std::memcpy(&params, input.data(), sizeof(params));
+
+    // Create a new nvmap object and obtain a handle to it.
+    auto object = std::make_shared<Object>();
+    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(), &params, sizeof(params));
+    return 0;
+}
+
+u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
+    IocAllocParams params;
+    std::memcpy(&params, 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(), &params, sizeof(params));
+    return 0;
+}
+
+u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
+    IocGetIdParams params;
+    std::memcpy(&params, 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(), &params, sizeof(params));
+    return 0;
+}
+
+u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
+    IocFromIdParams params;
+    std::memcpy(&params, 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(), &params, sizeof(params));
+    return 0;
+}
+
+u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
+    enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
+
+    IocParamParams params;
+    std::memcpy(&params, 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<ParamTypes>(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(), &params, sizeof(params));
+    return 0;
+}
 
-    u32 next_handle = 1;
-    u32 next_id = 1;
-    std::unordered_map<u32, std::shared_ptr<Object>> handles;
+u32 nvdisp_disp0::ioctl(u32 command, const std::vector<u8>& input, std::vector<u8>& 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<u8>& input, std::vector<u8>& output) {
-        IocCreateParams params;
-        std::memcpy(&params, input.data(), sizeof(params));
-
-        // Create a new nvmap object and obtain a handle to it.
-        auto object = std::make_shared<Object>();
-        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(), &params, sizeof(params));
-        return 0;
-    }
-
-    u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
-        IocAllocParams params;
-        std::memcpy(&params, 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(), &params, sizeof(params));
-        return 0;
-    }
-
-    u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
-        IocGetIdParams params;
-        std::memcpy(&params, 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(), &params, sizeof(params));
-        return 0;
-    }
-
-    u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
-        IocFromIdParams params;
-        std::memcpy(&params, 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(), &params, sizeof(params));
-        return 0;
-    }
-
-    u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
-        enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
-
-        IocParamParams params;
-        std::memcpy(&params, 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<ParamTypes>(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(), &params, 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<nvmap>();
     devices["/dev/nvhost-as-gpu"] = std::make_shared<nvhost_as_gpu>();
-    devices["/dev/nvmap"] = std::make_shared<nvmap>();
+    devices["/dev/nvmap"] = nvmap_dev;
+    devices["/dev/nvdisp_disp0"] = std::make_shared<nvdisp_disp0>(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 09522a486..af1017881 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 <memory>
+#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 <typename T>
+    std::shared_ptr<T> GetDevice(std::string name) {
+        auto itr = devices.find(name);
+        if (itr == devices.end())
+            return nullptr;
+        return std::static_pointer_cast<T>(itr->second);
+    }
+
 private:
     void Open(Kernel::HLERequestContext& ctx);
     void Ioctl(Kernel::HLERequestContext& ctx);
@@ -26,5 +36,7 @@ private:
     std::unordered_map<std::string, std::shared_ptr<nvdevice>> devices;
 };
 
+extern std::weak_ptr<NVDRV_A> 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 67d82c2bf..56aafe6bf 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<NVDRV::nvdisp_disp0>("/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 576c4ce32..9604bd1c2 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");