mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 17:54:16 +01:00
Address review comments
This commit is contained in:
parent
ae131502c6
commit
e5264f7762
@ -26,6 +26,7 @@ namespace skyline::gpu {
|
||||
|
||||
void GPU::Loop() {
|
||||
gpfifo.Run();
|
||||
vsyncEvent->Signal();
|
||||
|
||||
if (surfaceUpdate) {
|
||||
if (Surface == nullptr)
|
||||
|
@ -26,7 +26,7 @@ namespace skyline::gpu {
|
||||
u16 method;
|
||||
u32 argument;
|
||||
u32 subChannel;
|
||||
bool lastCall; //!< Whether this is the last call in the pushbuffer entry to this specifc macro
|
||||
bool lastCall; //!< If this is the last call in the pushbuffer entry to this specific macro
|
||||
};
|
||||
|
||||
namespace engine {
|
||||
|
@ -9,7 +9,7 @@
|
||||
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
constexpr u32 GpfifoRegisterSize = 0x40; //!< The size of the GPFIFO's register space in units of u32
|
||||
constexpr u32 GpfifoRegisterCount = 0x40; //!< The number of GPFIFO registers
|
||||
}
|
||||
|
||||
namespace gpu::engine {
|
||||
@ -23,8 +23,11 @@ namespace skyline {
|
||||
* @brief This holds the GPFIFO engine's registers
|
||||
* @url https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/clb06f.h#L65
|
||||
*/
|
||||
union Regs {
|
||||
enum class SemaphoreOperation {
|
||||
#pragma pack(push, 1)
|
||||
union Registers {
|
||||
std::array<u32, constant::GpfifoRegisterCount> raw;
|
||||
|
||||
enum class SemaphoreOperation : u8 {
|
||||
Acquire = 1,
|
||||
Release = 2,
|
||||
AcqGeq = 4,
|
||||
@ -32,22 +35,22 @@ namespace skyline {
|
||||
Reduction = 16
|
||||
};
|
||||
|
||||
enum class SemaphoreAcquireSwitch {
|
||||
enum class SemaphoreAcquireSwitch : u8 {
|
||||
Disabled = 0,
|
||||
Enabled = 1
|
||||
};
|
||||
|
||||
enum class SemaphoreReleaseWfi {
|
||||
enum class SemaphoreReleaseWfi : u8 {
|
||||
En = 0,
|
||||
Dis = 1
|
||||
};
|
||||
|
||||
enum class SemaphoreReleaseSize {
|
||||
enum class SemaphoreReleaseSize : u8 {
|
||||
SixteenBytes = 0,
|
||||
FourBytes = 1
|
||||
};
|
||||
|
||||
enum class SemaphoreReduction {
|
||||
enum class SemaphoreReduction : u8 {
|
||||
Min = 0,
|
||||
Max = 1,
|
||||
Xor = 2,
|
||||
@ -58,32 +61,32 @@ namespace skyline {
|
||||
Dec = 7
|
||||
};
|
||||
|
||||
enum class SemaphoreFormat {
|
||||
enum class SemaphoreFormat : u8 {
|
||||
Signed = 0,
|
||||
Unsigned = 1
|
||||
};
|
||||
|
||||
enum class MemOpTlbInvalidatePdb {
|
||||
enum class MemOpTlbInvalidatePdb : u8 {
|
||||
One = 0,
|
||||
All = 1
|
||||
};
|
||||
|
||||
enum class SyncpointOperation {
|
||||
enum class SyncpointOperation : u8 {
|
||||
Wait = 0,
|
||||
Incr = 1
|
||||
};
|
||||
|
||||
enum class SyncpointWaitSwitch {
|
||||
enum class SyncpointWaitSwitch : u8 {
|
||||
Dis = 0,
|
||||
En = 1
|
||||
};
|
||||
|
||||
enum class WfiScope {
|
||||
enum class WfiScope : u8 {
|
||||
CurrentScgType = 0,
|
||||
All = 1
|
||||
};
|
||||
|
||||
enum class YieldOp {
|
||||
enum class YieldOp : u8 {
|
||||
Nop = 0,
|
||||
PbdmaTimeslice = 1,
|
||||
RunlistTimeslice = 2,
|
||||
@ -93,8 +96,8 @@ namespace skyline {
|
||||
struct {
|
||||
struct {
|
||||
u16 nvClass : 16;
|
||||
u16 engine : 5;
|
||||
u32 _pad_ : 11;
|
||||
u8 engine : 5;
|
||||
u16 _pad_ : 11;
|
||||
} setObject;
|
||||
|
||||
u32 illegal;
|
||||
@ -114,7 +117,7 @@ namespace skyline {
|
||||
|
||||
u32 payload;
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
SemaphoreOperation operation : 5;
|
||||
u8 _pad2_ : 7;
|
||||
SemaphoreAcquireSwitch acquireSwitch : 1;
|
||||
@ -140,7 +143,7 @@ namespace skyline {
|
||||
struct {
|
||||
u32 payload;
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
SyncpointOperation operation : 1;
|
||||
u8 _pad0_ : 3;
|
||||
SyncpointWaitSwitch waitSwitch : 1;
|
||||
@ -162,9 +165,9 @@ namespace skyline {
|
||||
u32 _pad_ : 30;
|
||||
} yield;
|
||||
};
|
||||
std::array<u32, constant::GpfifoRegisterSize> raw;
|
||||
} regs{};
|
||||
static_assert(sizeof(Regs) == (constant::GpfifoRegisterSize << 2));
|
||||
} registers{};
|
||||
static_assert(sizeof(Registers) == (constant::GpfifoRegisterCount * sizeof(u32)));
|
||||
#pragma pack(pop)
|
||||
|
||||
public:
|
||||
GPFIFO(const DeviceState &state) : Engine(state) {}
|
||||
@ -172,7 +175,7 @@ namespace skyline {
|
||||
void CallMethod(MethodParams params) {
|
||||
state.logger->Debug("Called method in GPFIFO: 0x{:X} args: 0x{:X}", params.method, params.argument);
|
||||
|
||||
regs.raw[params.method] = params.argument;
|
||||
registers.raw[params.method] = params.argument;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -11,72 +11,76 @@ namespace skyline::gpu::engine {
|
||||
}
|
||||
|
||||
void Maxwell3D::ResetRegs() {
|
||||
memset(®s, 0, sizeof(regs));
|
||||
registers = {};
|
||||
|
||||
regs.rasterizerEnable = true;
|
||||
registers.rasterizerEnable = true;
|
||||
|
||||
for (auto &transform : regs.viewportTransform) {
|
||||
transform.swizzles.x = Regs::ViewportTransform::Swizzle::PositiveX;
|
||||
transform.swizzles.y = Regs::ViewportTransform::Swizzle::PositiveY;
|
||||
transform.swizzles.z = Regs::ViewportTransform::Swizzle::PositiveZ;
|
||||
transform.swizzles.w = Regs::ViewportTransform::Swizzle::PositiveW;
|
||||
for (auto &transform : registers.viewportTransform) {
|
||||
transform.swizzles.x = Registers::ViewportTransform::Swizzle::PositiveX;
|
||||
transform.swizzles.y = Registers::ViewportTransform::Swizzle::PositiveY;
|
||||
transform.swizzles.z = Registers::ViewportTransform::Swizzle::PositiveZ;
|
||||
transform.swizzles.w = Registers::ViewportTransform::Swizzle::PositiveW;
|
||||
}
|
||||
|
||||
for (auto &viewport : regs.viewport) {
|
||||
for (auto &viewport : registers.viewport) {
|
||||
viewport.depthRangeFar = 1.0f;
|
||||
viewport.depthRangeNear = 0.0f;
|
||||
}
|
||||
|
||||
regs.polygonMode.front = Regs::PolygonMode::Fill;
|
||||
regs.polygonMode.back = Regs::PolygonMode::Fill;
|
||||
registers.polygonMode.front = Registers::PolygonMode::Fill;
|
||||
registers.polygonMode.back = Registers::PolygonMode::Fill;
|
||||
|
||||
regs.stencilFront.failOp = regs.stencilFront.zFailOp = regs.stencilFront.zPassOp = Regs::StencilOp::Keep;
|
||||
regs.stencilFront.func.op = Regs::CompareOp::Always;
|
||||
regs.stencilFront.func.mask = 0xFFFFFFFF;
|
||||
regs.stencilFront.mask = 0xFFFFFFFF;
|
||||
registers.stencilFront.failOp = registers.stencilFront.zFailOp = registers.stencilFront.zPassOp = Registers::StencilOp::Keep;
|
||||
registers.stencilFront.compare.op = Registers::CompareOp::Always;
|
||||
registers.stencilFront.compare.mask = 0xFFFFFFFF;
|
||||
registers.stencilFront.writeMask = 0xFFFFFFFF;
|
||||
|
||||
regs.stencilBack.stencilTwoSideEnable = true;
|
||||
regs.stencilBack.failOp = regs.stencilBack.zFailOp = regs.stencilBack.zPassOp = Regs::StencilOp::Keep;
|
||||
regs.stencilBack.funcOp = Regs::CompareOp::Always;
|
||||
regs.stencilBackExtra.funcMask = 0xFFFFFFFF;
|
||||
regs.stencilBackExtra.mask = 0xFFFFFFFF;
|
||||
registers.stencilTwoSideEnable = true;
|
||||
registers.stencilBack.failOp = registers.stencilBack.zFailOp = registers.stencilBack.zPassOp = Registers::StencilOp::Keep;
|
||||
registers.stencilBack.compareOp = Registers::CompareOp::Always;
|
||||
registers.stencilBackExtra.compareMask = 0xFFFFFFFF;
|
||||
registers.stencilBackExtra.writeMask = 0xFFFFFFFF;
|
||||
|
||||
regs.rtSeparateFragData = true;
|
||||
registers.rtSeparateFragData = true;
|
||||
|
||||
for (auto &attribute : regs.vertexAttributeState)
|
||||
for (auto &attribute : registers.vertexAttributeState)
|
||||
attribute.fixed = true;
|
||||
|
||||
regs.depthTestFunc = Regs::CompareOp::Always;
|
||||
registers.depthTestFunc = Registers::CompareOp::Always;
|
||||
|
||||
regs.blend.colorOp = regs.blend.alphaOp = Regs::Blend::Op::Add;
|
||||
regs.blend.colorSrcFactor = regs.blend.alphaSrcFactor = Regs::Blend::Factor::One;
|
||||
regs.blend.colorDestFactor = regs.blend.alphaDestFactor = Regs::Blend::Factor::Zero;
|
||||
registers.blend.colorOp = registers.blend.alphaOp = Registers::Blend::Op::Add;
|
||||
registers.blend.colorSrcFactor = registers.blend.alphaSrcFactor = Registers::Blend::Factor::One;
|
||||
registers.blend.colorDestFactor = registers.blend.alphaDestFactor = Registers::Blend::Factor::Zero;
|
||||
|
||||
regs.lineWidthSmooth = 1.0f;
|
||||
regs.lineWidthAliased = 1.0f;
|
||||
registers.lineWidthSmooth = 1.0f;
|
||||
registers.lineWidthAliased = 1.0f;
|
||||
|
||||
regs.pointSpriteSize = 1.0f;
|
||||
registers.pointSpriteEnable = true;
|
||||
registers.pointSpriteSize = 1.0f;
|
||||
registers.pointCoordReplace.enable = true;
|
||||
|
||||
regs.frontFace = Regs::FrontFace::CounterClockwise;
|
||||
regs.cullFace = Regs::CullFace::Back;
|
||||
registers.frontFace = Registers::FrontFace::CounterClockwise;
|
||||
registers.cullFace = Registers::CullFace::Back;
|
||||
|
||||
for (auto &mask : regs.colorMask)
|
||||
for (auto &mask : registers.colorMask)
|
||||
mask.r = mask.g = mask.b = mask.a = 1;
|
||||
|
||||
for (auto &blend : regs.independentBlend) {
|
||||
blend.colorOp = blend.alphaOp = Regs::Blend::Op::Add;
|
||||
blend.colorSrcFactor = blend.alphaSrcFactor = Regs::Blend::Factor::One;
|
||||
blend.colorDestFactor = blend.alphaDestFactor = Regs::Blend::Factor::Zero;
|
||||
for (auto &blend : registers.independentBlend) {
|
||||
blend.colorOp = blend.alphaOp = Registers::Blend::Op::Add;
|
||||
blend.colorSrcFactor = blend.alphaSrcFactor = Registers::Blend::Factor::One;
|
||||
blend.colorDestFactor = blend.alphaDestFactor = Registers::Blend::Factor::Zero;
|
||||
}
|
||||
|
||||
registers.viewportTransformEnable = true;
|
||||
}
|
||||
|
||||
void Maxwell3D::CallMethod(MethodParams params) {
|
||||
state.logger->Debug("Called method in Maxwell 3D: 0x{:X} args: 0x{:X}", params.method, params.argument);
|
||||
|
||||
// Methods that are greater than the register size are for macro control
|
||||
if (params.method > constant::Maxwell3DRegisterSize) {
|
||||
if (params.method > constant::Maxwell3DRegisterCounter) {
|
||||
if (!(params.method & 1))
|
||||
macroInvocation.index = ((params.method - constant::Maxwell3DRegisterSize) >> 1) % macroPositions.size();
|
||||
macroInvocation.index = ((params.method - constant::Maxwell3DRegisterCounter) >> 1) % macroPositions.size();
|
||||
|
||||
macroInvocation.arguments.push_back(params.argument);
|
||||
|
||||
@ -90,58 +94,58 @@ namespace skyline::gpu::engine {
|
||||
return;
|
||||
}
|
||||
|
||||
regs.raw[params.method] = params.argument;
|
||||
registers.raw[params.method] = params.argument;
|
||||
|
||||
if (shadowRegs.mme.shadowRamControl == Regs::MmeShadowRamControl::MethodTrack || shadowRegs.mme.shadowRamControl == Regs::MmeShadowRamControl::MethodTrackWithFilter)
|
||||
shadowRegs.raw[params.method] = params.argument;
|
||||
else if (shadowRegs.mme.shadowRamControl == Regs::MmeShadowRamControl::MethodReplay)
|
||||
params.argument = shadowRegs.raw[params.method];
|
||||
if (shadowRegisters.mme.shadowRamControl == Registers::MmeShadowRamControl::MethodTrack || shadowRegisters.mme.shadowRamControl == Registers::MmeShadowRamControl::MethodTrackWithFilter)
|
||||
shadowRegisters.raw[params.method] = params.argument;
|
||||
else if (shadowRegisters.mme.shadowRamControl == Registers::MmeShadowRamControl::MethodReplay)
|
||||
params.argument = shadowRegisters.raw[params.method];
|
||||
|
||||
switch (params.method) {
|
||||
case MAXWELL3D_OFFSET(mme.instructionRamLoad):
|
||||
if (regs.mme.instructionRamPointer >= macroCode.size())
|
||||
if (registers.mme.instructionRamPointer >= macroCode.size())
|
||||
throw exception("Macro memory is full!");
|
||||
|
||||
macroCode[regs.mme.instructionRamPointer++] = params.argument;
|
||||
macroCode[registers.mme.instructionRamPointer++] = params.argument;
|
||||
break;
|
||||
case MAXWELL3D_OFFSET(mme.startAddressRamLoad):
|
||||
if (regs.mme.startAddressRamPointer >= macroPositions.size())
|
||||
if (registers.mme.startAddressRamPointer >= macroPositions.size())
|
||||
throw exception("Maximum amount of macros reached!");
|
||||
|
||||
macroPositions[regs.mme.startAddressRamPointer++] = params.argument;
|
||||
macroPositions[registers.mme.startAddressRamPointer++] = params.argument;
|
||||
break;
|
||||
case MAXWELL3D_OFFSET(mme.shadowRamControl):
|
||||
shadowRegs.mme.shadowRamControl = static_cast<Regs::MmeShadowRamControl>(params.argument);
|
||||
shadowRegisters.mme.shadowRamControl = static_cast<Registers::MmeShadowRamControl>(params.argument);
|
||||
break;
|
||||
case MAXWELL3D_OFFSET(syncpointAction):
|
||||
state.gpu->syncpoints.at(regs.syncpointAction.id).Increment();
|
||||
state.gpu->syncpoints.at(registers.syncpointAction.id).Increment();
|
||||
break;
|
||||
case MAXWELL3D_OFFSET(semaphore.info):
|
||||
switch (regs.semaphore.info.op) {
|
||||
case Regs::SemaphoreInfo::Op::Release:
|
||||
WriteSemaphoreResult(regs.semaphore.payload);
|
||||
switch (registers.semaphore.info.op) {
|
||||
case Registers::SemaphoreInfo::Op::Release:
|
||||
WriteSemaphoreResult(registers.semaphore.payload);
|
||||
break;
|
||||
case Regs::SemaphoreInfo::Op::Counter:
|
||||
case Registers::SemaphoreInfo::Op::Counter:
|
||||
HandleSemaphoreCounterOperation();
|
||||
break;
|
||||
default:
|
||||
state.logger->Warn("Unsupported semaphore operation: 0x{:X}", static_cast<u8>(regs.semaphore.info.op));
|
||||
state.logger->Warn("Unsupported semaphore operation: 0x{:X}", static_cast<u8>(registers.semaphore.info.op));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case MAXWELL3D_OFFSET(firmwareCall[4]):
|
||||
regs.raw[0xd00] = 1;
|
||||
registers.raw[0xd00] = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Maxwell3D::HandleSemaphoreCounterOperation() {
|
||||
switch (regs.semaphore.info.counterType) {
|
||||
case Regs::SemaphoreInfo::CounterType::Zero:
|
||||
switch (registers.semaphore.info.counterType) {
|
||||
case Registers::SemaphoreInfo::CounterType::Zero:
|
||||
WriteSemaphoreResult(0);
|
||||
break;
|
||||
default:
|
||||
state.logger->Warn("Unsupported semaphore counter type: 0x{:X}", static_cast<u8>(regs.semaphore.info.counterType));
|
||||
state.logger->Warn("Unsupported semaphore counter type: 0x{:X}", static_cast<u8>(registers.semaphore.info.counterType));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -152,11 +156,11 @@ namespace skyline::gpu::engine {
|
||||
u64 timestamp;
|
||||
};
|
||||
|
||||
switch (regs.semaphore.info.structureSize) {
|
||||
case Regs::SemaphoreInfo::StructureSize::OneWord:
|
||||
state.gpu->memoryManager.Write<u32>(static_cast<u32>(result), regs.semaphore.address.Pack());
|
||||
switch (registers.semaphore.info.structureSize) {
|
||||
case Registers::SemaphoreInfo::StructureSize::OneWord:
|
||||
state.gpu->memoryManager.Write<u32>(static_cast<u32>(result), registers.semaphore.address.Pack());
|
||||
break;
|
||||
case Regs::SemaphoreInfo::StructureSize::FourWords: {
|
||||
case Registers::SemaphoreInfo::StructureSize::FourWords: {
|
||||
// Convert the current nanosecond time to GPU ticks
|
||||
constexpr u64 NsToTickNumerator = 384;
|
||||
constexpr u64 NsToTickDenominator = 625;
|
||||
@ -164,7 +168,7 @@ namespace skyline::gpu::engine {
|
||||
u64 nsTime = util::GetTimeNs();
|
||||
u64 timestamp = (nsTime / NsToTickDenominator) * NsToTickNumerator + ((nsTime % NsToTickDenominator) * NsToTickNumerator) / NsToTickDenominator;
|
||||
|
||||
state.gpu->memoryManager.Write<FourWordResult>(FourWordResult{result, timestamp}, regs.semaphore.address.Pack());
|
||||
state.gpu->memoryManager.Write<FourWordResult>(FourWordResult{result, timestamp}, registers.semaphore.address.Pack());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,11 @@
|
||||
#include <gpu/macro_interpreter.h>
|
||||
#include "engine.h"
|
||||
|
||||
#define MAXWELL3D_OFFSET(field) U32_OFFSET(skyline::gpu::engine::Maxwell3D::Regs, field)
|
||||
#define MAXWELL3D_OFFSET(field) U32_OFFSET(skyline::gpu::engine::Maxwell3D::Registers, field)
|
||||
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
constexpr u32 Maxwell3DRegisterSize = 0xe00; //!< The size of the GPFIFO's register space in units of u32
|
||||
constexpr u32 Maxwell3DRegisterCounter = 0xe00; //!< The number of Maxwell 3D registers
|
||||
}
|
||||
|
||||
namespace gpu::engine {
|
||||
@ -40,7 +40,10 @@ namespace skyline {
|
||||
* @brief This holds the Maxwell3D engine's register space
|
||||
* @url https://github.com/devkitPro/deko3d/blob/master/source/maxwell/engine_3d.def#L478
|
||||
*/
|
||||
union Regs {
|
||||
#pragma pack(push, 1)
|
||||
union Registers {
|
||||
std::array<u32, constant::Maxwell3DRegisterCounter> raw;
|
||||
|
||||
struct Address {
|
||||
u32 high;
|
||||
u32 low;
|
||||
@ -77,7 +80,7 @@ namespace skyline {
|
||||
float translateY;
|
||||
float translateZ;
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
Swizzle x : 3;
|
||||
u8 _pad0_ : 1;
|
||||
Swizzle y : 3;
|
||||
@ -88,7 +91,7 @@ namespace skyline {
|
||||
u32 _pad3_ : 17;
|
||||
} swizzles;
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
u8 x : 5;
|
||||
u8 _pad0_ : 3;
|
||||
u8 y : 5;
|
||||
@ -120,6 +123,8 @@ namespace skyline {
|
||||
};
|
||||
|
||||
union VertexAttribute {
|
||||
u32 raw;
|
||||
|
||||
enum class Size : u8 {
|
||||
Size_1x32 = 0x12,
|
||||
Size_2x32 = 0x04,
|
||||
@ -148,7 +153,7 @@ namespace skyline {
|
||||
Float = 7,
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
u8 bufferId : 5;
|
||||
u8 _pad0_ : 1;
|
||||
bool fixed : 1;
|
||||
@ -158,8 +163,6 @@ namespace skyline {
|
||||
u8 _pad1_ : 1;
|
||||
bool bgra : 1;
|
||||
};
|
||||
|
||||
u32 raw;
|
||||
};
|
||||
static_assert(sizeof(VertexAttribute) == sizeof(u32));
|
||||
|
||||
@ -275,19 +278,19 @@ namespace skyline {
|
||||
FrontAndBack = 0x408,
|
||||
};
|
||||
|
||||
union ColorMask {
|
||||
struct __attribute__((__packed__)) {
|
||||
union ColorWriteMask {
|
||||
u32 raw;
|
||||
|
||||
struct {
|
||||
u8 r : 4;
|
||||
u8 g : 4;
|
||||
u8 b : 4;
|
||||
u8 a : 4;
|
||||
};
|
||||
|
||||
u32 raw;
|
||||
};
|
||||
static_assert(sizeof(ColorMask) == sizeof(u32));
|
||||
static_assert(sizeof(ColorWriteMask) == sizeof(u32));
|
||||
|
||||
struct __attribute__((__packed__)) SemaphoreInfo {
|
||||
struct SemaphoreInfo {
|
||||
enum class Op : u8 {
|
||||
Release = 0,
|
||||
Acquire = 1,
|
||||
@ -372,6 +375,11 @@ namespace skyline {
|
||||
};
|
||||
static_assert(sizeof(SemaphoreInfo) == sizeof(u32));
|
||||
|
||||
enum class CoordOrigin : u8 {
|
||||
LowerLeft = 0,
|
||||
UpperLeft = 1
|
||||
};
|
||||
|
||||
struct {
|
||||
u32 _pad0_[0x40]; // 0x0
|
||||
u32 noOperation; // 0x40
|
||||
@ -412,9 +420,9 @@ namespace skyline {
|
||||
u32 _pad6_[0x68]; // 0x36d
|
||||
|
||||
struct {
|
||||
u32 funcRef; // 0x3d5
|
||||
u32 mask; // 0x3d6
|
||||
u32 funcMask; // 0x3d7
|
||||
u32 compareRef; // 0x3d5
|
||||
u32 writeMask; // 0x3d6
|
||||
u32 compareMask; // 0x3d7
|
||||
} stencilBackExtra;
|
||||
|
||||
u32 _pad7_[0x13]; // 0x3d8
|
||||
@ -461,9 +469,9 @@ namespace skyline {
|
||||
CompareOp op; // 0x4e4
|
||||
i32 ref; // 0x4e5
|
||||
u32 mask; // 0x4e6
|
||||
} func;
|
||||
} compare;
|
||||
|
||||
u32 mask; // 0x4e7
|
||||
u32 writeMask; // 0x4e7
|
||||
} stencilFront;
|
||||
|
||||
u32 _pad11_[0x4]; // 0x4e8
|
||||
@ -484,7 +492,7 @@ namespace skyline {
|
||||
u32 multisampleEnable; // 0x54d
|
||||
u32 depthTargetEnable; // 0x54e
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
bool alphaToCoverage : 1;
|
||||
u8 _pad0_ : 3;
|
||||
bool alphaToOne : 1;
|
||||
@ -509,22 +517,34 @@ namespace skyline {
|
||||
|
||||
u32 _pad18_[0x5]; // 0x560
|
||||
|
||||
struct {
|
||||
u32 stencilTwoSideEnable; // 0x565
|
||||
|
||||
struct {
|
||||
StencilOp failOp; // 0x566
|
||||
StencilOp zFailOp; // 0x567
|
||||
StencilOp zPassOp; // 0x568
|
||||
CompareOp funcOp; // 0x569
|
||||
CompareOp compareOp; // 0x569
|
||||
} stencilBack;
|
||||
|
||||
u32 _pad19_[0xdc]; // 0x56a
|
||||
u32 _pad19_[0x17]; // 0x56a
|
||||
|
||||
struct {
|
||||
u8 _unk_ : 2;
|
||||
CoordOrigin origin : 1;
|
||||
u16 enable : 10;
|
||||
u32 _pad_ : 19;
|
||||
} pointCoordReplace; // 0x581
|
||||
|
||||
u32 _pad20_[0xc4]; // 0x582
|
||||
u32 cullFaceEnable; // 0x646
|
||||
FrontFace frontFace; // 0x647
|
||||
CullFace cullFace; // 0x648
|
||||
u32 pixelCentreImage; // 0x649
|
||||
u32 _pad20_[0x36]; // 0x64a
|
||||
std::array<ColorMask, 8> colorMask; // 0x680 For each render target
|
||||
u32 _pad21_[0x38]; // 0x688
|
||||
u32 _pad21_; // 0x64a
|
||||
u32 viewportTransformEnable; // 0x64b
|
||||
u32 _pad22_[0x34]; // 0x64a
|
||||
std::array<ColorWriteMask, 8> colorMask; // 0x680 For each render target
|
||||
u32 _pad23_[0x38]; // 0x688
|
||||
|
||||
struct {
|
||||
Address address; // 0x6c0
|
||||
@ -532,18 +552,17 @@ namespace skyline {
|
||||
SemaphoreInfo info; // 0x6c3
|
||||
} semaphore;
|
||||
|
||||
u32 _pad22_[0xbc]; // 0x6c4
|
||||
u32 _pad24_[0xbc]; // 0x6c4
|
||||
std::array<Blend, 8> independentBlend; // 0x780 For each render target
|
||||
u32 _pad23_[0x100]; // 0x7c0
|
||||
u32 _pad25_[0x100]; // 0x7c0
|
||||
u32 firmwareCall[0x20]; // 0x8c0
|
||||
};
|
||||
|
||||
std::array<u32, constant::Maxwell3DRegisterSize> raw;
|
||||
};
|
||||
static_assert(sizeof(Regs) == (constant::Maxwell3DRegisterSize * sizeof(u32)));
|
||||
static_assert(sizeof(Registers) == (constant::Maxwell3DRegisterCounter * sizeof(u32)));
|
||||
#pragma pack(pop)
|
||||
|
||||
Regs regs{}; //!< The maxwell 3D register space
|
||||
Regs shadowRegs{}; //!< The shadow registers, their function is controlled by the 'shadowRamControl' register
|
||||
Registers registers{}; //!< The maxwell 3D register space
|
||||
Registers shadowRegisters{}; //!< The shadow registers, their function is controlled by the 'shadowRamControl' register
|
||||
|
||||
std::array<u32, 0x10000> macroCode{}; //!< This is used to store GPU macros, the 256kb size is from Ryujinx
|
||||
|
||||
|
@ -32,7 +32,7 @@ namespace skyline::gpu::gpfifo {
|
||||
|
||||
state.logger->Info("Bound GPU engine 0x{:X} to subchannel {}", params.argument, params.subChannel);
|
||||
return;
|
||||
} else if (params.method < constant::GpfifoRegisterSize) {
|
||||
} else if (params.method < constant::GpfifoRegisterCount) {
|
||||
gpfifoEngine.CallMethod(params);
|
||||
} else {
|
||||
if (subchannels.at(params.subChannel) == nullptr)
|
||||
|
@ -18,18 +18,19 @@ namespace skyline::gpu {
|
||||
* @url https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/clb06f.h#L155
|
||||
*/
|
||||
struct GpEntry {
|
||||
enum class Fetch {
|
||||
enum class Fetch : u8 {
|
||||
Unconditional = 0,
|
||||
Conditional = 1,
|
||||
};
|
||||
|
||||
union {
|
||||
u32 entry0;
|
||||
|
||||
struct {
|
||||
Fetch fetch : 1;
|
||||
u8 _pad_ : 1;
|
||||
u32 get : 30;
|
||||
};
|
||||
u32 entry0;
|
||||
};
|
||||
|
||||
enum class Opcode : u8 {
|
||||
@ -39,36 +40,38 @@ namespace skyline::gpu {
|
||||
PbCrc = 3,
|
||||
};
|
||||
|
||||
enum class Priv {
|
||||
enum class Priv : u8 {
|
||||
User = 0,
|
||||
Kernel = 1,
|
||||
};
|
||||
|
||||
enum class Level {
|
||||
enum class Level : u8 {
|
||||
Main = 0,
|
||||
Subroutine = 1,
|
||||
};
|
||||
|
||||
enum class Sync {
|
||||
enum class Sync : u8 {
|
||||
Proceed = 0,
|
||||
Wait = 1,
|
||||
};
|
||||
|
||||
union {
|
||||
u32 entry1;
|
||||
|
||||
struct {
|
||||
union {
|
||||
u8 getHi;
|
||||
Opcode opcode;
|
||||
};
|
||||
|
||||
Priv priv : 1;
|
||||
Level level : 1;
|
||||
u32 size : 21;
|
||||
Sync sync : 1;
|
||||
};
|
||||
u32 entry1;
|
||||
};
|
||||
};
|
||||
static_assert(sizeof(GpEntry) == 0x8);
|
||||
static_assert(sizeof(GpEntry) == sizeof(u64));
|
||||
|
||||
/**
|
||||
* @brief This holds a single pushbuffer method header that describes a compressed method sequence
|
||||
@ -76,6 +79,8 @@ namespace skyline::gpu {
|
||||
* @url https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/clb06f.h#L179
|
||||
*/
|
||||
union PushBufferMethodHeader {
|
||||
u32 raw;
|
||||
|
||||
enum class TertOp : u8 {
|
||||
Grp0IncMethod = 0,
|
||||
Grp0SetSubDevMask = 1,
|
||||
@ -95,8 +100,6 @@ namespace skyline::gpu {
|
||||
EndPbSegment = 7
|
||||
};
|
||||
|
||||
struct {
|
||||
union {
|
||||
u16 methodAddress : 12;
|
||||
struct {
|
||||
u8 _pad0_ : 4;
|
||||
@ -118,10 +121,7 @@ namespace skyline::gpu {
|
||||
SecOp secOp : 3;
|
||||
};
|
||||
};
|
||||
};
|
||||
u32 entry;
|
||||
};
|
||||
static_assert(sizeof(PushBufferMethodHeader) == 0x4);
|
||||
static_assert(sizeof(PushBufferMethodHeader) == sizeof(u32));
|
||||
|
||||
/**
|
||||
* @brief The GPFIFO class handles creating pushbuffers from GP entries and then processing them
|
||||
|
@ -67,7 +67,7 @@ namespace skyline::gpu {
|
||||
break;
|
||||
}
|
||||
case Opcode::Operation::ReadImmediate: {
|
||||
u32 result = maxwell3D.regs.raw[registers[opcode->srcA] + opcode->immediate];
|
||||
u32 result = maxwell3D.registers.raw[registers[opcode->srcA] + opcode->immediate];
|
||||
HandleAssignment(opcode->assignmentOperation, opcode->dest, result);
|
||||
break;
|
||||
}
|
||||
|
@ -10,12 +10,18 @@ namespace skyline::gpu {
|
||||
class Maxwell3D;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The MacroInterpreter class handles interpreting macros. Macros are small programs that run on the GPU and are used for things like instanced rendering.
|
||||
*/
|
||||
class MacroInterpreter {
|
||||
private:
|
||||
/**
|
||||
* @brief This holds a single macro opcode
|
||||
*/
|
||||
#pragma pack(push, 1)
|
||||
union Opcode {
|
||||
u32 raw;
|
||||
|
||||
enum class Operation : u8 {
|
||||
AluRegister = 0,
|
||||
AddImmediate = 1,
|
||||
@ -54,16 +60,16 @@ namespace skyline::gpu {
|
||||
NonZero = 1,
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
Operation operation : 3;
|
||||
u8 _pad0_ : 1;
|
||||
AssignmentOperation assignmentOperation : 3;
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
u8 _pad1_ : 4;
|
||||
BranchCondition branchCondition : 1;
|
||||
u8 noDelay : 1;
|
||||
bool noDelay : 1;
|
||||
u8 _pad2_ : 1;
|
||||
u8 exit : 1;
|
||||
u8 dest : 3;
|
||||
@ -72,12 +78,12 @@ namespace skyline::gpu {
|
||||
AluOperation aluOperation : 5;
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
u16 _pad3_ : 14;
|
||||
i32 immediate : 18;
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) {
|
||||
struct {
|
||||
u32 _pad_ : 17;
|
||||
u8 srcBit : 5;
|
||||
u8 size : 5;
|
||||
@ -87,21 +93,20 @@ namespace skyline::gpu {
|
||||
return (1 << size) - 1;
|
||||
}
|
||||
} bitfield;
|
||||
|
||||
u32 raw;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(Opcode) == sizeof(u32));
|
||||
|
||||
/**
|
||||
* @brief This holds information about the Maxwell 3D method to be called in 'Send'
|
||||
*/
|
||||
union MethodAddress {
|
||||
u32 raw;
|
||||
|
||||
struct {
|
||||
u16 address : 12;
|
||||
u8 increment : 6;
|
||||
};
|
||||
|
||||
u32 raw;
|
||||
};
|
||||
|
||||
engine::Maxwell3D &maxwell3D;
|
||||
|
@ -97,8 +97,8 @@ namespace skyline::gpu::vmm {
|
||||
}
|
||||
|
||||
u64 MemoryManager::ReserveFixed(u64 address, u64 size) {
|
||||
if ((address & (constant::GpuPageSize - 1)) != 0)
|
||||
return 0;
|
||||
if (!util::IsAligned(address, constant::GpuPageSize))
|
||||
return false;
|
||||
|
||||
size = util::AlignUp(size, constant::GpuPageSize);
|
||||
|
||||
@ -120,8 +120,8 @@ namespace skyline::gpu::vmm {
|
||||
}
|
||||
|
||||
u64 MemoryManager::MapFixed(u64 address, u64 cpuAddress, u64 size) {
|
||||
if ((address & (constant::GpuPageSize - 1)) != 0)
|
||||
return 0;
|
||||
if (!util::IsAligned(address, constant::GpuPageSize))
|
||||
return false;
|
||||
|
||||
size = util::AlignUp(size, constant::GpuPageSize);
|
||||
|
||||
@ -129,7 +129,7 @@ namespace skyline::gpu::vmm {
|
||||
}
|
||||
|
||||
bool MemoryManager::Unmap(u64 address) {
|
||||
if ((address & (constant::GpuPageSize - 1)) != 0)
|
||||
if (!util::IsAligned(address, constant::GpuPageSize))
|
||||
return false;
|
||||
|
||||
auto chunk = std::find_if(chunkList.begin(), chunkList.end(), [address](const ChunkDescriptor &chunk) -> bool {
|
||||
@ -146,56 +146,62 @@ namespace skyline::gpu::vmm {
|
||||
}
|
||||
|
||||
void MemoryManager::Read(u8 *destination, u64 address, u64 size) const {
|
||||
auto chunk = --std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
|
||||
auto chunk = std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
|
||||
return address < chunk.address;
|
||||
});
|
||||
|
||||
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to read region in GPU address space - address: 0x{:X} size: 0x{:X}", address, size);
|
||||
throw exception("Failed to read region in GPU address space: Address: 0x{:X}, Size: 0x{:X}", address, size);
|
||||
|
||||
u64 chunkOffset = address - chunk->address;
|
||||
u64 destinationOffset{};
|
||||
chunk--;
|
||||
|
||||
u64 initialSize{size};
|
||||
u64 chunkOffset{address - chunk->address};
|
||||
u64 readAddress{chunk->cpuAddress + chunkOffset};
|
||||
u64 readSize{std::min(chunk->size - chunkOffset, size)};
|
||||
|
||||
// A continuous region in the GPU address space may be made up of several discontinuous regions in physical memory so we have to iterate over all chunks
|
||||
while (size != 0) {
|
||||
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to read region in GPU address space - address: {#:X} size: {#:X}", address, size);
|
||||
while (size) {
|
||||
state.process->ReadMemory(destination + (initialSize - size), readAddress, readSize);
|
||||
|
||||
u64 readSize = std::min(chunk->size - chunkOffset, size);
|
||||
state.process->ReadMemory(destination + destinationOffset, chunk->cpuAddress + chunkOffset, readSize);
|
||||
|
||||
// After the first read all further reads will start from the base of the chunk
|
||||
chunkOffset = 0;
|
||||
size -= readSize;
|
||||
destinationOffset += readSize;
|
||||
chunk++;
|
||||
if (size) {
|
||||
if (++chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to read region in GPU address space: Address: 0x{:X}, Size: 0x{:X}", address, size);
|
||||
|
||||
readAddress = chunk->cpuAddress;
|
||||
readSize = std::min(chunk->size, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MemoryManager::Write(u8 *source, u64 address, u64 size) const {
|
||||
auto chunk = --std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
|
||||
auto chunk = std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool {
|
||||
return address < chunk.address;
|
||||
});
|
||||
|
||||
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to write to region in GPU address space - address: {#:X} size: {#:X}", address, size);
|
||||
throw exception("Failed to write region in GPU address space: Address: 0x{:X}, Size: 0x{:X}", address, size);
|
||||
|
||||
u64 chunkOffset = address - chunk->address;
|
||||
u64 sourceOffset{};
|
||||
chunk--;
|
||||
|
||||
u64 initialSize{size};
|
||||
u64 chunkOffset{address - chunk->address};
|
||||
u64 writeAddress{chunk->cpuAddress + chunkOffset};
|
||||
u64 writeSize{std::min(chunk->size - chunkOffset, size)};
|
||||
|
||||
// A continuous region in the GPU address space may be made up of several discontinuous regions in physical memory so we have to iterate over all chunks
|
||||
while (size != 0) {
|
||||
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to write to region in GPU address space - address: {#:X} size: {#:X}", address, size);
|
||||
while (size) {
|
||||
state.process->WriteMemory(source + (initialSize - size), writeAddress, writeSize);
|
||||
|
||||
u64 writeSize = std::min(chunk->size - chunkOffset, size);
|
||||
state.process->WriteMemory(source + sourceOffset, chunk->cpuAddress + chunkOffset, writeSize);
|
||||
|
||||
// After the first read all further reads will start from the base of the chunk
|
||||
chunkOffset = 0;
|
||||
size -= writeSize;
|
||||
sourceOffset += writeSize;
|
||||
chunk++;
|
||||
if (size) {
|
||||
if (++chunk == chunkList.end() || chunk->state != ChunkState::Mapped)
|
||||
throw exception("Failed to write region in GPU address space: Address: 0x{:X}, Size: 0x{:X}", address, size);
|
||||
|
||||
writeAddress = chunk->cpuAddress;
|
||||
writeSize = std::min(chunk->size, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +47,6 @@ namespace skyline {
|
||||
class MemoryManager {
|
||||
private:
|
||||
const DeviceState &state;
|
||||
std::vector<ChunkDescriptor> chunkList; //!< This vector holds all the chunk descriptors
|
||||
|
||||
/**
|
||||
* @brief This finds a chunk of the specified type in the GPU address space that is larger than the given size
|
||||
@ -66,6 +65,7 @@ namespace skyline {
|
||||
|
||||
public:
|
||||
MemoryManager(const DeviceState &state);
|
||||
std::vector<ChunkDescriptor> chunkList; //!< This vector holds all the chunk descriptors
|
||||
|
||||
/**
|
||||
* @brief This reserves a region of the GPU address space so it will not be chosen automatically when mapping
|
||||
|
@ -7,10 +7,13 @@
|
||||
|
||||
namespace skyline {
|
||||
namespace constant {
|
||||
constexpr size_t MaxHwSyncpointCount = 192;
|
||||
constexpr size_t MaxHwSyncpointCount = 192; //!< The maximum number of HOST1X syncpoints on t210
|
||||
}
|
||||
|
||||
namespace gpu {
|
||||
/**
|
||||
* @brief The Syncpoint class represents a single syncpoint in the GPU which is used for GPU -> CPU synchronisation
|
||||
*/
|
||||
class Syncpoint {
|
||||
private:
|
||||
/**
|
||||
|
@ -4,7 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <common.h>
|
||||
#include "devices/nvhost_syncpoint.h"
|
||||
#include <services/nvdrv/devices/nvhost_syncpoint.h>
|
||||
|
||||
namespace skyline::service::nvdrv {
|
||||
/**
|
@ -29,7 +29,7 @@ namespace skyline::service::fssrv {
|
||||
void Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
/**
|
||||
* @brief This flushes any written data to the IFile
|
||||
* @brief This flushes any written data to the IFile on the Switch, however the emulator processes any FS event immediately so this does nothing
|
||||
*/
|
||||
void Flush(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
#include <os.h>
|
||||
#include <kernel/types/KProcess.h>
|
||||
#include <services/nvdrv/INvDrvServices.h>
|
||||
#include <services/nvdrv/fence.h>
|
||||
#include <services/common/fence.h>
|
||||
#include <gpu/format.h>
|
||||
#include "IHOSBinderDriver.h"
|
||||
#include "display.h"
|
||||
|
@ -126,6 +126,8 @@ namespace skyline::service::nvdrv::device {
|
||||
u32 pages;
|
||||
};
|
||||
|
||||
constexpr u32 MinAlignmentShift{0x10}; // This shift is applied to all addresses passed to Remap
|
||||
|
||||
size_t entryCount{buffer.input.at(0).size / sizeof(Entry)};
|
||||
std::span entries(state.process->GetPointer<Entry>(buffer.input.at(0).address), entryCount);
|
||||
|
||||
@ -133,9 +135,9 @@ namespace skyline::service::nvdrv::device {
|
||||
try {
|
||||
auto nvmap = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->GetDevice<nvdrv::device::NvMap>(nvdrv::device::NvDeviceType::nvmap)->handleTable.at(entry.nvmapHandle);
|
||||
|
||||
u64 mapAddress = static_cast<u64>(entry.gpuOffset) << 0x10;
|
||||
u64 mapPhysicalAddress = nvmap->address + (static_cast<u64>(entry.mapOffset) << 0x10);
|
||||
u64 mapSize = static_cast<u64>(entry.pages) << 0x10;
|
||||
u64 mapAddress = static_cast<u64>(entry.gpuOffset) << MinAlignmentShift;
|
||||
u64 mapPhysicalAddress = nvmap->address + (static_cast<u64>(entry.mapOffset) << MinAlignmentShift);
|
||||
u64 mapSize = static_cast<u64>(entry.pages) << MinAlignmentShift;
|
||||
|
||||
state.gpu->memoryManager.MapFixed(mapAddress, mapPhysicalAddress, mapSize);
|
||||
} catch (const std::exception &e) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <services/nvdrv/fence.h>
|
||||
#include <services/common/fence.h>
|
||||
#include "nvdevice.h"
|
||||
|
||||
namespace skyline::service::nvdrv::device {
|
||||
|
@ -45,65 +45,6 @@ namespace skyline::service::nvdrv::device {
|
||||
{0x001F, NFUNC(NvHostCtrl::EventRegister)},
|
||||
}) {}
|
||||
|
||||
void NvHostCtrl::GetConfig(IoctlData &buffer) {
|
||||
buffer.status = NvStatus::BadValue;
|
||||
}
|
||||
|
||||
void NvHostCtrl::EventSignal(IoctlData &buffer) {
|
||||
struct Data {
|
||||
u16 _pad_;
|
||||
u16 userEventId;
|
||||
};
|
||||
auto userEventId = state.process->GetObject<Data>(buffer.input.at(0).address).userEventId;
|
||||
state.logger->Debug("Signalling nvhost event: {}", userEventId);
|
||||
|
||||
if (userEventId >= constant::NvHostEventCount || !events.at(userEventId)) {
|
||||
buffer.status = NvStatus::BadValue;
|
||||
return;
|
||||
}
|
||||
|
||||
auto &event = *events.at(userEventId);
|
||||
|
||||
if (event.state == NvHostEvent::State::Waiting) {
|
||||
event.state = NvHostEvent::State::Cancelling;
|
||||
state.logger->Debug("Cancelling waiting nvhost event: {}", userEventId);
|
||||
event.Cancel(state.gpu);
|
||||
}
|
||||
|
||||
event.state = NvHostEvent::State::Cancelled;
|
||||
|
||||
auto &hostSyncpoint = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->hostSyncpoint;
|
||||
hostSyncpoint.UpdateMin(event.fence.id);
|
||||
}
|
||||
|
||||
void NvHostCtrl::EventWait(IoctlData &buffer) {
|
||||
EventWaitImpl(buffer, false);
|
||||
}
|
||||
|
||||
void NvHostCtrl::EventWaitAsync(IoctlData &buffer) {
|
||||
EventWaitImpl(buffer, true);
|
||||
}
|
||||
|
||||
void NvHostCtrl::EventRegister(IoctlData &buffer) {
|
||||
auto userEventId = state.process->GetObject<u32>(buffer.input.at(0).address);
|
||||
state.logger->Debug("Registering nvhost event: {}", userEventId);
|
||||
|
||||
if (events.at(userEventId))
|
||||
throw exception("Recreating events is unimplemented");
|
||||
|
||||
events.at(userEventId) = NvHostEvent(state);
|
||||
}
|
||||
|
||||
std::shared_ptr<type::KEvent> NvHostCtrl::QueryEvent(u32 eventId) {
|
||||
auto eventValue = reinterpret_cast<EventValue *>(&eventId);
|
||||
const auto &event = events.at(eventValue->nonAsync ? eventValue->eventSlotNonAsync : eventValue->eventSlotAsync);
|
||||
|
||||
if (event && event->fence.id == (eventValue->nonAsync ? eventValue->syncpointIdNonAsync : eventValue->syncpointIdAsync))
|
||||
return event->event;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 NvHostCtrl::FindFreeEvent(u32 syncpointId) {
|
||||
u32 eventIndex{constant::NvHostEventCount}; //!< Holds the index of the last populated event in the event array
|
||||
u32 freeIndex{constant::NvHostEventCount}; //!< Holds the index of the first unused event id
|
||||
@ -208,4 +149,65 @@ namespace skyline::service::nvdrv::device {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void NvHostCtrl::GetConfig(IoctlData &buffer) {
|
||||
buffer.status = NvStatus::BadValue;
|
||||
}
|
||||
|
||||
void NvHostCtrl::EventSignal(IoctlData &buffer) {
|
||||
struct Data {
|
||||
u16 _pad_;
|
||||
u16 userEventId;
|
||||
};
|
||||
auto userEventId = state.process->GetObject<Data>(buffer.input.at(0).address).userEventId;
|
||||
state.logger->Debug("Signalling nvhost event: {}", userEventId);
|
||||
|
||||
if (userEventId >= constant::NvHostEventCount || !events.at(userEventId)) {
|
||||
buffer.status = NvStatus::BadValue;
|
||||
return;
|
||||
}
|
||||
|
||||
auto &event = *events.at(userEventId);
|
||||
|
||||
if (event.state == NvHostEvent::State::Waiting) {
|
||||
event.state = NvHostEvent::State::Cancelling;
|
||||
state.logger->Debug("Cancelling waiting nvhost event: {}", userEventId);
|
||||
event.Cancel(state.gpu);
|
||||
}
|
||||
|
||||
event.state = NvHostEvent::State::Cancelled;
|
||||
|
||||
auto &hostSyncpoint = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->hostSyncpoint;
|
||||
hostSyncpoint.UpdateMin(event.fence.id);
|
||||
}
|
||||
|
||||
void NvHostCtrl::EventWait(IoctlData &buffer) {
|
||||
EventWaitImpl(buffer, false);
|
||||
}
|
||||
|
||||
void NvHostCtrl::EventWaitAsync(IoctlData &buffer) {
|
||||
EventWaitImpl(buffer, true);
|
||||
}
|
||||
|
||||
void NvHostCtrl::EventRegister(IoctlData &buffer) {
|
||||
auto userEventId = state.process->GetObject<u32>(buffer.input.at(0).address);
|
||||
state.logger->Debug("Registering nvhost event: {}", userEventId);
|
||||
|
||||
auto &event = events.at(userEventId);
|
||||
|
||||
if (event)
|
||||
throw exception("Recreating events is unimplemented");
|
||||
|
||||
event = NvHostEvent(state);
|
||||
}
|
||||
|
||||
std::shared_ptr<type::KEvent> NvHostCtrl::QueryEvent(u32 eventId) {
|
||||
auto eventValue = EventValue{.val = eventId};
|
||||
const auto &event = events.at(eventValue.nonAsync ? eventValue.eventSlotNonAsync : eventValue.eventSlotAsync);
|
||||
|
||||
if (event && event->fence.id == (eventValue.nonAsync ? eventValue.syncpointIdNonAsync : eventValue.syncpointIdAsync))
|
||||
return event->event;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <services/nvdrv/fence.h>
|
||||
#include <services/common/fence.h>
|
||||
#include "nvdevice.h"
|
||||
|
||||
namespace skyline {
|
||||
@ -12,6 +12,9 @@ namespace skyline {
|
||||
}
|
||||
|
||||
namespace service::nvdrv::device {
|
||||
/**
|
||||
* @brief This represents a single registered event with an attached fence
|
||||
*/
|
||||
class NvHostEvent {
|
||||
private:
|
||||
u64 waiterId{};
|
||||
@ -57,6 +60,8 @@ namespace skyline {
|
||||
* @brief This holds metadata about an event, it is used by QueryEvent and EventWait
|
||||
*/
|
||||
union EventValue {
|
||||
u32 val;
|
||||
|
||||
struct {
|
||||
u8 _pad0_ : 4;
|
||||
u32 syncpointIdAsync : 28;
|
||||
@ -71,8 +76,6 @@ namespace skyline {
|
||||
bool nonAsync : 1;
|
||||
u8 _pad12_ : 3;
|
||||
};
|
||||
|
||||
u32 val;
|
||||
};
|
||||
static_assert(sizeof(EventValue) == sizeof(u32));
|
||||
|
||||
|
@ -9,7 +9,6 @@
|
||||
|
||||
namespace skyline::service::nvdrv {
|
||||
/**
|
||||
* @todo Implement the GPU side of this
|
||||
* @brief NvHostSyncpoint handles allocating and accessing host1x syncpoints
|
||||
* @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html
|
||||
* @url https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c
|
||||
|
@ -28,7 +28,7 @@ namespace skyline::vfs {
|
||||
* @param size The size of the file to create
|
||||
* @return Whether creating the file succeeded
|
||||
*/
|
||||
virtual bool CreateFile(std::string path, size_t size) {
|
||||
virtual bool CreateFile(const std::string &path, size_t size) {
|
||||
throw exception("This filesystem does not support creating files");
|
||||
};
|
||||
|
||||
@ -38,7 +38,7 @@ namespace skyline::vfs {
|
||||
* @param parents Whether all parent directories in the given path should be created
|
||||
* @return Whether creating the directory succeeded
|
||||
*/
|
||||
virtual bool CreateDirectory(std::string path, bool parents) {
|
||||
virtual bool CreateDirectory(const std::string &path, bool parents) {
|
||||
throw exception("This filesystem does not support creating directories");
|
||||
};
|
||||
|
||||
@ -48,21 +48,21 @@ namespace skyline::vfs {
|
||||
* @param mode The mode to open the file with
|
||||
* @return A shared pointer to a Backing object of the file
|
||||
*/
|
||||
virtual std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false}) = 0;
|
||||
virtual std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false}) = 0;
|
||||
|
||||
/**
|
||||
* @brief Queries the type of the entry given by path
|
||||
* @param path The path to the entry
|
||||
* @return The type of the entry, if present
|
||||
*/
|
||||
virtual std::optional<Directory::EntryType> GetEntryType(std::string path) = 0;
|
||||
virtual std::optional<Directory::EntryType> GetEntryType(const std::string &path) = 0;
|
||||
|
||||
/**
|
||||
* @brief Checks if a given file exists in a filesystem
|
||||
* @param path The path to the file
|
||||
* @return Whether the file exists
|
||||
*/
|
||||
inline bool FileExists(std::string path) {
|
||||
inline bool FileExists(const std::string &path) {
|
||||
auto entry = GetEntryType(path);
|
||||
return entry && *entry == Directory::EntryType::File;
|
||||
}
|
||||
@ -72,7 +72,7 @@ namespace skyline::vfs {
|
||||
* @param path The path to the directory
|
||||
* @return Whether the directory exists
|
||||
*/
|
||||
inline bool DirectoryExists(std::string path) {
|
||||
inline bool DirectoryExists(const std::string &path) {
|
||||
auto entry = GetEntryType(path);
|
||||
return entry && *entry == Directory::EntryType::Directory;
|
||||
}
|
||||
@ -83,7 +83,7 @@ namespace skyline::vfs {
|
||||
* @param listMode The list mode for the directory
|
||||
* @return A shared pointer to a Directory object of the directory
|
||||
*/
|
||||
virtual std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode) {
|
||||
virtual std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode) {
|
||||
throw exception("This filesystem does not support opening directories");
|
||||
};
|
||||
};
|
||||
|
@ -10,13 +10,13 @@
|
||||
#include "os_filesystem.h"
|
||||
|
||||
namespace skyline::vfs {
|
||||
OsFileSystem::OsFileSystem(std::string basePath) : FileSystem(), basePath(basePath) {
|
||||
OsFileSystem::OsFileSystem(const std::string &basePath) : FileSystem(), basePath(std::move(basePath)) {
|
||||
if (!DirectoryExists(basePath))
|
||||
if (!CreateDirectory(basePath, true))
|
||||
throw exception("Error creating the OS filesystem backing directory");
|
||||
}
|
||||
|
||||
bool OsFileSystem::CreateFile(std::string path, size_t size) {
|
||||
bool OsFileSystem::CreateFile(const std::string &path, size_t size) {
|
||||
auto fullPath = basePath + path;
|
||||
|
||||
// Create a directory that will hold the file
|
||||
@ -39,7 +39,7 @@ namespace skyline::vfs {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OsFileSystem::CreateDirectory(std::string path, bool parents) {
|
||||
bool OsFileSystem::CreateDirectory(const std::string &path, bool parents) {
|
||||
if (!parents) {
|
||||
int ret = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
return ret == 0 || errno == EEXIST;
|
||||
@ -59,7 +59,7 @@ namespace skyline::vfs {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<Backing> OsFileSystem::OpenFile(std::string path, Backing::Mode mode) {
|
||||
std::shared_ptr<Backing> OsFileSystem::OpenFile(const std::string &path, Backing::Mode mode) {
|
||||
if (!(mode.read || mode.write))
|
||||
throw exception("Cannot open a file that is neither readable or writable");
|
||||
|
||||
@ -70,7 +70,7 @@ namespace skyline::vfs {
|
||||
return std::make_shared<OsBacking>(fd, true, mode);
|
||||
}
|
||||
|
||||
std::optional<Directory::EntryType> OsFileSystem::GetEntryType(std::string path) {
|
||||
std::optional<Directory::EntryType> OsFileSystem::GetEntryType(const std::string &path) {
|
||||
auto fullPath = basePath + path;
|
||||
|
||||
auto directory = opendir(fullPath.c_str());
|
||||
|
@ -14,14 +14,14 @@ namespace skyline::vfs {
|
||||
std::string basePath; //!< The base path for filesystem operations
|
||||
|
||||
public:
|
||||
OsFileSystem(std::string basePath);
|
||||
OsFileSystem(const std::string &basePath);
|
||||
|
||||
bool CreateFile(std::string path, size_t size);
|
||||
bool CreateFile(const std::string &path, size_t size);
|
||||
|
||||
bool CreateDirectory(std::string path, bool parents);
|
||||
bool CreateDirectory(const std::string &path, bool parents);
|
||||
|
||||
std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false});
|
||||
std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false});
|
||||
|
||||
std::optional<Directory::EntryType> GetEntryType(std::string path);
|
||||
std::optional<Directory::EntryType> GetEntryType(const std::string &path);
|
||||
};
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ namespace skyline::vfs {
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Backing> PartitionFileSystem::OpenFile(std::string path, Backing::Mode mode) {
|
||||
std::shared_ptr<Backing> PartitionFileSystem::OpenFile(const std::string &path, Backing::Mode mode) {
|
||||
try {
|
||||
auto &entry = fileMap.at(path);
|
||||
return std::make_shared<RegionBacking>(backing, fileDataOffset + entry.offset, entry.size, mode);
|
||||
@ -40,14 +40,14 @@ namespace skyline::vfs {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Directory::EntryType> PartitionFileSystem::GetEntryType(std::string path) {
|
||||
std::optional<Directory::EntryType> PartitionFileSystem::GetEntryType(const std::string &path) {
|
||||
if (fileMap.count(path))
|
||||
return Directory::EntryType::File;
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<Directory> PartitionFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) {
|
||||
std::shared_ptr<Directory> PartitionFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) {
|
||||
// PFS doesn't have directories
|
||||
if (path != "")
|
||||
return nullptr;
|
||||
|
@ -52,11 +52,11 @@ namespace skyline::vfs {
|
||||
public:
|
||||
PartitionFileSystem(std::shared_ptr<Backing> backing);
|
||||
|
||||
std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false});
|
||||
std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false});
|
||||
|
||||
std::optional<Directory::EntryType> GetEntryType(std::string path);
|
||||
std::optional<Directory::EntryType> GetEntryType(const std::string &path);
|
||||
|
||||
std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode);
|
||||
std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -11,7 +11,7 @@ namespace skyline::vfs {
|
||||
TraverseDirectory(0, "");
|
||||
}
|
||||
|
||||
void RomFileSystem::TraverseFiles(u32 offset, std::string path) {
|
||||
void RomFileSystem::TraverseFiles(u32 offset, const std::string &path) {
|
||||
RomFsFileEntry entry{};
|
||||
|
||||
do {
|
||||
@ -29,7 +29,7 @@ namespace skyline::vfs {
|
||||
} while (offset != constant::RomFsEmptyEntry);
|
||||
}
|
||||
|
||||
void RomFileSystem::TraverseDirectory(u32 offset, std::string path) {
|
||||
void RomFileSystem::TraverseDirectory(u32 offset, const std::string &path) {
|
||||
RomFsDirectoryEntry entry{};
|
||||
backing->Read(&entry, header.dirMetaTableOffset + offset);
|
||||
|
||||
@ -52,7 +52,7 @@ namespace skyline::vfs {
|
||||
TraverseDirectory(entry.siblingOffset, path);
|
||||
}
|
||||
|
||||
std::shared_ptr<Backing> RomFileSystem::OpenFile(std::string path, Backing::Mode mode) {
|
||||
std::shared_ptr<Backing> RomFileSystem::OpenFile(const std::string &path, Backing::Mode mode) {
|
||||
try {
|
||||
const auto &entry = fileMap.at(path);
|
||||
return std::make_shared<RegionBacking>(backing, header.dataOffset + entry.offset, entry.size, mode);
|
||||
@ -61,7 +61,7 @@ namespace skyline::vfs {
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<Directory::EntryType> RomFileSystem::GetEntryType(std::string path) {
|
||||
std::optional<Directory::EntryType> RomFileSystem::GetEntryType(const std::string &path) {
|
||||
if (fileMap.count(path))
|
||||
return Directory::EntryType::File;
|
||||
else if (directoryMap.count(path))
|
||||
@ -70,7 +70,7 @@ namespace skyline::vfs {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::shared_ptr<Directory> RomFileSystem::OpenDirectory(std::string path, Directory::ListMode listMode) {
|
||||
std::shared_ptr<Directory> RomFileSystem::OpenDirectory(const std::string &path, Directory::ListMode listMode) {
|
||||
try {
|
||||
auto &entry = directoryMap.at(path);
|
||||
return std::make_shared<RomFileSystemDirectory>(backing, header, entry, listMode);
|
||||
|
@ -23,14 +23,14 @@ namespace skyline {
|
||||
* @param offset The offset of the file entry to traverses the siblings of
|
||||
* @param path The path to the parent directory of the supplied file entry
|
||||
*/
|
||||
void TraverseFiles(u32 offset, std::string path);
|
||||
void TraverseFiles(u32 offset, const std::string &path);
|
||||
|
||||
/**
|
||||
* @brief Traverses the directories within the given directory, adds them to the directory map and calls TraverseFiles on them
|
||||
* @param offset The offset of the directory entry to traverses the directories in
|
||||
* @param path The path to the supplied directory entry
|
||||
*/
|
||||
void TraverseDirectory(u32 offset, std::string path);
|
||||
void TraverseDirectory(u32 offset, const std::string &path);
|
||||
|
||||
public:
|
||||
/**
|
||||
@ -79,11 +79,11 @@ namespace skyline {
|
||||
|
||||
RomFileSystem(std::shared_ptr<Backing> backing);
|
||||
|
||||
std::shared_ptr<Backing> OpenFile(std::string path, Backing::Mode mode = {true, false, false});
|
||||
std::shared_ptr<Backing> OpenFile(const std::string &path, Backing::Mode mode = {true, false, false});
|
||||
|
||||
std::optional<Directory::EntryType> GetEntryType(std::string path);
|
||||
std::optional<Directory::EntryType> GetEntryType(const std::string &path);
|
||||
|
||||
std::shared_ptr<Directory> OpenDirectory(std::string path, Directory::ListMode listMode);
|
||||
std::shared_ptr<Directory> OpenDirectory(const std::string &path, Directory::ListMode listMode);
|
||||
};
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user