mirror of
https://github.com/Takiiiiiiii/strato.git
synced 2025-07-17 08:46:39 +00:00
Refactor Display Services and GPU
This commit addresses the incorrect hierarchy of the GPU and refactors them at the same time. Now, the hierarchy much closely matches HOS. This commit also introduces a texture classes, albeit they're not complete and only partially implemented.
This commit is contained in:
@ -1,183 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include <kernel/ipc.h>
|
||||
|
||||
#define NFUNC(function) std::bind(&function, this, std::placeholders::_1)
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
/**
|
||||
* @brief An enumeration of all the devices that can be opened by nvdrv
|
||||
*/
|
||||
enum class NvDeviceType {
|
||||
nvhost_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl
|
||||
nvhost_gpu, //!< https://switchbrew.org/wiki/NV_services#Channels
|
||||
nvhost_nvdec, //!< https://switchbrew.org/wiki/NV_services#Channels
|
||||
nvhost_vic, //!< https://switchbrew.org/wiki/NV_services#Channels
|
||||
nvmap, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvmap
|
||||
nvdisp_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdisp-ctrl
|
||||
nvdisp_disp0, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdisp-disp0.2C_.2Fdev.2Fnvdisp-disp1
|
||||
nvdisp_disp1, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdisp-disp0.2C_.2Fdev.2Fnvdisp-disp1
|
||||
nvcec_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvcec-ctrl
|
||||
nvhdcp_up_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhdcp_up-ctrl
|
||||
nvdcutil_disp0, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdcutil-disp0.2C_.2Fdev.2Fnvdcutil-disp1
|
||||
nvdcutil_disp1, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvdcutil-disp0.2C_.2Fdev.2Fnvdcutil-disp1
|
||||
nvsched_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvsched-ctrl
|
||||
nverpt_ctrl, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnverpt-ctrl
|
||||
nvhost_as_gpu, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu
|
||||
nvhost_dbg_gpu, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-dbg-gpu
|
||||
nvhost_prof_gpu, //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-prof-gpu
|
||||
nvhost_ctrl_gpu //!< https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl-gpu
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A mapping from a device's path to it's nvDevice entry
|
||||
*/
|
||||
const static std::unordered_map<std::string, NvDeviceType> nvDeviceMap{
|
||||
{"/dev/nvhost-ctrl", NvDeviceType::nvhost_ctrl},
|
||||
{"/dev/nvhost-gpu", NvDeviceType::nvhost_gpu},
|
||||
{"/dev/nvhost-nvdec", NvDeviceType::nvhost_nvdec},
|
||||
{"/dev/nvhost-vic", NvDeviceType::nvhost_vic},
|
||||
{"/dev/nvmap", NvDeviceType::nvmap},
|
||||
{"/dev/nvdisp-ctrl", NvDeviceType::nvdisp_ctrl},
|
||||
{"/dev/nvdisp-disp0", NvDeviceType::nvdisp_disp0},
|
||||
{"/dev/nvdisp-disp1", NvDeviceType::nvdisp_disp1},
|
||||
{"/dev/nvcec-ctrl", NvDeviceType::nvcec_ctrl},
|
||||
{"/dev/nvhdcp_up-ctrl", NvDeviceType::nvhdcp_up_ctrl},
|
||||
{"/dev/nvdcutil-disp0", NvDeviceType::nvdcutil_disp0},
|
||||
{"/dev/nvdcutil-disp1", NvDeviceType::nvdcutil_disp1},
|
||||
{"/dev/nvsched-ctrl", NvDeviceType::nvsched_ctrl},
|
||||
{"/dev/nverpt-ctrl", NvDeviceType::nverpt_ctrl},
|
||||
{"/dev/nvhost-as-gpu", NvDeviceType::nvhost_as_gpu},
|
||||
{"/dev/nvhost-dbg-gpu", NvDeviceType::nvhost_dbg_gpu},
|
||||
{"/dev/nvhost-prof-gpu", NvDeviceType::nvhost_prof_gpu},
|
||||
{"/dev/nvhost-ctrl-gpu", NvDeviceType::nvhost_ctrl_gpu}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This enumerates all the possible error codes returned by the Nvidia driver (https://switchbrew.org/wiki/NV_services#Errors)
|
||||
*/
|
||||
enum NvStatus : u32 {
|
||||
Success = 0x0, //!< The operation has succeeded
|
||||
NotImplemented = 0x1, //!< The operation is not implemented
|
||||
NotSupported = 0x2, //!< The operation is not supported
|
||||
NotInitialized = 0x3, //!< The operation uses an uninitialized object
|
||||
BadParameter = 0x4, //!< The operation was provided a bad parameter
|
||||
Timeout = 0x5, //!< The operation has timed out
|
||||
InsufficientMemory = 0x6, //!< The device ran out of memory during the operation
|
||||
ReadOnlyAttribute = 0x7, //!< The mutating operation was performed on a read only section
|
||||
InvalidState = 0x8, //!< The state of the device was invalid
|
||||
InvalidAddress = 0x9, //!< The provided address is invalid
|
||||
InvalidSize = 0xA, //!< The provided size is invalid
|
||||
BadValue = 0xB, //!< The operation was provided a bad value
|
||||
AlreadyAllocated = 0xD, //!< An object was tried to be reallocated
|
||||
Busy = 0xE, //!< The device is busy
|
||||
ResourceError = 0xF, //!< There was an error accessing the resource
|
||||
CountMismatch = 0x10, //!< ?
|
||||
SharedMemoryTooSmall = 0x1000, //!< The shared memory segment is too small
|
||||
FileOperationFailed = 0x30003, //!< The file operation has failed
|
||||
DirOperationFailed = 0x30004, //!< The directory operation has failed
|
||||
IoctlFailed = 0x3000F, //!< The IOCTL operation has failed
|
||||
AccessDenied = 0x30010, //!< The access to a resource was denied
|
||||
FileNotFound = 0x30013, //!< A file was not found
|
||||
ModuleNotPresent = 0xA000E, //!< A module was not present
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This holds all the input and output data for an IOCTL function
|
||||
*/
|
||||
struct IoctlData {
|
||||
std::vector<kernel::ipc::InputBuffer> input; //!< A vector of all input IOCTL buffers
|
||||
std::vector<kernel::ipc::OutputBuffer> output; //!< A vector of all output IOCTL buffers
|
||||
NvStatus status{NvStatus::Success}; //!< The error code that is returned to the application
|
||||
|
||||
/**
|
||||
* @brief This constructor takes 1 input buffer and 1 output buffer, it's used for Ioctl
|
||||
* @param input An input buffer
|
||||
* @param output An output buffer
|
||||
*/
|
||||
IoctlData(kernel::ipc::InputBuffer input, kernel::ipc::OutputBuffer output) : input({input}), output({output}) {}
|
||||
|
||||
/**
|
||||
* @brief This constructor takes 1 input buffer, it's used for Ioctl sometimes
|
||||
* @param output An output buffer
|
||||
*/
|
||||
IoctlData(kernel::ipc::InputBuffer input) : input({input}) {}
|
||||
|
||||
/**
|
||||
* @brief This constructor takes 1 output buffer, it's used for Ioctl sometimes
|
||||
* @param output An output buffer
|
||||
*/
|
||||
IoctlData(kernel::ipc::OutputBuffer output) : output({output}) {}
|
||||
|
||||
/**
|
||||
* @brief This constructor takes 2 input buffers and 1 output buffer, it's used for Ioctl1
|
||||
* @param input1 The first input buffer
|
||||
* @param input2 The second input buffer
|
||||
* @param output An output buffer
|
||||
*/
|
||||
IoctlData(kernel::ipc::InputBuffer input1, kernel::ipc::InputBuffer input2, kernel::ipc::OutputBuffer output) : input({input1, input2}), output({output}) {}
|
||||
|
||||
/**
|
||||
* @brief This constructor takes 1 input buffer and 2 output buffers, it's used for Ioctl2
|
||||
* @param input An input buffer
|
||||
* @param output1 The first output buffer
|
||||
* @param output2 The second output buffer
|
||||
*/
|
||||
IoctlData(kernel::ipc::InputBuffer input, kernel::ipc::OutputBuffer output1, kernel::ipc::OutputBuffer output2) : input({input}), output({output1, output2}) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief NvDevice is the base class all /dev/nv* devices inherit from
|
||||
*/
|
||||
class NvDevice {
|
||||
protected:
|
||||
const DeviceState &state; //!< The state of the device
|
||||
std::unordered_map<u32, std::function<void(IoctlData &)>> vTable; //!< This holds the mapping from an Ioctl to the actual function
|
||||
|
||||
public:
|
||||
u16 refCount{1}; //!< The amount of handles to the device
|
||||
NvDeviceType deviceType; //!< The type of the device
|
||||
|
||||
/**
|
||||
* @param state The state of the device
|
||||
* @param deviceType The type of the device
|
||||
* @param vTable The functions in this device
|
||||
*/
|
||||
NvDevice(const DeviceState &state, NvDeviceType deviceType, std::unordered_map<u32, std::function<void(IoctlData &)>> vTable) : state(state), deviceType(deviceType), vTable(vTable) {}
|
||||
|
||||
/**
|
||||
* @brief This returns the name of the current service
|
||||
* @note It may not return the exact name the service was initialized with if there are multiple entries in ServiceString
|
||||
* @return The name of the service
|
||||
*/
|
||||
std::string getName() {
|
||||
std::string serviceName;
|
||||
for (const auto&[name, type] : nvDeviceMap)
|
||||
if (type == deviceType)
|
||||
serviceName = name;
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This handles IOCTL calls for devices
|
||||
* @param cmd The IOCTL command that was called
|
||||
* @param input The input to the IOCTL call
|
||||
*/
|
||||
void HandleIoctl(u32 cmd, IoctlData &input) {
|
||||
std::function<void(IoctlData &)> function;
|
||||
try {
|
||||
function = vTable.at(cmd);
|
||||
} catch (std::out_of_range &) {
|
||||
state.logger->Warn("Cannot find IOCTL for device '{}': 0x{:X}", getName(), cmd);
|
||||
input.status = NvStatus::NotImplemented;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
function(input);
|
||||
} catch (std::exception &e) {
|
||||
throw exception("{} (Device: {})", e.what(), getName());
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
#include "nvhost_as_gpu.h"
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
NvHostAsGpu::NvHostAsGpu(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_as_gpu, {}) {}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvdevice.h"
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
/**
|
||||
* @brief NvHostAsGpu (/dev/nvhost-as-gpu) is used to access GPU virtual address spaces (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-as-gpu)
|
||||
*/
|
||||
class NvHostAsGpu : public NvDevice {
|
||||
public:
|
||||
NvHostAsGpu(const DeviceState &state);
|
||||
};
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
#include "nvhost_channel.h"
|
||||
#include <kernel/types/KProcess.h>
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
NvHostChannel::NvHostChannel(const DeviceState &state, NvDeviceType type) : NvDevice(state, type, {
|
||||
{0x40044801, NFUNC(NvHostChannel::SetNvmapFd)},
|
||||
{0xC0104809, NFUNC(NvHostChannel::AllocObjCtx)},
|
||||
{0xC010480B, NFUNC(NvHostChannel::ZcullBind)},
|
||||
{0xC018480C, NFUNC(NvHostChannel::SetErrorNotifier)},
|
||||
{0x4004480D, NFUNC(NvHostChannel::SetPriority)},
|
||||
{0xC020481A, NFUNC(NvHostChannel::AllocGpfifoEx2)},
|
||||
{0x40084714, NFUNC(NvHostChannel::SetUserData)}
|
||||
}) {}
|
||||
|
||||
void NvHostChannel::SetNvmapFd(skyline::gpu::device::IoctlData &buffer) {}
|
||||
|
||||
void NvHostChannel::AllocObjCtx(skyline::gpu::device::IoctlData &buffer) {}
|
||||
|
||||
void NvHostChannel::ZcullBind(IoctlData &buffer) {}
|
||||
|
||||
void NvHostChannel::SetErrorNotifier(skyline::gpu::device::IoctlData &buffer) {}
|
||||
|
||||
void NvHostChannel::SetPriority(skyline::gpu::device::IoctlData &buffer) {
|
||||
auto priority = state.process->GetObject<NvChannelPriority>(buffer.input[0].address);
|
||||
switch (priority) {
|
||||
case NvChannelPriority::Low:
|
||||
timeslice = 1300;
|
||||
break;
|
||||
case NvChannelPriority::Medium:
|
||||
timeslice = 2600;
|
||||
break;
|
||||
case NvChannelPriority::High:
|
||||
timeslice = 5200;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void NvHostChannel::AllocGpfifoEx2(skyline::gpu::device::IoctlData &buffer) {}
|
||||
|
||||
void NvHostChannel::SetUserData(skyline::gpu::device::IoctlData &buffer) {}
|
||||
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvdevice.h"
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
/**
|
||||
* @brief NvHostChannel is used as a common interface for all Channel devices (https://switchbrew.org/wiki/NV_services#Channels)
|
||||
*/
|
||||
class NvHostChannel : public NvDevice {
|
||||
private:
|
||||
enum class NvChannelPriority : u32 {
|
||||
Low = 0x32,
|
||||
Medium = 0x64,
|
||||
High = 0x94
|
||||
};
|
||||
|
||||
u32 timeslice{};
|
||||
|
||||
public:
|
||||
NvHostChannel(const DeviceState &state, NvDeviceType type);
|
||||
|
||||
/**
|
||||
* @brief This sets the nvmap file descriptor (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_NVMAP_FD)
|
||||
*/
|
||||
void SetNvmapFd(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This allocates a graphic context object (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_OBJ_CTX)
|
||||
*/
|
||||
void AllocObjCtx(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This initializes the error notifier for this channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ZCULL_BIND)
|
||||
*/
|
||||
void ZcullBind(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This initializes the error notifier for this channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_ERROR_NOTIFIER)
|
||||
*/
|
||||
void SetErrorNotifier(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This sets the priority of the channel (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_PRIORITY)
|
||||
*/
|
||||
void SetPriority(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This allocates a GPFIFO entry (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_ALLOC_GPFIFO_EX2)
|
||||
*/
|
||||
void AllocGpfifoEx2(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This sets the user specific data (https://switchbrew.org/wiki/NV_services#NVGPU_IOCTL_CHANNEL_SET_USER_DATA)
|
||||
*/
|
||||
void SetUserData(IoctlData &buffer);
|
||||
};
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
#include "nvhost_ctrl.h"
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
NvHostCtrl::NvHostCtrl(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_ctrl, {}) {}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvdevice.h"
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
/**
|
||||
* @brief NvHostCtrl (/dev/nvhost-ctrl) is used for GPU synchronization (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl)
|
||||
*/
|
||||
class NvHostCtrl : public NvDevice {
|
||||
public:
|
||||
NvHostCtrl(const DeviceState &state);
|
||||
};
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
#include "nvhost_ctrl_gpu.h"
|
||||
#include <kernel/types/KProcess.h>
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
NvHostCtrlGpu::NvHostCtrlGpu(const DeviceState &state) : NvDevice(state, NvDeviceType::nvhost_ctrl_gpu, {
|
||||
{0x80044701, NFUNC(NvHostCtrlGpu::ZCullGetCtxSize)},
|
||||
{0x80284702, NFUNC(NvHostCtrlGpu::ZCullGetInfo)},
|
||||
{0xC0184706, NFUNC(NvHostCtrlGpu::GetTpcMasks)},
|
||||
{0xC0B04705, NFUNC(NvHostCtrlGpu::GetCharacteristics)},
|
||||
{0x80084714, NFUNC(NvHostCtrlGpu::GetActiveSlotMask)}
|
||||
}) {}
|
||||
|
||||
void NvHostCtrlGpu::ZCullGetCtxSize(IoctlData &buffer) {
|
||||
u32 size = 0x1;
|
||||
state.process->WriteMemory(size, buffer.output[0].address);
|
||||
}
|
||||
|
||||
void NvHostCtrlGpu::ZCullGetInfo(skyline::gpu::device::IoctlData &buffer) {
|
||||
struct {
|
||||
u32 widthAlignPixels{0x20};
|
||||
u32 heightAlignPixels{0x20};
|
||||
u32 pixelSquaresByAliquots{0x400};
|
||||
u32 aliquotTotal{0x800};
|
||||
u32 regionByteMultiplier{0x20};
|
||||
u32 regionHeaderSize{0x20};
|
||||
u32 subregionHeaderSize{0xC0};
|
||||
u32 subregionWidthAlignPixels{0x20};
|
||||
u32 subregionHeightAlignPixels{0x40};
|
||||
u32 subregionCount{0x10};
|
||||
} zCullInfo;
|
||||
state.process->WriteMemory(zCullInfo, buffer.output[0].address);
|
||||
}
|
||||
|
||||
void NvHostCtrlGpu::GetCharacteristics(IoctlData &buffer) {
|
||||
struct GpuCharacteristics {
|
||||
u32 arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
|
||||
u32 impl; // 0xB (NVGPU_GPU_IMPL_GM20B) or 0xE (NVGPU_GPU_IMPL_GM20B_B)
|
||||
u32 rev; // 0xA1 (Revision A1)
|
||||
u32 numGpc; // 0x1
|
||||
u64 l2CacheSize; // 0x40000
|
||||
u64 onBoardVideoMemorySize; // 0x0 (not used)
|
||||
u32 numTpcPerGpc; // 0x2
|
||||
u32 busType; // 0x20 (NVGPU_GPU_BUS_TYPE_AXI)
|
||||
u32 bigPageSize; // 0x20000
|
||||
u32 compressionPageSize; // 0x20000
|
||||
u32 pdeCoverageBitCount; // 0x1B
|
||||
u32 availableBigPageSizes; // 0x30000
|
||||
u32 gpcMask; // 0x1
|
||||
u32 smArchSmVersion; // 0x503 (Maxwell Generation 5.0.3)
|
||||
u32 smArchSpaVersion; // 0x503 (Maxwell Generation 5.0.3)
|
||||
u32 smArchWarpCount; // 0x80
|
||||
u32 gpuVaBitCount; // 0x28
|
||||
u32 reserved; // NULL
|
||||
u64 flags; // 0x55 (HAS_SYNCPOINTS | SUPPORT_SPARSE_ALLOCS | SUPPORT_CYCLE_STATS | SUPPORT_CYCLE_STATS_SNAPSHOT)
|
||||
u32 twodClass; // 0x902D (FERMI_TWOD_A)
|
||||
u32 threedClass; // 0xB197 (MAXWELL_B)
|
||||
u32 computeClass; // 0xB1C0 (MAXWELL_COMPUTE_B)
|
||||
u32 gpfifoClass; // 0xB06F (MAXWELL_CHANNEL_GPFIFO_A)
|
||||
u32 inlineToMemoryClass; // 0xA140 (KEPLER_INLINE_TO_MEMORY_B)
|
||||
u32 dmaCopyClass; // 0xB0B5 (MAXWELL_DMA_COPY_A)
|
||||
u32 maxFbpsCount; // 0x1
|
||||
u32 fbpEnMask; // 0x0 (disabled)
|
||||
u32 maxLtcPerFbp; // 0x2
|
||||
u32 maxLtsPerLtc; // 0x1
|
||||
u32 maxTexPerTpc; // 0x0 (not supported)
|
||||
u32 maxGpcCount; // 0x1
|
||||
u32 ropL2EnMask0; // 0x21D70 (fuse_status_opt_rop_l2_fbp_r)
|
||||
u32 ropL2EnMask1; // 0x0
|
||||
u64 chipName; // 0x6230326D67 ("gm20b")
|
||||
u64 grCompbitStoreBaseHw; // 0x0 (not supported)
|
||||
};
|
||||
struct Data {
|
||||
u64 gpuCharacteristicsBufSize; // InOut
|
||||
u64 gpuCharacteristicsBufAddr; // In
|
||||
GpuCharacteristics gpuCharacteristics; // Out
|
||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
||||
data.gpuCharacteristics = {
|
||||
.arch = 0x120,
|
||||
.impl = 0xB,
|
||||
.rev = 0xA1,
|
||||
.numGpc = 0x1,
|
||||
.l2CacheSize = 0x40000,
|
||||
.onBoardVideoMemorySize = 0x0,
|
||||
.numTpcPerGpc = 0x2,
|
||||
.busType = 0x20,
|
||||
.bigPageSize = 0x20000,
|
||||
.compressionPageSize = 0x20000,
|
||||
.pdeCoverageBitCount = 0x1B,
|
||||
.availableBigPageSizes = 0x30000,
|
||||
.gpcMask = 0x1,
|
||||
.smArchSmVersion = 0x503,
|
||||
.smArchSpaVersion = 0x503,
|
||||
.smArchWarpCount = 0x80,
|
||||
.gpuVaBitCount = 0x2,
|
||||
.flags = 0x55,
|
||||
.twodClass = 0x902D,
|
||||
.threedClass = 0xB197,
|
||||
.computeClass = 0xB1C0,
|
||||
.gpfifoClass = 0xB06F,
|
||||
.inlineToMemoryClass = 0xA140,
|
||||
.dmaCopyClass = 0xB0B5,
|
||||
.maxFbpsCount = 0x1,
|
||||
.fbpEnMask = 0x0,
|
||||
.maxLtcPerFbp = 0x2,
|
||||
.maxLtsPerLtc = 0x1,
|
||||
.maxTexPerTpc = 0x0,
|
||||
.maxGpcCount = 0x1,
|
||||
.ropL2EnMask0 = 0x21D70,
|
||||
.ropL2EnMask1 = 0x0,
|
||||
.chipName = 0x6230326D67,
|
||||
.grCompbitStoreBaseHw = 0x0
|
||||
};
|
||||
data.gpuCharacteristicsBufSize = 0xA0;
|
||||
state.process->WriteMemory(data, buffer.output[0].address);
|
||||
}
|
||||
|
||||
void NvHostCtrlGpu::GetTpcMasks(IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 maskBufSize; // In
|
||||
u32 reserved[3]; // In
|
||||
u64 maskBuf; // Out
|
||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
||||
if (data.maskBufSize)
|
||||
data.maskBuf = 0x3;
|
||||
state.process->WriteMemory(data, buffer.output[0].address);
|
||||
}
|
||||
|
||||
void NvHostCtrlGpu::GetActiveSlotMask(IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 slot; // Out
|
||||
u32 mask; // Out
|
||||
} data = {
|
||||
.slot = 0x07,
|
||||
.mask = 0x01
|
||||
};
|
||||
state.process->WriteMemory(data, buffer.output[0].address);
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvdevice.h"
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
/**
|
||||
* @brief NvHostCtrlGpu (/dev/nvhost-ctrl-gpu) is used for context independent operations on the underlying GPU (https://switchbrew.org/wiki/NV_services#.2Fdev.2Fnvhost-ctrl-gpu)
|
||||
*/
|
||||
class NvHostCtrlGpu : public NvDevice {
|
||||
public:
|
||||
NvHostCtrlGpu(const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief This returns a u32 GPU ZCULL Context Size (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZCULL_GET_CTX_SIZE)
|
||||
*/
|
||||
void ZCullGetCtxSize(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This returns a the GPU ZCULL Information (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZCULL_GET_INFO)
|
||||
*/
|
||||
void ZCullGetInfo(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This returns a struct with certain GPU characteristics (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_CHARACTERISTICS)
|
||||
*/
|
||||
void GetCharacteristics(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This returns the TPC mask value for each GPC (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_GET_TPC_MASKS)
|
||||
*/
|
||||
void GetTpcMasks(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This returns the mask value for a ZBC slot (https://switchbrew.org/wiki/NV_services#NVGPU_GPU_IOCTL_ZBC_GET_ACTIVE_SLOT_MASK)
|
||||
*/
|
||||
void GetActiveSlotMask(IoctlData &buffer);
|
||||
};
|
||||
}
|
@ -1,140 +0,0 @@
|
||||
#include "nvmap.h"
|
||||
#include <kernel/types/KProcess.h>
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
NvMap::NvMapObject::NvMapObject(u32 id, u32 size) : id(id), size(size) {}
|
||||
|
||||
NvMap::NvMap(const DeviceState &state) : NvDevice(state, NvDeviceType::nvmap, {
|
||||
{0xC0080101, NFUNC(NvMap::Create)},
|
||||
{0xC0080103, NFUNC(NvMap::FromId)},
|
||||
{0xC0200104, NFUNC(NvMap::Alloc)},
|
||||
{0xC0180105, NFUNC(NvMap::Free)},
|
||||
{0xC00C0109, NFUNC(NvMap::Param)},
|
||||
{0xC008010E, NFUNC(NvMap::GetId)}
|
||||
}) {}
|
||||
|
||||
void NvMap::Create(IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 size; // In
|
||||
u32 handle; // Out
|
||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
||||
handleTable[handleIndex] = std::make_shared<NvMapObject>(idIndex++, data.size);
|
||||
data.handle = handleIndex++;
|
||||
state.process->WriteMemory(data, buffer.output[0].address);
|
||||
state.logger->Debug("Create: Input: Size: 0x{:X}, Output: Handle: 0x{:X}, Status: {}", data.size, data.handle, buffer.status);
|
||||
}
|
||||
|
||||
void NvMap::FromId(skyline::gpu::device::IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 id; // In
|
||||
u32 handle; // Out
|
||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
||||
bool found{};
|
||||
for (const auto &object : handleTable) {
|
||||
if (object.second->id == data.id) {
|
||||
data.handle = object.first;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
state.process->WriteMemory(data, buffer.output[0].address);
|
||||
else
|
||||
buffer.status = NvStatus::BadValue;
|
||||
state.logger->Debug("FromId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
|
||||
}
|
||||
|
||||
void NvMap::Alloc(IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 handle; // In
|
||||
u32 heapMask; // In
|
||||
u32 flags; // In
|
||||
u32 align; // In
|
||||
u8 kind; // In
|
||||
u8 _pad0_[7];
|
||||
u64 address; // InOut
|
||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
||||
auto &object = handleTable.at(data.handle);
|
||||
object->heapMask = data.heapMask;
|
||||
object->flags = data.flags;
|
||||
object->align = data.align;
|
||||
object->kind = data.kind;
|
||||
object->address = data.address;
|
||||
object->status = NvMapObject::Status::Allocated;
|
||||
state.logger->Debug("Alloc: Input: Handle: 0x{:X}, HeapMask: 0x{:X}, Flags: {}, Align: 0x{:X}, Kind: {}, Address: 0x{:X}, Output: Status: {}", data.handle, data.heapMask, data.flags, data.align, data.kind, data.address, buffer.status);
|
||||
}
|
||||
|
||||
void NvMap::Free(skyline::gpu::device::IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 handle; // In
|
||||
u32 _pad0_;
|
||||
u32 address; // Out
|
||||
u32 size; // Out
|
||||
u64 flags; // Out
|
||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
||||
const auto &object = handleTable.at(data.handle);
|
||||
if (object.use_count() > 1) {
|
||||
data.address = static_cast<u32>(object->address);
|
||||
data.flags = 0x0;
|
||||
} else {
|
||||
data.address = 0x0;
|
||||
data.flags = 0x1; // Not free yet
|
||||
}
|
||||
data.size = object->size;
|
||||
handleTable.erase(data.handle);
|
||||
state.process->WriteMemory(data, buffer.output[0].address);
|
||||
}
|
||||
|
||||
void NvMap::Param(IoctlData &buffer) {
|
||||
enum class Parameter : u32 { Size = 1, Alignment = 2, Base = 3, HeapMask = 4, Kind = 5, Compr = 6 }; // https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h#102
|
||||
struct Data {
|
||||
u32 handle; // In
|
||||
Parameter parameter; // In
|
||||
u32 result; // Out
|
||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
||||
auto &object = handleTable.at(data.handle);
|
||||
switch (data.parameter) {
|
||||
case Parameter::Size:
|
||||
data.result = object->size;
|
||||
break;
|
||||
case Parameter::Alignment:
|
||||
case Parameter::HeapMask:
|
||||
case Parameter::Kind: {
|
||||
if (object->status != NvMapObject::Status::Allocated)
|
||||
data.result = NvStatus::BadParameter;
|
||||
switch (data.parameter) {
|
||||
case Parameter::Alignment:
|
||||
data.result = object->align;
|
||||
break;
|
||||
case Parameter::HeapMask:
|
||||
data.result = object->heapMask;
|
||||
break;
|
||||
case Parameter::Kind:
|
||||
data.result = object->kind;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Parameter::Base:
|
||||
buffer.status = NvStatus::NotImplemented;
|
||||
break;
|
||||
case Parameter::Compr:
|
||||
buffer.status = NvStatus::NotImplemented;
|
||||
break;
|
||||
}
|
||||
state.process->WriteMemory(data, buffer.output[0].address);
|
||||
state.logger->Debug("Param: Input: Handle: 0x{:X}, Parameter: {}, Output: Result: 0x{:X}, Status: {}", data.handle, data.parameter, data.result, buffer.status);
|
||||
}
|
||||
|
||||
void NvMap::GetId(skyline::gpu::device::IoctlData &buffer) {
|
||||
struct Data {
|
||||
u32 id; // Out
|
||||
u32 handle; // In
|
||||
} data = state.process->GetObject<Data>(buffer.input[0].address);
|
||||
data.id = handleTable.at(data.handle)->id;
|
||||
state.process->WriteMemory(data, buffer.output[0].address);
|
||||
state.logger->Debug("GetId: Input: Handle: 0x{:X}, Output: ID: 0x{:X}, Status: {}", data.handle, data.id, buffer.status);
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvdevice.h"
|
||||
|
||||
namespace skyline::gpu::device {
|
||||
/**
|
||||
* @brief NvMap (/dev/nvmap) is used to map certain CPU memory as GPU memory (https://switchbrew.org/wiki/NV_services) (https://android.googlesource.com/kernel/tegra/+/refs/heads/android-tegra-flounder-3.10-marshmallow/include/linux/nvmap.h)
|
||||
*/
|
||||
class NvMap : public NvDevice {
|
||||
public:
|
||||
/**
|
||||
* @brief NvMapObject is used to hold the state of held objects
|
||||
*/
|
||||
struct NvMapObject {
|
||||
u32 id; //!< The ID of this object
|
||||
u32 size; //!< The size of this object
|
||||
u64 address{}; //!< The address of the allocation
|
||||
u32 flags{}; //!< The flag of the memory (0 = Read Only, 1 = Read-Write)
|
||||
u32 align{}; //!< The alignment of the allocation
|
||||
u32 heapMask{}; //!< This is set during Alloc and returned during Param
|
||||
u8 kind{}; //!< This is same as heapMask
|
||||
|
||||
enum class Status {
|
||||
Created, //!< The object has been created but memory has not been allocated
|
||||
Allocated //!< The object has been allocated
|
||||
} status{Status::Created}; //!< This holds the status of the object
|
||||
|
||||
/**
|
||||
* @param handle The ID of this object
|
||||
* @param size The size of the object in bytes
|
||||
*/
|
||||
NvMapObject(u32 id, u32 size);
|
||||
};
|
||||
|
||||
std::unordered_map<handle_t, std::shared_ptr<NvMapObject>> handleTable; //!< A mapping from a handle to it's corresponding NvMapObject
|
||||
handle_t handleIndex{1}; //!< This is used to keep track of the next handle to allocate
|
||||
u32 idIndex{1}; //!< This is used to keep track of the next ID to allocate
|
||||
|
||||
NvMap(const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief This creates an NvMapObject and returns an handle to it (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_CREATE)
|
||||
*/
|
||||
void Create(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This returns the handle of an NvMapObject from it's ID (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FROM_ID)
|
||||
*/
|
||||
void FromId(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This allocates memory for an NvMapObject (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_ALLOC)
|
||||
*/
|
||||
void Alloc(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This frees previously allocated memory (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_FREE)
|
||||
*/
|
||||
void Free(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This returns a particular parameter from an NvMapObject (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_PARAM)
|
||||
*/
|
||||
void Param(IoctlData &buffer);
|
||||
|
||||
/**
|
||||
* @brief This returns the ID of an NvMapObject from it's handle (https://switchbrew.org/wiki/NV_services#NVMAP_IOC_GET_ID)
|
||||
*/
|
||||
void GetId(IoctlData &buffer);
|
||||
};
|
||||
}
|
@ -1,142 +0,0 @@
|
||||
#include "display.h"
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <gpu.h>
|
||||
|
||||
namespace skyline::gpu {
|
||||
Buffer::Buffer(const DeviceState &state, u32 slot, GbpBuffer &gbpBuffer) : state(state), slot(slot), gbpBuffer(gbpBuffer), resolution{gbpBuffer.width, gbpBuffer.height} {
|
||||
if (gbpBuffer.nvmapHandle)
|
||||
nvBuffer = state.gpu->GetDevice<device::NvMap>(device::NvDeviceType::nvmap)->handleTable.at(gbpBuffer.nvmapHandle);
|
||||
else {
|
||||
auto nvmap = state.gpu->GetDevice<device::NvMap>(device::NvDeviceType::nvmap);
|
||||
for (const auto &object : nvmap->handleTable) {
|
||||
if (object.second->id == gbpBuffer.nvmapId) {
|
||||
nvBuffer = object.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!nvBuffer)
|
||||
throw exception("A QueueBuffer request has an invalid NVMap Handle ({}) and ID ({})", gbpBuffer.nvmapHandle, gbpBuffer.nvmapId);
|
||||
}
|
||||
switch (gbpBuffer.format) {
|
||||
case WINDOW_FORMAT_RGBA_8888:
|
||||
case WINDOW_FORMAT_RGBX_8888:
|
||||
bpp = sizeof(u32);
|
||||
break;
|
||||
case WINDOW_FORMAT_RGB_565:
|
||||
bpp = sizeof(u16);
|
||||
break;
|
||||
default:
|
||||
throw exception("Unknown pixel format used for FB");
|
||||
}
|
||||
}
|
||||
|
||||
u8 *Buffer::GetAddress() {
|
||||
return state.process->GetPointer<u8>(nvBuffer->address + gbpBuffer.offset);
|
||||
}
|
||||
|
||||
BufferQueue::BufferQueue(const DeviceState &state) : state(state) {}
|
||||
|
||||
void BufferQueue::RequestBuffer(Parcel &in, Parcel &out) {
|
||||
u32 slot = *reinterpret_cast<u32 *>(in.data.data() + constant::TokenLength);
|
||||
out.WriteData<u32>(1);
|
||||
out.WriteData<u32>(sizeof(GbpBuffer));
|
||||
out.WriteData<u32>(0);
|
||||
out.WriteData(queue.at(slot)->gbpBuffer);
|
||||
state.logger->Debug("RequestBuffer: Slot: {}", slot, sizeof(GbpBuffer));
|
||||
}
|
||||
|
||||
void BufferQueue::DequeueBuffer(Parcel &in, Parcel &out) {
|
||||
struct Data {
|
||||
u32 format;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 timestamps;
|
||||
u32 usage;
|
||||
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength);
|
||||
i64 slot{-1};
|
||||
while (slot == -1) {
|
||||
for (auto &buffer : queue) {
|
||||
if (buffer.second->status == BufferStatus::Free && buffer.second->resolution.width == data->width && buffer.second->resolution.height == data->height && buffer.second->gbpBuffer.usage == data->usage) {
|
||||
slot = buffer.first;
|
||||
buffer.second->status = BufferStatus::Dequeued;
|
||||
break;
|
||||
}
|
||||
}
|
||||
asm("yield");
|
||||
}
|
||||
struct {
|
||||
u32 slot;
|
||||
u32 _unk_[13];
|
||||
} output{
|
||||
.slot = static_cast<u32>(slot)
|
||||
};
|
||||
out.WriteData(output);
|
||||
state.logger->Debug("DequeueBuffer: Width: {}, Height: {}, Format: {}, Usage: {}, Timestamps: {}, Slot: {}", data->width, data->height, data->format, data->usage, data->timestamps, slot);
|
||||
}
|
||||
|
||||
void BufferQueue::QueueBuffer(Parcel &in, Parcel &out) {
|
||||
struct Data {
|
||||
u32 slot;
|
||||
u64 timestamp;
|
||||
u32 autoTimestamp;
|
||||
ARect crop;
|
||||
u32 scalingMode;
|
||||
u32 transform;
|
||||
u32 stickyTransform;
|
||||
u64 _unk0_;
|
||||
u32 swapInterval;
|
||||
Fence fence[4];
|
||||
} *data = reinterpret_cast<Data *>(in.data.data() + constant::TokenLength);
|
||||
auto buffer = queue.at(data->slot);
|
||||
buffer->status = BufferStatus::Queued;
|
||||
displayQueue.emplace(buffer);
|
||||
state.gpu->bufferEvent->Signal();
|
||||
struct {
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 _pad0_[3];
|
||||
} output{
|
||||
.width = buffer->gbpBuffer.width,
|
||||
.height = buffer->gbpBuffer.height
|
||||
};
|
||||
out.WriteData(output);
|
||||
state.logger->Debug("QueueBuffer: Timestamp: {}, Auto Timestamp: {}, Crop: [T: {}, B: {}, L: {}, R: {}], Scaling Mode: {}, Transform: {}, Sticky Transform: {}, Swap Interval: {}, Slot: {}", data->timestamp, data->autoTimestamp, data->crop.top, data->crop.bottom, data->crop.left, data->crop.right, data->scalingMode, data->transform, data->stickyTransform, data->swapInterval, data->slot);
|
||||
}
|
||||
|
||||
void BufferQueue::CancelBuffer(Parcel &parcel) {
|
||||
struct Data {
|
||||
u32 slot;
|
||||
Fence fence[4];
|
||||
} *data = reinterpret_cast<Data *>(parcel.data.data() + constant::TokenLength);
|
||||
FreeBuffer(data->slot);
|
||||
state.logger->Debug("CancelBuffer: Slot: {}", data->slot);
|
||||
}
|
||||
|
||||
void BufferQueue::SetPreallocatedBuffer(Parcel &parcel) {
|
||||
auto pointer = parcel.data.data() + constant::TokenLength;
|
||||
struct Data {
|
||||
u32 slot;
|
||||
u32 _unk0_;
|
||||
u32 length;
|
||||
u32 _pad0_;
|
||||
} *data = reinterpret_cast<Data *>(pointer);
|
||||
pointer += sizeof(Data);
|
||||
auto gbpBuffer = reinterpret_cast<GbpBuffer *>(pointer);
|
||||
queue[data->slot] = std::make_shared<Buffer>(state, data->slot, *gbpBuffer);
|
||||
state.gpu->bufferEvent->Signal();
|
||||
state.logger->Debug("SetPreallocatedBuffer: Slot: {}, Magic: 0x{:X}, Width: {}, Height: {}, Stride: {}, Format: {}, Usage: {}, Index: {}, ID: {}, Handle: {}, Offset: 0x{:X}, Block Height: {}, Size: 0x{:X}",
|
||||
data->slot,
|
||||
gbpBuffer->magic,
|
||||
gbpBuffer->width,
|
||||
gbpBuffer->height,
|
||||
gbpBuffer->stride,
|
||||
gbpBuffer->format,
|
||||
gbpBuffer->usage,
|
||||
gbpBuffer->index,
|
||||
gbpBuffer->nvmapId,
|
||||
gbpBuffer->nvmapHandle,
|
||||
gbpBuffer->offset,
|
||||
(1U << gbpBuffer->blockHeightLog2),
|
||||
gbpBuffer->size);
|
||||
}
|
||||
}
|
@ -1,171 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include <queue>
|
||||
#include <gpu/devices/nvmap.h>
|
||||
#include "parcel.h"
|
||||
|
||||
namespace skyline::gpu {
|
||||
/**
|
||||
* @brief A struct that encapsulates a resolution
|
||||
*/
|
||||
struct Resolution {
|
||||
u32 width; //!< The width component of the resolution
|
||||
u32 height; //!< The height component of the resolution
|
||||
|
||||
inline bool operator==(const Resolution &r) {
|
||||
return (width == r.width) && (height == r.height);
|
||||
}
|
||||
|
||||
inline bool operator!=(const Resolution &r) {
|
||||
return !operator==(r);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief An enumeration of all the possible display IDs (https://switchbrew.org/wiki/Display_services#DisplayName)
|
||||
*/
|
||||
enum class DisplayId : u64 {
|
||||
Default,
|
||||
External,
|
||||
Edid,
|
||||
Internal,
|
||||
Null
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A mapping from a display's name to it's displayType entry
|
||||
*/
|
||||
static const std::unordered_map<std::string, DisplayId> displayTypeMap{
|
||||
{"Default", DisplayId::Default},
|
||||
{"External", DisplayId::External},
|
||||
{"Edid", DisplayId::Edid},
|
||||
{"Internal", DisplayId::Internal},
|
||||
{"Null", DisplayId::Null},
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The status of a specific layer
|
||||
*/
|
||||
enum class LayerStatus {
|
||||
Uninitialized,
|
||||
Initialized
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The status of a specific buffer
|
||||
*/
|
||||
enum class BufferStatus {
|
||||
Free,
|
||||
Dequeued,
|
||||
Queued
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This struct holds information about the graphics buffer (https://github.com/reswitched/libtransistor/blob/0f0c36227842c344d163922fc98ee76229e9f0ee/lib/display/graphic_buffer_queue.c#L66)
|
||||
*/
|
||||
struct GbpBuffer {
|
||||
u32 magic; //!< The magic of the graphics buffer: 0x47424652
|
||||
u32 width; //!< The width of the buffer
|
||||
u32 height; //!< The height of the buffer
|
||||
u32 stride; //!< The stride of the buffer
|
||||
u32 format; //!< The format of the buffer, this corresponds to AHardwareBuffer_Format
|
||||
u32 usage; //!< The usage flags for the buffer
|
||||
u32 _pad0_;
|
||||
u32 index; //!< The index of the buffer
|
||||
u32 _pad1_[3];
|
||||
u32 nvmapId; //!< The ID of the buffer in regards to /dev/nvmap
|
||||
u32 _pad2_[8];
|
||||
u32 size; //!< The size of the buffer
|
||||
u32 _pad3_[8];
|
||||
u32 nvmapHandle; //!< The handle of the buffer in regards to /dev/nvmap
|
||||
u32 offset; //!< This is the offset of the pixel data in the GPU Buffer
|
||||
u32 _pad4_;
|
||||
u32 blockHeightLog2; //!< The log2 of the block height
|
||||
u32 _pad5_[58];
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This represents conditions for the completion of an asynchronous graphics operation
|
||||
*/
|
||||
struct Fence {
|
||||
u32 syncptId; //!< The ID of the syncpoint
|
||||
u32 syncptValue; //!< The value of the syncpoint
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This holds the state and describes a single Buffer
|
||||
*/
|
||||
class Buffer {
|
||||
public:
|
||||
const DeviceState &state; //!< The state of the device
|
||||
u32 slot; //!< The slot the buffer is in
|
||||
u32 bpp; //!< The amount of bytes per pixel
|
||||
Resolution resolution; //!< The resolution of this buffer
|
||||
GbpBuffer gbpBuffer; //!< The information about the underlying buffer
|
||||
BufferStatus status{BufferStatus::Free}; //!< The status of this buffer
|
||||
std::shared_ptr<device::NvMap::NvMapObject> nvBuffer{}; //!< A shared pointer to the buffer's nvmap object
|
||||
|
||||
/**
|
||||
* @param state The state of the device
|
||||
* @param slot The slot this buffer is in
|
||||
* @param gpBuffer The GbpBuffer object for this buffer
|
||||
*/
|
||||
Buffer(const DeviceState &state, u32 slot, GbpBuffer &gbpBuffer);
|
||||
|
||||
/**
|
||||
* @return The address of the buffer on the kernel
|
||||
*/
|
||||
u8* GetAddress();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is used to manage and queue up all display buffers to be shown
|
||||
*/
|
||||
class BufferQueue {
|
||||
private:
|
||||
const DeviceState &state; //!< The state of the device
|
||||
|
||||
public:
|
||||
std::unordered_map<u32, std::shared_ptr<Buffer>> queue; //!< A vector of shared pointers to all the queued buffers
|
||||
std::queue<std::shared_ptr<Buffer>> displayQueue; //!< A queue of all the buffers to be posted to the display
|
||||
|
||||
/**
|
||||
* @param state The state of the device
|
||||
*/
|
||||
BufferQueue(const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief This the GbpBuffer struct of the specified buffer
|
||||
*/
|
||||
void RequestBuffer(Parcel &in, Parcel &out);
|
||||
|
||||
/**
|
||||
* @brief This returns the slot of a free buffer
|
||||
*/
|
||||
void DequeueBuffer(Parcel &in, Parcel &out);
|
||||
|
||||
/**
|
||||
* @brief This queues a buffer to be displayed
|
||||
*/
|
||||
void QueueBuffer(Parcel &in, Parcel &out);
|
||||
|
||||
/**
|
||||
* @brief This removes a previously queued buffer
|
||||
*/
|
||||
void CancelBuffer(Parcel &parcel);
|
||||
|
||||
/**
|
||||
* @brief This adds a pre-existing buffer to the queue
|
||||
*/
|
||||
void SetPreallocatedBuffer(Parcel &parcel);
|
||||
|
||||
/**
|
||||
* @brief This frees a buffer which is currently queued
|
||||
* @param slotNo The slot of the buffer
|
||||
*/
|
||||
inline void FreeBuffer(u32 slotNo) {
|
||||
queue.at(slotNo)->status = BufferStatus::Free;
|
||||
}
|
||||
};
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
#include "parcel.h"
|
||||
#include <os.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
|
||||
namespace skyline::gpu {
|
||||
Parcel::Parcel(kernel::ipc::InputBuffer &buffer, const DeviceState &state) : Parcel(buffer.address, buffer.size, state) {}
|
||||
|
||||
Parcel::Parcel(u64 address, u64 size, const DeviceState &state) : state(state) {
|
||||
state.process->ReadMemory(&header, address, sizeof(ParcelHeader));
|
||||
if (size < (sizeof(ParcelHeader) + header.dataSize + header.objectsSize))
|
||||
throw exception("The size of the parcel according to the header exceeds the specified size");
|
||||
data.resize(header.dataSize);
|
||||
state.process->ReadMemory(data.data(), address + header.dataOffset, header.dataSize);
|
||||
objects.resize(header.objectsSize);
|
||||
state.process->ReadMemory(objects.data(), address + header.objectsOffset, header.objectsSize);
|
||||
}
|
||||
|
||||
Parcel::Parcel(const DeviceState &state) : state(state) {}
|
||||
|
||||
u64 Parcel::WriteParcel(kernel::ipc::OutputBuffer &buffer) {
|
||||
return WriteParcel(buffer.address, buffer.size);
|
||||
}
|
||||
|
||||
u64 Parcel::WriteParcel(u64 address, u64 maxSize) {
|
||||
header.dataSize = static_cast<u32>(data.size());
|
||||
header.dataOffset = sizeof(ParcelHeader);
|
||||
header.objectsSize = static_cast<u32>(objects.size());
|
||||
header.objectsOffset = sizeof(ParcelHeader) + data.size();
|
||||
u64 totalSize = sizeof(ParcelHeader) + header.dataSize + header.objectsSize;
|
||||
if (maxSize < totalSize)
|
||||
throw exception("The size of the parcel exceeds maxSize");
|
||||
state.process->WriteMemory(header, address);
|
||||
state.process->WriteMemory(data.data(), address + header.dataOffset, data.size());
|
||||
state.process->WriteMemory(objects.data(), address + header.objectsOffset, objects.size());
|
||||
return totalSize;
|
||||
}
|
||||
}
|
@ -1,95 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include <kernel/ipc.h>
|
||||
|
||||
namespace skyline::gpu {
|
||||
/**
|
||||
* @brief This class encapsulates a Parcel object (https://switchbrew.org/wiki/Display_services#Parcel)
|
||||
*/
|
||||
class Parcel {
|
||||
private:
|
||||
/**
|
||||
* @brief This holds the header of a parcel
|
||||
*/
|
||||
struct ParcelHeader {
|
||||
u32 dataSize;
|
||||
u32 dataOffset;
|
||||
u32 objectsSize;
|
||||
u32 objectsOffset;
|
||||
} header{};
|
||||
static_assert(sizeof(ParcelHeader) == 0x10);
|
||||
|
||||
const DeviceState &state; //!< The state of the device
|
||||
|
||||
public:
|
||||
std::vector<u8> data; //!< A vector filled with data in the parcel
|
||||
std::vector<u8> objects; //!< A vector filled with objects in the parcel
|
||||
|
||||
/**
|
||||
* @brief This constructor fills in the Parcel object with data from a IPC buffer
|
||||
* @param buffer The buffer that contains the parcel
|
||||
* @param state The state of the device
|
||||
*/
|
||||
Parcel(kernel::ipc::InputBuffer &buffer, const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief This constructor fills in the Parcel object with data from a Parcel on a remote process
|
||||
* @param address The remote address of the parcel
|
||||
* @param size The size of the parcel
|
||||
* @param state The state of the device
|
||||
*/
|
||||
Parcel(u64 address, u64 size, const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief This constructor is used to create an empty parcel then write to a process
|
||||
* @param state The state of the device
|
||||
*/
|
||||
Parcel(const DeviceState &state);
|
||||
|
||||
/**
|
||||
* @brief Writes some data to the Parcel
|
||||
* @tparam ValueType The type of the object to write
|
||||
* @param value The object to be written
|
||||
*/
|
||||
template<typename ValueType>
|
||||
void WriteData(const ValueType &value) {
|
||||
data.reserve(data.size() + sizeof(ValueType));
|
||||
auto item = reinterpret_cast<const u8 *>(&value);
|
||||
for (uint index = 0; sizeof(ValueType) > index; index++) {
|
||||
data.push_back(*item);
|
||||
item++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes an object to the Parcel
|
||||
* @tparam ValueType The type of the object to write
|
||||
* @param value The object to be written
|
||||
*/
|
||||
template<typename ValueType>
|
||||
void WriteObject(const ValueType &value) {
|
||||
objects.reserve(objects.size() + sizeof(ValueType));
|
||||
auto item = reinterpret_cast<const u8 *>(&value);
|
||||
for (uint index = 0; sizeof(ValueType) > index; index++) {
|
||||
objects.push_back(*item);
|
||||
item++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes the Parcel object into a particular output buffer on a process
|
||||
* @param buffer The buffer to write into
|
||||
* @return The total size of the message
|
||||
*/
|
||||
u64 WriteParcel(kernel::ipc::OutputBuffer &buffer);
|
||||
|
||||
/**
|
||||
* @brief Writes the Parcel object into the process's memory
|
||||
* @param address The address to write the Parcel object to
|
||||
* @param maxSize The maximum size of the Parcel
|
||||
* @return The total size of the message
|
||||
*/
|
||||
u64 WriteParcel(u64 address, u64 maxSize);
|
||||
};
|
||||
}
|
81
app/src/main/cpp/skyline/gpu/texture.cpp
Normal file
81
app/src/main/cpp/skyline/gpu/texture.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
#include <android/native_window.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include "texture.h"
|
||||
|
||||
namespace skyline::gpu {
|
||||
GuestTexture::GuestTexture(const DeviceState &state, u64 address, texture::Dimensions dimensions, texture::Format format, texture::TileMode tiling, texture::TileConfig layout) : state(state), address(address), dimensions(dimensions), format(format), tileMode(tiling), tileConfig(layout) {}
|
||||
|
||||
Texture::Texture(const DeviceState &state, std::shared_ptr<GuestTexture> guest, texture::Dimensions dimensions, texture::Format format, texture::Swizzle swizzle) : state(state), guest(guest), dimensions(dimensions), format(format), swizzle(swizzle) {
|
||||
SynchronizeHost();
|
||||
}
|
||||
|
||||
void Texture::SynchronizeHost() {
|
||||
auto texture = state.process->GetPointer<u8>(guest->address);
|
||||
auto size = format.GetSize(dimensions);
|
||||
backing.resize(size);
|
||||
auto output = reinterpret_cast<u8 *>(backing.data());
|
||||
|
||||
if (guest->tileMode == texture::TileMode::Block) {
|
||||
// Reference on Block-linear tiling: https://gist.github.com/PixelyIon/d9c35050af0ef5690566ca9f0965bc32
|
||||
constexpr auto sectorWidth = 16; // The width of a sector in bytes
|
||||
constexpr auto sectorHeight = 2; // The height of a sector in lines
|
||||
constexpr auto gobWidth = 64; // The width of a GOB in bytes
|
||||
constexpr auto gobHeight = 8; // The height of a GOB in lines
|
||||
|
||||
auto robHeight = gobHeight * guest->tileConfig.blockHeight; // The height of a single ROB (Row of Blocks) in lines
|
||||
auto surfaceHeightRobs = utils::AlignUp(dimensions.height / format.blockHeight, robHeight) / robHeight; // The height of the surface in ROBs (Row Of Blocks)
|
||||
auto robWidthBytes = utils::AlignUp((guest->tileConfig.surfaceWidth / format.blockWidth) * format.bpb, gobWidth); // The width of a ROB in bytes
|
||||
auto robWidthBlocks = robWidthBytes / gobWidth; // The width of a ROB in blocks (and GOBs because block width == 1 on the Tegra X1)
|
||||
auto robBytes = robWidthBytes * robHeight; // The size of a ROB in bytes
|
||||
auto gobYOffset = robWidthBytes * gobHeight; // The offset of the next Y-axis GOB from the current one in linear space
|
||||
|
||||
auto inputSector = texture; // The address of the input sector
|
||||
auto outputRob = output; // The address of the output block
|
||||
|
||||
for (u32 rob = 0; rob < surfaceHeightRobs; rob++) { // Every Surface contains `surfaceHeightRobs` ROBs
|
||||
auto outputBlock = outputRob; // We iterate through a block independently of the ROB
|
||||
for (u32 block = 0; block < robWidthBlocks; block++) { // Every ROB contains `surfaceWidthBlocks` Blocks
|
||||
auto outputGob = outputBlock; // We iterate through a GOB independently of the block
|
||||
for (u32 gobY = 0; gobY < guest->tileConfig.blockHeight; gobY++) { // Every Block contains `blockHeight` Y-axis GOBs
|
||||
for (u32 index = 0; index < sectorWidth * sectorHeight; index++) { // Every Y-axis GOB contains `sectorWidth * sectorHeight` sectors
|
||||
const u32 xT = ((index << 3) & 0b10000) | ((index << 1) & 0b100000); // Morton-Swizzle on the X-axis
|
||||
const u32 yT = ((index >> 1) & 0b110) | (index & 0b1); // Morton-Swizzle on the Y-axis
|
||||
std::memcpy(outputGob + (yT * robWidthBytes) + xT, inputSector, sectorWidth);
|
||||
inputSector += sectorWidth; // `sectorWidth` bytes are of sequential image data
|
||||
}
|
||||
outputGob += gobYOffset; // Increment the output GOB to the next Y-axis GOB
|
||||
}
|
||||
outputBlock += gobWidth; // Increment the output block to the next block (As Block Width = 1 GOB Width)
|
||||
}
|
||||
outputRob += robBytes; // Increment the output block to the next ROB
|
||||
}
|
||||
} else if (guest->tileMode == texture::TileMode::Pitch) {
|
||||
auto sizeLine = guest->format.GetSize(dimensions.width, 1); // The size of a single line of pixel data
|
||||
auto sizeStride = guest->format.GetSize(guest->tileConfig.pitch, 1); // The size of a single stride of pixel data
|
||||
|
||||
auto inputLine = texture; // The address of the input line
|
||||
auto outputLine = output; // The address of the output line
|
||||
|
||||
for (auto line = 0; line < dimensions.height; line++) {
|
||||
std::memcpy(outputLine, inputLine, sizeLine);
|
||||
inputLine += sizeStride;
|
||||
outputLine += sizeLine;
|
||||
}
|
||||
} else if (guest->tileMode == texture::TileMode::Linear) {
|
||||
std::memcpy(output, texture, size);
|
||||
}
|
||||
}
|
||||
|
||||
PresentationTexture::PresentationTexture(const DeviceState &state, const std::shared_ptr<GuestTexture> &guest, const texture::Dimensions &dimensions, const texture::Format &format, const std::function<void()> &releaseCallback) : releaseCallback(releaseCallback), Texture(state, guest, dimensions, format, {}) {}
|
||||
|
||||
i32 PresentationTexture::GetAndroidFormat() {
|
||||
switch (format.vkFormat) {
|
||||
case vk::Format::eR8G8B8A8Unorm:
|
||||
return WINDOW_FORMAT_RGBA_8888;
|
||||
case vk::Format::eR5G6B5UnormPack16:
|
||||
return WINDOW_FORMAT_RGB_565;
|
||||
default:
|
||||
throw exception("GetAndroidFormat: Cannot find corresponding Android surface format");
|
||||
}
|
||||
}
|
||||
}
|
266
app/src/main/cpp/skyline/gpu/texture.h
Normal file
266
app/src/main/cpp/skyline/gpu/texture.h
Normal file
@ -0,0 +1,266 @@
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace skyline {
|
||||
namespace service::hosbinder {
|
||||
class IHOSBinderDriver;
|
||||
}
|
||||
namespace gpu {
|
||||
namespace texture {
|
||||
/*
|
||||
* @brief This is used to hold the dimensions of a surface
|
||||
*/
|
||||
struct Dimensions {
|
||||
u32 width; //!< The width of the surface
|
||||
u32 height; //!< The height of the surface
|
||||
u32 depth; //!< The depth of the surface
|
||||
|
||||
constexpr Dimensions() : width(0), height(0), depth(0) {}
|
||||
|
||||
constexpr Dimensions(u32 width, u32 height) : width(width), height(height), depth(1) {}
|
||||
|
||||
constexpr Dimensions(u32 width, u32 height, u32 depth) : width(width), height(height), depth(depth) {}
|
||||
|
||||
/**
|
||||
* @return If the specified dimension is equal to this one
|
||||
*/
|
||||
constexpr inline bool operator==(const Dimensions &dimensions) {
|
||||
return (width == dimensions.width) && (height == dimensions.height) && (depth == dimensions.depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the specified dimension is not equal to this one
|
||||
*/
|
||||
constexpr inline bool operator!=(const Dimensions &dimensions) {
|
||||
return (width != dimensions.width) || (height != dimensions.height) || (depth != dimensions.depth);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This is used to hold the attributes of a texture format
|
||||
*/
|
||||
struct Format {
|
||||
u8 bpb; //!< Bytes Per Block, this is to accommodate compressed formats
|
||||
u16 blockHeight; //!< The height of a single block
|
||||
u16 blockWidth; //!< The width of a single block
|
||||
vk::Format vkFormat; //!< The underlying Vulkan type of the format
|
||||
|
||||
/**
|
||||
* @return If this is a compressed texture format or not
|
||||
*/
|
||||
inline constexpr bool IsCompressed() {
|
||||
return (blockHeight != 1) || (blockWidth != 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param width The width of the texture in pixels
|
||||
* @param height The height of the texture in pixels
|
||||
* @param depth The depth of the texture in layers
|
||||
* @return The size of the texture in bytes
|
||||
*/
|
||||
inline constexpr size_t GetSize(u32 width, u32 height, u32 depth = 1) {
|
||||
return (((width / blockWidth) * (height / blockHeight)) * bpb) * depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dimensions The dimensions of a texture
|
||||
* @return The size of the texture in bytes
|
||||
*/
|
||||
inline constexpr size_t GetSize(Dimensions dimensions) {
|
||||
return GetSize(dimensions.width, dimensions.height, dimensions.depth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the specified format is equal to this one
|
||||
*/
|
||||
inline constexpr bool operator==(const Format &format) {
|
||||
return vkFormat == format.vkFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If the specified format is not equal to this one
|
||||
*/
|
||||
inline constexpr bool operator!=(const Format &format) {
|
||||
return vkFormat != format.vkFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If this format is actually valid or not
|
||||
*/
|
||||
inline constexpr operator bool() {
|
||||
return bpb;
|
||||
}
|
||||
};
|
||||
|
||||
namespace format {
|
||||
constexpr Format RGBA8888Unorm{sizeof(u8) * 4, 1, 1, vk::Format::eR8G8B8A8Unorm}; //!< 8-bits per channel 4-channel pixels
|
||||
constexpr Format RGB565Unorm{sizeof(u8) * 2, 1, 1, vk::Format::eR5G6B5UnormPack16}; //!< Red channel: 5-bit, Green channel: 6-bit, Blue channel: 5-bit
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This describes the linearity of a texture. Refer to Chapter 20.1 of the Tegra X1 TRM for information.
|
||||
*/
|
||||
enum class TileMode {
|
||||
Linear, //!< This is a purely linear texture
|
||||
Pitch, //!< This is a pitch-linear texture
|
||||
Block, //!< This is a 16Bx2 block-linear texture
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This holds the parameters of the tiling mode, covered in Table 76 in the Tegra X1 TRM
|
||||
*/
|
||||
union TileConfig {
|
||||
struct {
|
||||
u8 blockHeight; //!< The height of the blocks in GOBs
|
||||
u8 blockDepth; //!< The depth of the blocks in GOBs
|
||||
u16 surfaceWidth; //!< The width of a surface in samples
|
||||
};
|
||||
u32 pitch; //!< The pitch of the texture if it's pitch linear
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This enumerates all of the channel swizzle options
|
||||
*/
|
||||
enum class SwizzleChannel {
|
||||
Zero, //!< Write 0 to the channel
|
||||
One, //!< Write 1 to the channel
|
||||
Red, //!< Red color channel
|
||||
Green, //!< Green color channel
|
||||
Blue, //!< Blue color channel
|
||||
Alpha, //!< Alpha channel
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This holds all of the texture swizzles on each color channel
|
||||
*/
|
||||
struct Swizzle {
|
||||
SwizzleChannel red{SwizzleChannel::Red}; //!< Swizzle for the red channel
|
||||
SwizzleChannel green{SwizzleChannel::Green}; //!< Swizzle for the green channel
|
||||
SwizzleChannel blue{SwizzleChannel::Blue}; //!< Swizzle for the blue channel
|
||||
SwizzleChannel alpha{SwizzleChannel::Alpha}; //!< Swizzle for the alpha channel
|
||||
};
|
||||
}
|
||||
|
||||
class Texture;
|
||||
class PresentationTexture;
|
||||
|
||||
/**
|
||||
* @brief This class is used to hold metadata about a guest texture and can be used to create a host Texture object
|
||||
*/
|
||||
class GuestTexture : public std::enable_shared_from_this<GuestTexture> {
|
||||
private:
|
||||
const DeviceState &state; //!< The state of the device
|
||||
|
||||
public:
|
||||
u64 address; //!< The address of the texture in guest memory
|
||||
std::shared_ptr<Texture> host; //!< The corresponding host texture object
|
||||
texture::Dimensions dimensions; //!< The dimensions of the texture
|
||||
texture::Format format; //!< The format of the texture
|
||||
texture::TileMode tileMode; //!< The tiling mode of the texture
|
||||
texture::TileConfig tileConfig; //!< The tiling configuration of the texture
|
||||
|
||||
GuestTexture(const DeviceState &state, u64 address, texture::Dimensions dimensions, texture::Format format, texture::TileMode tileMode = texture::TileMode::Linear, texture::TileConfig tileConfig = {});
|
||||
|
||||
inline constexpr size_t Size() {
|
||||
return format.GetSize(dimensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief This creates a corresponding host texture object for this guest texture
|
||||
* @param format The format of the host texture (Defaults to the format of the guest texture)
|
||||
* @param dimensions The dimensions of the host texture (Defaults to the dimensions of the host texture)
|
||||
* @param swizzle The channel swizzle of the host texture (Defaults to no channel swizzling)
|
||||
* @return A shared pointer to the host texture object
|
||||
* @note There can only be one host texture for a corresponding guest texture
|
||||
*/
|
||||
std::shared_ptr<Texture> InitializeTexture(std::optional<texture::Format> format = std::nullopt, std::optional<texture::Dimensions> dimensions = std::nullopt, texture::Swizzle swizzle = {}) {
|
||||
if (host)
|
||||
throw exception("Trying to create multiple Texture objects from a single GuestTexture");
|
||||
host = std::make_shared<Texture>(state, shared_from_this(), dimensions ? *dimensions : this->dimensions, format ? *format : this->format, swizzle);
|
||||
return host;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<PresentationTexture> InitializePresentationTexture() {
|
||||
if (host)
|
||||
throw exception("Trying to create multiple PresentationTexture objects from a single GuestTexture");
|
||||
auto presentation = std::make_shared<PresentationTexture>(state, shared_from_this(), dimensions, format);
|
||||
host = std::static_pointer_cast<Texture>(presentation);
|
||||
return presentation;
|
||||
}
|
||||
|
||||
friend service::hosbinder::IHOSBinderDriver;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class is used to store a texture which is backed by host objects
|
||||
*/
|
||||
class Texture {
|
||||
private:
|
||||
const DeviceState &state; //!< The state of the device
|
||||
|
||||
public:
|
||||
std::vector<u8> backing; //!< The object that holds a host copy of the guest texture (Will be replaced with a vk::Image)
|
||||
std::shared_ptr<GuestTexture> guest; //!< The corresponding guest texture object
|
||||
texture::Dimensions dimensions; //!< The dimensions of the texture
|
||||
texture::Format format; //!< The format of the host texture
|
||||
texture::Swizzle swizzle; //!< The swizzle of the host texture
|
||||
|
||||
public:
|
||||
Texture(const DeviceState &state, std::shared_ptr<GuestTexture> guest, texture::Dimensions dimensions, texture::Format format, texture::Swizzle swizzle);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief This convert this texture to the specified tiling mode
|
||||
* @param tileMode The tiling mode to convert it to
|
||||
* @param tileConfig The configuration for the tiling mode (Can be default argument for Linear)
|
||||
*/
|
||||
void ConvertTileMode(texture::TileMode tileMode, texture::TileConfig tileConfig = {});
|
||||
|
||||
/**
|
||||
* @brief This sets the texture dimensions to the specified ones (As long as they are within the GuestTexture's range)
|
||||
* @param dimensions The dimensions to adjust the texture to
|
||||
*/
|
||||
void SetDimensions(texture::Dimensions dimensions);
|
||||
|
||||
/**
|
||||
* @brief This sets the format to the specified one
|
||||
* @param format The format to change the texture to
|
||||
*/
|
||||
void SetFormat(texture::Format format);
|
||||
|
||||
/**
|
||||
* @brief This sets the channel swizzle to the specified one
|
||||
* @param swizzle The channel swizzle to the change the texture to
|
||||
*/
|
||||
void SetSwizzle(texture::Swizzle swizzle);
|
||||
|
||||
/**
|
||||
* @brief This synchronizes the host texture with the guest after it has been modified
|
||||
*/
|
||||
void SynchronizeHost();
|
||||
|
||||
/**
|
||||
* @brief This synchronizes the guest texture with the host texture after it has been modified
|
||||
*/
|
||||
void SynchronizeGuest();
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This class is used to hold a texture object alongside a release callback used for display presentation
|
||||
*/
|
||||
class PresentationTexture : public Texture {
|
||||
public:
|
||||
std::function<void()> releaseCallback; //!< The release callback after this texture has been displayed
|
||||
|
||||
PresentationTexture(const DeviceState &state, const std::shared_ptr<GuestTexture> &guest, const texture::Dimensions &dimensions, const texture::Format &format, const std::function<void()> &releaseCallback = {});
|
||||
|
||||
/**
|
||||
* @return The corresponding Android surface format for the current texture format
|
||||
*/
|
||||
i32 GetAndroidFormat();
|
||||
};
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user