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.
This commit is contained in:
Subv 2018-01-08 21:30:22 -05:00 committed by bunnei
parent e21fbd9ae5
commit 34ae2ec644
6 changed files with 276 additions and 196 deletions

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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");