Address review comments

This commit is contained in:
Billy Laws 2020-08-12 20:02:24 +01:00 committed by ◱ PixelyIon
parent ae131502c6
commit e5264f7762
27 changed files with 342 additions and 295 deletions

View File

@ -26,6 +26,7 @@ namespace skyline::gpu {
void GPU::Loop() { void GPU::Loop() {
gpfifo.Run(); gpfifo.Run();
vsyncEvent->Signal();
if (surfaceUpdate) { if (surfaceUpdate) {
if (Surface == nullptr) if (Surface == nullptr)

View File

@ -26,7 +26,7 @@ namespace skyline::gpu {
u16 method; u16 method;
u32 argument; u32 argument;
u32 subChannel; 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 { namespace engine {

View File

@ -9,7 +9,7 @@
namespace skyline { namespace skyline {
namespace constant { 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 { namespace gpu::engine {
@ -23,8 +23,11 @@ namespace skyline {
* @brief This holds the GPFIFO engine's registers * @brief This holds the GPFIFO engine's registers
* @url https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/clb06f.h#L65 * @url https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/clb06f.h#L65
*/ */
union Regs { #pragma pack(push, 1)
enum class SemaphoreOperation { union Registers {
std::array<u32, constant::GpfifoRegisterCount> raw;
enum class SemaphoreOperation : u8 {
Acquire = 1, Acquire = 1,
Release = 2, Release = 2,
AcqGeq = 4, AcqGeq = 4,
@ -32,22 +35,22 @@ namespace skyline {
Reduction = 16 Reduction = 16
}; };
enum class SemaphoreAcquireSwitch { enum class SemaphoreAcquireSwitch : u8 {
Disabled = 0, Disabled = 0,
Enabled = 1 Enabled = 1
}; };
enum class SemaphoreReleaseWfi { enum class SemaphoreReleaseWfi : u8 {
En = 0, En = 0,
Dis = 1 Dis = 1
}; };
enum class SemaphoreReleaseSize { enum class SemaphoreReleaseSize : u8 {
SixteenBytes = 0, SixteenBytes = 0,
FourBytes = 1 FourBytes = 1
}; };
enum class SemaphoreReduction { enum class SemaphoreReduction : u8 {
Min = 0, Min = 0,
Max = 1, Max = 1,
Xor = 2, Xor = 2,
@ -58,32 +61,32 @@ namespace skyline {
Dec = 7 Dec = 7
}; };
enum class SemaphoreFormat { enum class SemaphoreFormat : u8 {
Signed = 0, Signed = 0,
Unsigned = 1 Unsigned = 1
}; };
enum class MemOpTlbInvalidatePdb { enum class MemOpTlbInvalidatePdb : u8 {
One = 0, One = 0,
All = 1 All = 1
}; };
enum class SyncpointOperation { enum class SyncpointOperation : u8 {
Wait = 0, Wait = 0,
Incr = 1 Incr = 1
}; };
enum class SyncpointWaitSwitch { enum class SyncpointWaitSwitch : u8 {
Dis = 0, Dis = 0,
En = 1 En = 1
}; };
enum class WfiScope { enum class WfiScope : u8 {
CurrentScgType = 0, CurrentScgType = 0,
All = 1 All = 1
}; };
enum class YieldOp { enum class YieldOp : u8 {
Nop = 0, Nop = 0,
PbdmaTimeslice = 1, PbdmaTimeslice = 1,
RunlistTimeslice = 2, RunlistTimeslice = 2,
@ -93,8 +96,8 @@ namespace skyline {
struct { struct {
struct { struct {
u16 nvClass : 16; u16 nvClass : 16;
u16 engine : 5; u8 engine : 5;
u32 _pad_ : 11; u16 _pad_ : 11;
} setObject; } setObject;
u32 illegal; u32 illegal;
@ -114,7 +117,7 @@ namespace skyline {
u32 payload; u32 payload;
struct __attribute__((__packed__)) { struct {
SemaphoreOperation operation : 5; SemaphoreOperation operation : 5;
u8 _pad2_ : 7; u8 _pad2_ : 7;
SemaphoreAcquireSwitch acquireSwitch : 1; SemaphoreAcquireSwitch acquireSwitch : 1;
@ -140,7 +143,7 @@ namespace skyline {
struct { struct {
u32 payload; u32 payload;
struct __attribute__((__packed__)) { struct {
SyncpointOperation operation : 1; SyncpointOperation operation : 1;
u8 _pad0_ : 3; u8 _pad0_ : 3;
SyncpointWaitSwitch waitSwitch : 1; SyncpointWaitSwitch waitSwitch : 1;
@ -162,9 +165,9 @@ namespace skyline {
u32 _pad_ : 30; u32 _pad_ : 30;
} yield; } yield;
}; };
std::array<u32, constant::GpfifoRegisterSize> raw; } registers{};
} regs{}; static_assert(sizeof(Registers) == (constant::GpfifoRegisterCount * sizeof(u32)));
static_assert(sizeof(Regs) == (constant::GpfifoRegisterSize << 2)); #pragma pack(pop)
public: public:
GPFIFO(const DeviceState &state) : Engine(state) {} GPFIFO(const DeviceState &state) : Engine(state) {}
@ -172,7 +175,7 @@ namespace skyline {
void CallMethod(MethodParams params) { void CallMethod(MethodParams params) {
state.logger->Debug("Called method in GPFIFO: 0x{:X} args: 0x{:X}", params.method, params.argument); 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;
}; };
}; };
} }

View File

@ -11,72 +11,76 @@ namespace skyline::gpu::engine {
} }
void Maxwell3D::ResetRegs() { void Maxwell3D::ResetRegs() {
memset(&regs, 0, sizeof(regs)); registers = {};
regs.rasterizerEnable = true; registers.rasterizerEnable = true;
for (auto &transform : regs.viewportTransform) { for (auto &transform : registers.viewportTransform) {
transform.swizzles.x = Regs::ViewportTransform::Swizzle::PositiveX; transform.swizzles.x = Registers::ViewportTransform::Swizzle::PositiveX;
transform.swizzles.y = Regs::ViewportTransform::Swizzle::PositiveY; transform.swizzles.y = Registers::ViewportTransform::Swizzle::PositiveY;
transform.swizzles.z = Regs::ViewportTransform::Swizzle::PositiveZ; transform.swizzles.z = Registers::ViewportTransform::Swizzle::PositiveZ;
transform.swizzles.w = Regs::ViewportTransform::Swizzle::PositiveW; transform.swizzles.w = Registers::ViewportTransform::Swizzle::PositiveW;
} }
for (auto &viewport : regs.viewport) { for (auto &viewport : registers.viewport) {
viewport.depthRangeFar = 1.0f; viewport.depthRangeFar = 1.0f;
viewport.depthRangeNear = 0.0f; viewport.depthRangeNear = 0.0f;
} }
regs.polygonMode.front = Regs::PolygonMode::Fill; registers.polygonMode.front = Registers::PolygonMode::Fill;
regs.polygonMode.back = Regs::PolygonMode::Fill; registers.polygonMode.back = Registers::PolygonMode::Fill;
regs.stencilFront.failOp = regs.stencilFront.zFailOp = regs.stencilFront.zPassOp = Regs::StencilOp::Keep; registers.stencilFront.failOp = registers.stencilFront.zFailOp = registers.stencilFront.zPassOp = Registers::StencilOp::Keep;
regs.stencilFront.func.op = Regs::CompareOp::Always; registers.stencilFront.compare.op = Registers::CompareOp::Always;
regs.stencilFront.func.mask = 0xFFFFFFFF; registers.stencilFront.compare.mask = 0xFFFFFFFF;
regs.stencilFront.mask = 0xFFFFFFFF; registers.stencilFront.writeMask = 0xFFFFFFFF;
regs.stencilBack.stencilTwoSideEnable = true; registers.stencilTwoSideEnable = true;
regs.stencilBack.failOp = regs.stencilBack.zFailOp = regs.stencilBack.zPassOp = Regs::StencilOp::Keep; registers.stencilBack.failOp = registers.stencilBack.zFailOp = registers.stencilBack.zPassOp = Registers::StencilOp::Keep;
regs.stencilBack.funcOp = Regs::CompareOp::Always; registers.stencilBack.compareOp = Registers::CompareOp::Always;
regs.stencilBackExtra.funcMask = 0xFFFFFFFF; registers.stencilBackExtra.compareMask = 0xFFFFFFFF;
regs.stencilBackExtra.mask = 0xFFFFFFFF; registers.stencilBackExtra.writeMask = 0xFFFFFFFF;
regs.rtSeparateFragData = true; registers.rtSeparateFragData = true;
for (auto &attribute : regs.vertexAttributeState) for (auto &attribute : registers.vertexAttributeState)
attribute.fixed = true; attribute.fixed = true;
regs.depthTestFunc = Regs::CompareOp::Always; registers.depthTestFunc = Registers::CompareOp::Always;
regs.blend.colorOp = regs.blend.alphaOp = Regs::Blend::Op::Add; registers.blend.colorOp = registers.blend.alphaOp = Registers::Blend::Op::Add;
regs.blend.colorSrcFactor = regs.blend.alphaSrcFactor = Regs::Blend::Factor::One; registers.blend.colorSrcFactor = registers.blend.alphaSrcFactor = Registers::Blend::Factor::One;
regs.blend.colorDestFactor = regs.blend.alphaDestFactor = Regs::Blend::Factor::Zero; registers.blend.colorDestFactor = registers.blend.alphaDestFactor = Registers::Blend::Factor::Zero;
regs.lineWidthSmooth = 1.0f; registers.lineWidthSmooth = 1.0f;
regs.lineWidthAliased = 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; registers.frontFace = Registers::FrontFace::CounterClockwise;
regs.cullFace = Regs::CullFace::Back; registers.cullFace = Registers::CullFace::Back;
for (auto &mask : regs.colorMask) for (auto &mask : registers.colorMask)
mask.r = mask.g = mask.b = mask.a = 1; mask.r = mask.g = mask.b = mask.a = 1;
for (auto &blend : regs.independentBlend) { for (auto &blend : registers.independentBlend) {
blend.colorOp = blend.alphaOp = Regs::Blend::Op::Add; blend.colorOp = blend.alphaOp = Registers::Blend::Op::Add;
blend.colorSrcFactor = blend.alphaSrcFactor = Regs::Blend::Factor::One; blend.colorSrcFactor = blend.alphaSrcFactor = Registers::Blend::Factor::One;
blend.colorDestFactor = blend.alphaDestFactor = Regs::Blend::Factor::Zero; blend.colorDestFactor = blend.alphaDestFactor = Registers::Blend::Factor::Zero;
} }
registers.viewportTransformEnable = true;
} }
void Maxwell3D::CallMethod(MethodParams params) { void Maxwell3D::CallMethod(MethodParams params) {
state.logger->Debug("Called method in Maxwell 3D: 0x{:X} args: 0x{:X}", params.method, params.argument); 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 // 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)) 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); macroInvocation.arguments.push_back(params.argument);
@ -90,58 +94,58 @@ namespace skyline::gpu::engine {
return; 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) if (shadowRegisters.mme.shadowRamControl == Registers::MmeShadowRamControl::MethodTrack || shadowRegisters.mme.shadowRamControl == Registers::MmeShadowRamControl::MethodTrackWithFilter)
shadowRegs.raw[params.method] = params.argument; shadowRegisters.raw[params.method] = params.argument;
else if (shadowRegs.mme.shadowRamControl == Regs::MmeShadowRamControl::MethodReplay) else if (shadowRegisters.mme.shadowRamControl == Registers::MmeShadowRamControl::MethodReplay)
params.argument = shadowRegs.raw[params.method]; params.argument = shadowRegisters.raw[params.method];
switch (params.method) { switch (params.method) {
case MAXWELL3D_OFFSET(mme.instructionRamLoad): case MAXWELL3D_OFFSET(mme.instructionRamLoad):
if (regs.mme.instructionRamPointer >= macroCode.size()) if (registers.mme.instructionRamPointer >= macroCode.size())
throw exception("Macro memory is full!"); throw exception("Macro memory is full!");
macroCode[regs.mme.instructionRamPointer++] = params.argument; macroCode[registers.mme.instructionRamPointer++] = params.argument;
break; break;
case MAXWELL3D_OFFSET(mme.startAddressRamLoad): case MAXWELL3D_OFFSET(mme.startAddressRamLoad):
if (regs.mme.startAddressRamPointer >= macroPositions.size()) if (registers.mme.startAddressRamPointer >= macroPositions.size())
throw exception("Maximum amount of macros reached!"); throw exception("Maximum amount of macros reached!");
macroPositions[regs.mme.startAddressRamPointer++] = params.argument; macroPositions[registers.mme.startAddressRamPointer++] = params.argument;
break; break;
case MAXWELL3D_OFFSET(mme.shadowRamControl): case MAXWELL3D_OFFSET(mme.shadowRamControl):
shadowRegs.mme.shadowRamControl = static_cast<Regs::MmeShadowRamControl>(params.argument); shadowRegisters.mme.shadowRamControl = static_cast<Registers::MmeShadowRamControl>(params.argument);
break; break;
case MAXWELL3D_OFFSET(syncpointAction): case MAXWELL3D_OFFSET(syncpointAction):
state.gpu->syncpoints.at(regs.syncpointAction.id).Increment(); state.gpu->syncpoints.at(registers.syncpointAction.id).Increment();
break; break;
case MAXWELL3D_OFFSET(semaphore.info): case MAXWELL3D_OFFSET(semaphore.info):
switch (regs.semaphore.info.op) { switch (registers.semaphore.info.op) {
case Regs::SemaphoreInfo::Op::Release: case Registers::SemaphoreInfo::Op::Release:
WriteSemaphoreResult(regs.semaphore.payload); WriteSemaphoreResult(registers.semaphore.payload);
break; break;
case Regs::SemaphoreInfo::Op::Counter: case Registers::SemaphoreInfo::Op::Counter:
HandleSemaphoreCounterOperation(); HandleSemaphoreCounterOperation();
break; break;
default: 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;
} }
break; break;
case MAXWELL3D_OFFSET(firmwareCall[4]): case MAXWELL3D_OFFSET(firmwareCall[4]):
regs.raw[0xd00] = 1; registers.raw[0xd00] = 1;
break; break;
} }
} }
void Maxwell3D::HandleSemaphoreCounterOperation() { void Maxwell3D::HandleSemaphoreCounterOperation() {
switch (regs.semaphore.info.counterType) { switch (registers.semaphore.info.counterType) {
case Regs::SemaphoreInfo::CounterType::Zero: case Registers::SemaphoreInfo::CounterType::Zero:
WriteSemaphoreResult(0); WriteSemaphoreResult(0);
break; break;
default: 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; break;
} }
} }
@ -152,11 +156,11 @@ namespace skyline::gpu::engine {
u64 timestamp; u64 timestamp;
}; };
switch (regs.semaphore.info.structureSize) { switch (registers.semaphore.info.structureSize) {
case Regs::SemaphoreInfo::StructureSize::OneWord: case Registers::SemaphoreInfo::StructureSize::OneWord:
state.gpu->memoryManager.Write<u32>(static_cast<u32>(result), regs.semaphore.address.Pack()); state.gpu->memoryManager.Write<u32>(static_cast<u32>(result), registers.semaphore.address.Pack());
break; break;
case Regs::SemaphoreInfo::StructureSize::FourWords: { case Registers::SemaphoreInfo::StructureSize::FourWords: {
// Convert the current nanosecond time to GPU ticks // Convert the current nanosecond time to GPU ticks
constexpr u64 NsToTickNumerator = 384; constexpr u64 NsToTickNumerator = 384;
constexpr u64 NsToTickDenominator = 625; constexpr u64 NsToTickDenominator = 625;
@ -164,7 +168,7 @@ namespace skyline::gpu::engine {
u64 nsTime = util::GetTimeNs(); u64 nsTime = util::GetTimeNs();
u64 timestamp = (nsTime / NsToTickDenominator) * NsToTickNumerator + ((nsTime % NsToTickDenominator) * NsToTickNumerator) / NsToTickDenominator; 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; break;
} }
} }

View File

@ -9,11 +9,11 @@
#include <gpu/macro_interpreter.h> #include <gpu/macro_interpreter.h>
#include "engine.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 skyline {
namespace constant { 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 { namespace gpu::engine {
@ -40,7 +40,10 @@ namespace skyline {
* @brief This holds the Maxwell3D engine's register space * @brief This holds the Maxwell3D engine's register space
* @url https://github.com/devkitPro/deko3d/blob/master/source/maxwell/engine_3d.def#L478 * @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 { struct Address {
u32 high; u32 high;
u32 low; u32 low;
@ -77,7 +80,7 @@ namespace skyline {
float translateY; float translateY;
float translateZ; float translateZ;
struct __attribute__((__packed__)) { struct {
Swizzle x : 3; Swizzle x : 3;
u8 _pad0_ : 1; u8 _pad0_ : 1;
Swizzle y : 3; Swizzle y : 3;
@ -88,7 +91,7 @@ namespace skyline {
u32 _pad3_ : 17; u32 _pad3_ : 17;
} swizzles; } swizzles;
struct __attribute__((__packed__)) { struct {
u8 x : 5; u8 x : 5;
u8 _pad0_ : 3; u8 _pad0_ : 3;
u8 y : 5; u8 y : 5;
@ -120,6 +123,8 @@ namespace skyline {
}; };
union VertexAttribute { union VertexAttribute {
u32 raw;
enum class Size : u8 { enum class Size : u8 {
Size_1x32 = 0x12, Size_1x32 = 0x12,
Size_2x32 = 0x04, Size_2x32 = 0x04,
@ -148,7 +153,7 @@ namespace skyline {
Float = 7, Float = 7,
}; };
struct __attribute__((__packed__)) { struct {
u8 bufferId : 5; u8 bufferId : 5;
u8 _pad0_ : 1; u8 _pad0_ : 1;
bool fixed : 1; bool fixed : 1;
@ -158,8 +163,6 @@ namespace skyline {
u8 _pad1_ : 1; u8 _pad1_ : 1;
bool bgra : 1; bool bgra : 1;
}; };
u32 raw;
}; };
static_assert(sizeof(VertexAttribute) == sizeof(u32)); static_assert(sizeof(VertexAttribute) == sizeof(u32));
@ -275,19 +278,19 @@ namespace skyline {
FrontAndBack = 0x408, FrontAndBack = 0x408,
}; };
union ColorMask { union ColorWriteMask {
struct __attribute__((__packed__)) { u32 raw;
struct {
u8 r : 4; u8 r : 4;
u8 g : 4; u8 g : 4;
u8 b : 4; u8 b : 4;
u8 a : 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 { enum class Op : u8 {
Release = 0, Release = 0,
Acquire = 1, Acquire = 1,
@ -372,6 +375,11 @@ namespace skyline {
}; };
static_assert(sizeof(SemaphoreInfo) == sizeof(u32)); static_assert(sizeof(SemaphoreInfo) == sizeof(u32));
enum class CoordOrigin : u8 {
LowerLeft = 0,
UpperLeft = 1
};
struct { struct {
u32 _pad0_[0x40]; // 0x0 u32 _pad0_[0x40]; // 0x0
u32 noOperation; // 0x40 u32 noOperation; // 0x40
@ -412,9 +420,9 @@ namespace skyline {
u32 _pad6_[0x68]; // 0x36d u32 _pad6_[0x68]; // 0x36d
struct { struct {
u32 funcRef; // 0x3d5 u32 compareRef; // 0x3d5
u32 mask; // 0x3d6 u32 writeMask; // 0x3d6
u32 funcMask; // 0x3d7 u32 compareMask; // 0x3d7
} stencilBackExtra; } stencilBackExtra;
u32 _pad7_[0x13]; // 0x3d8 u32 _pad7_[0x13]; // 0x3d8
@ -461,9 +469,9 @@ namespace skyline {
CompareOp op; // 0x4e4 CompareOp op; // 0x4e4
i32 ref; // 0x4e5 i32 ref; // 0x4e5
u32 mask; // 0x4e6 u32 mask; // 0x4e6
} func; } compare;
u32 mask; // 0x4e7 u32 writeMask; // 0x4e7
} stencilFront; } stencilFront;
u32 _pad11_[0x4]; // 0x4e8 u32 _pad11_[0x4]; // 0x4e8
@ -484,7 +492,7 @@ namespace skyline {
u32 multisampleEnable; // 0x54d u32 multisampleEnable; // 0x54d
u32 depthTargetEnable; // 0x54e u32 depthTargetEnable; // 0x54e
struct __attribute__((__packed__)) { struct {
bool alphaToCoverage : 1; bool alphaToCoverage : 1;
u8 _pad0_ : 3; u8 _pad0_ : 3;
bool alphaToOne : 1; bool alphaToOne : 1;
@ -509,22 +517,34 @@ namespace skyline {
u32 _pad18_[0x5]; // 0x560 u32 _pad18_[0x5]; // 0x560
struct {
u32 stencilTwoSideEnable; // 0x565 u32 stencilTwoSideEnable; // 0x565
struct {
StencilOp failOp; // 0x566 StencilOp failOp; // 0x566
StencilOp zFailOp; // 0x567 StencilOp zFailOp; // 0x567
StencilOp zPassOp; // 0x568 StencilOp zPassOp; // 0x568
CompareOp funcOp; // 0x569 CompareOp compareOp; // 0x569
} stencilBack; } 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 u32 cullFaceEnable; // 0x646
FrontFace frontFace; // 0x647 FrontFace frontFace; // 0x647
CullFace cullFace; // 0x648 CullFace cullFace; // 0x648
u32 pixelCentreImage; // 0x649 u32 pixelCentreImage; // 0x649
u32 _pad20_[0x36]; // 0x64a u32 _pad21_; // 0x64a
std::array<ColorMask, 8> colorMask; // 0x680 For each render target u32 viewportTransformEnable; // 0x64b
u32 _pad21_[0x38]; // 0x688 u32 _pad22_[0x34]; // 0x64a
std::array<ColorWriteMask, 8> colorMask; // 0x680 For each render target
u32 _pad23_[0x38]; // 0x688
struct { struct {
Address address; // 0x6c0 Address address; // 0x6c0
@ -532,18 +552,17 @@ namespace skyline {
SemaphoreInfo info; // 0x6c3 SemaphoreInfo info; // 0x6c3
} semaphore; } semaphore;
u32 _pad22_[0xbc]; // 0x6c4 u32 _pad24_[0xbc]; // 0x6c4
std::array<Blend, 8> independentBlend; // 0x780 For each render target std::array<Blend, 8> independentBlend; // 0x780 For each render target
u32 _pad23_[0x100]; // 0x7c0 u32 _pad25_[0x100]; // 0x7c0
u32 firmwareCall[0x20]; // 0x8c0 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 Registers registers{}; //!< The maxwell 3D register space
Regs shadowRegs{}; //!< The shadow registers, their function is controlled by the 'shadowRamControl' register 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 std::array<u32, 0x10000> macroCode{}; //!< This is used to store GPU macros, the 256kb size is from Ryujinx

View File

@ -32,7 +32,7 @@ namespace skyline::gpu::gpfifo {
state.logger->Info("Bound GPU engine 0x{:X} to subchannel {}", params.argument, params.subChannel); state.logger->Info("Bound GPU engine 0x{:X} to subchannel {}", params.argument, params.subChannel);
return; return;
} else if (params.method < constant::GpfifoRegisterSize) { } else if (params.method < constant::GpfifoRegisterCount) {
gpfifoEngine.CallMethod(params); gpfifoEngine.CallMethod(params);
} else { } else {
if (subchannels.at(params.subChannel) == nullptr) if (subchannels.at(params.subChannel) == nullptr)

View File

@ -18,18 +18,19 @@ namespace skyline::gpu {
* @url https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/clb06f.h#L155 * @url https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/clb06f.h#L155
*/ */
struct GpEntry { struct GpEntry {
enum class Fetch { enum class Fetch : u8 {
Unconditional = 0, Unconditional = 0,
Conditional = 1, Conditional = 1,
}; };
union { union {
u32 entry0;
struct { struct {
Fetch fetch : 1; Fetch fetch : 1;
u8 _pad_ : 1; u8 _pad_ : 1;
u32 get : 30; u32 get : 30;
}; };
u32 entry0;
}; };
enum class Opcode : u8 { enum class Opcode : u8 {
@ -39,36 +40,38 @@ namespace skyline::gpu {
PbCrc = 3, PbCrc = 3,
}; };
enum class Priv { enum class Priv : u8 {
User = 0, User = 0,
Kernel = 1, Kernel = 1,
}; };
enum class Level { enum class Level : u8 {
Main = 0, Main = 0,
Subroutine = 1, Subroutine = 1,
}; };
enum class Sync { enum class Sync : u8 {
Proceed = 0, Proceed = 0,
Wait = 1, Wait = 1,
}; };
union { union {
u32 entry1;
struct { struct {
union { union {
u8 getHi; u8 getHi;
Opcode opcode; Opcode opcode;
}; };
Priv priv : 1; Priv priv : 1;
Level level : 1; Level level : 1;
u32 size : 21; u32 size : 21;
Sync sync : 1; 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 * @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 * @url https://github.com/NVIDIA/open-gpu-doc/blob/ab27fc22db5de0d02a4cabe08e555663b62db4d4/classes/host/clb06f.h#L179
*/ */
union PushBufferMethodHeader { union PushBufferMethodHeader {
u32 raw;
enum class TertOp : u8 { enum class TertOp : u8 {
Grp0IncMethod = 0, Grp0IncMethod = 0,
Grp0SetSubDevMask = 1, Grp0SetSubDevMask = 1,
@ -95,8 +100,6 @@ namespace skyline::gpu {
EndPbSegment = 7 EndPbSegment = 7
}; };
struct {
union {
u16 methodAddress : 12; u16 methodAddress : 12;
struct { struct {
u8 _pad0_ : 4; u8 _pad0_ : 4;
@ -118,10 +121,7 @@ namespace skyline::gpu {
SecOp secOp : 3; SecOp secOp : 3;
}; };
}; };
}; static_assert(sizeof(PushBufferMethodHeader) == sizeof(u32));
u32 entry;
};
static_assert(sizeof(PushBufferMethodHeader) == 0x4);
/** /**
* @brief The GPFIFO class handles creating pushbuffers from GP entries and then processing them * @brief The GPFIFO class handles creating pushbuffers from GP entries and then processing them

View File

@ -67,7 +67,7 @@ namespace skyline::gpu {
break; break;
} }
case Opcode::Operation::ReadImmediate: { 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); HandleAssignment(opcode->assignmentOperation, opcode->dest, result);
break; break;
} }

View File

@ -10,12 +10,18 @@ namespace skyline::gpu {
class Maxwell3D; 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 { class MacroInterpreter {
private: private:
/** /**
* @brief This holds a single macro opcode * @brief This holds a single macro opcode
*/ */
#pragma pack(push, 1)
union Opcode { union Opcode {
u32 raw;
enum class Operation : u8 { enum class Operation : u8 {
AluRegister = 0, AluRegister = 0,
AddImmediate = 1, AddImmediate = 1,
@ -54,16 +60,16 @@ namespace skyline::gpu {
NonZero = 1, NonZero = 1,
}; };
struct __attribute__((__packed__)) { struct {
Operation operation : 3; Operation operation : 3;
u8 _pad0_ : 1; u8 _pad0_ : 1;
AssignmentOperation assignmentOperation : 3; AssignmentOperation assignmentOperation : 3;
}; };
struct __attribute__((__packed__)) { struct {
u8 _pad1_ : 4; u8 _pad1_ : 4;
BranchCondition branchCondition : 1; BranchCondition branchCondition : 1;
u8 noDelay : 1; bool noDelay : 1;
u8 _pad2_ : 1; u8 _pad2_ : 1;
u8 exit : 1; u8 exit : 1;
u8 dest : 3; u8 dest : 3;
@ -72,12 +78,12 @@ namespace skyline::gpu {
AluOperation aluOperation : 5; AluOperation aluOperation : 5;
}; };
struct __attribute__((__packed__)) { struct {
u16 _pad3_ : 14; u16 _pad3_ : 14;
i32 immediate : 18; i32 immediate : 18;
}; };
struct __attribute__((__packed__)) { struct {
u32 _pad_ : 17; u32 _pad_ : 17;
u8 srcBit : 5; u8 srcBit : 5;
u8 size : 5; u8 size : 5;
@ -87,21 +93,20 @@ namespace skyline::gpu {
return (1 << size) - 1; return (1 << size) - 1;
} }
} bitfield; } bitfield;
u32 raw;
}; };
#pragma pack(pop)
static_assert(sizeof(Opcode) == sizeof(u32)); static_assert(sizeof(Opcode) == sizeof(u32));
/** /**
* @brief This holds information about the Maxwell 3D method to be called in 'Send' * @brief This holds information about the Maxwell 3D method to be called in 'Send'
*/ */
union MethodAddress { union MethodAddress {
u32 raw;
struct { struct {
u16 address : 12; u16 address : 12;
u8 increment : 6; u8 increment : 6;
}; };
u32 raw;
}; };
engine::Maxwell3D &maxwell3D; engine::Maxwell3D &maxwell3D;

View File

@ -97,8 +97,8 @@ namespace skyline::gpu::vmm {
} }
u64 MemoryManager::ReserveFixed(u64 address, u64 size) { u64 MemoryManager::ReserveFixed(u64 address, u64 size) {
if ((address & (constant::GpuPageSize - 1)) != 0) if (!util::IsAligned(address, constant::GpuPageSize))
return 0; return false;
size = util::AlignUp(size, constant::GpuPageSize); size = util::AlignUp(size, constant::GpuPageSize);
@ -120,8 +120,8 @@ namespace skyline::gpu::vmm {
} }
u64 MemoryManager::MapFixed(u64 address, u64 cpuAddress, u64 size) { u64 MemoryManager::MapFixed(u64 address, u64 cpuAddress, u64 size) {
if ((address & (constant::GpuPageSize - 1)) != 0) if (!util::IsAligned(address, constant::GpuPageSize))
return 0; return false;
size = util::AlignUp(size, constant::GpuPageSize); size = util::AlignUp(size, constant::GpuPageSize);
@ -129,7 +129,7 @@ namespace skyline::gpu::vmm {
} }
bool MemoryManager::Unmap(u64 address) { bool MemoryManager::Unmap(u64 address) {
if ((address & (constant::GpuPageSize - 1)) != 0) if (!util::IsAligned(address, constant::GpuPageSize))
return false; return false;
auto chunk = std::find_if(chunkList.begin(), chunkList.end(), [address](const ChunkDescriptor &chunk) -> bool { 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 { 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; return address < chunk.address;
}); });
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped) 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; chunk--;
u64 destinationOffset{};
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 // 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) { while (size) {
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped) state.process->ReadMemory(destination + (initialSize - size), readAddress, readSize);
throw exception("Failed to read region in GPU address space - address: {#:X} size: {#:X}", address, size);
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; size -= readSize;
destinationOffset += readSize; if (size) {
chunk++; 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 { 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; return address < chunk.address;
}); });
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped) 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; chunk--;
u64 sourceOffset{};
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 // 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) { while (size) {
if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped) state.process->WriteMemory(source + (initialSize - size), writeAddress, writeSize);
throw exception("Failed to write to region in GPU address space - address: {#:X} size: {#:X}", address, size);
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; size -= writeSize;
sourceOffset += writeSize; if (size) {
chunk++; 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);
}
} }
} }
} }

View File

@ -47,7 +47,6 @@ namespace skyline {
class MemoryManager { class MemoryManager {
private: private:
const DeviceState &state; 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 * @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: public:
MemoryManager(const DeviceState &state); 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 * @brief This reserves a region of the GPU address space so it will not be chosen automatically when mapping

View File

@ -7,10 +7,13 @@
namespace skyline { namespace skyline {
namespace constant { namespace constant {
constexpr size_t MaxHwSyncpointCount = 192; constexpr size_t MaxHwSyncpointCount = 192; //!< The maximum number of HOST1X syncpoints on t210
} }
namespace gpu { namespace gpu {
/**
* @brief The Syncpoint class represents a single syncpoint in the GPU which is used for GPU -> CPU synchronisation
*/
class Syncpoint { class Syncpoint {
private: private:
/** /**

View File

@ -4,7 +4,7 @@
#pragma once #pragma once
#include <common.h> #include <common.h>
#include "devices/nvhost_syncpoint.h" #include <services/nvdrv/devices/nvhost_syncpoint.h>
namespace skyline::service::nvdrv { namespace skyline::service::nvdrv {
/** /**

View File

@ -29,7 +29,7 @@ namespace skyline::service::fssrv {
void Write(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response); 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); void Flush(type::KSession &session, ipc::IpcRequest &request, ipc::IpcResponse &response);

View File

@ -5,7 +5,7 @@
#include <os.h> #include <os.h>
#include <kernel/types/KProcess.h> #include <kernel/types/KProcess.h>
#include <services/nvdrv/INvDrvServices.h> #include <services/nvdrv/INvDrvServices.h>
#include <services/nvdrv/fence.h> #include <services/common/fence.h>
#include <gpu/format.h> #include <gpu/format.h>
#include "IHOSBinderDriver.h" #include "IHOSBinderDriver.h"
#include "display.h" #include "display.h"

View File

@ -126,6 +126,8 @@ namespace skyline::service::nvdrv::device {
u32 pages; 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)}; size_t entryCount{buffer.input.at(0).size / sizeof(Entry)};
std::span entries(state.process->GetPointer<Entry>(buffer.input.at(0).address), entryCount); std::span entries(state.process->GetPointer<Entry>(buffer.input.at(0).address), entryCount);
@ -133,9 +135,9 @@ namespace skyline::service::nvdrv::device {
try { try {
auto nvmap = state.os->serviceManager.GetService<nvdrv::INvDrvServices>(Service::nvdrv_INvDrvServices)->GetDevice<nvdrv::device::NvMap>(nvdrv::device::NvDeviceType::nvmap)->handleTable.at(entry.nvmapHandle); 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 mapAddress = static_cast<u64>(entry.gpuOffset) << MinAlignmentShift;
u64 mapPhysicalAddress = nvmap->address + (static_cast<u64>(entry.mapOffset) << 0x10); u64 mapPhysicalAddress = nvmap->address + (static_cast<u64>(entry.mapOffset) << MinAlignmentShift);
u64 mapSize = static_cast<u64>(entry.pages) << 0x10; u64 mapSize = static_cast<u64>(entry.pages) << MinAlignmentShift;
state.gpu->memoryManager.MapFixed(mapAddress, mapPhysicalAddress, mapSize); state.gpu->memoryManager.MapFixed(mapAddress, mapPhysicalAddress, mapSize);
} catch (const std::exception &e) { } catch (const std::exception &e) {

View File

@ -3,7 +3,7 @@
#pragma once #pragma once
#include <services/nvdrv/fence.h> #include <services/common/fence.h>
#include "nvdevice.h" #include "nvdevice.h"
namespace skyline::service::nvdrv::device { namespace skyline::service::nvdrv::device {

View File

@ -45,65 +45,6 @@ namespace skyline::service::nvdrv::device {
{0x001F, NFUNC(NvHostCtrl::EventRegister)}, {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 NvHostCtrl::FindFreeEvent(u32 syncpointId) {
u32 eventIndex{constant::NvHostEventCount}; //!< Holds the index of the last populated event in the event array 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 u32 freeIndex{constant::NvHostEventCount}; //!< Holds the index of the first unused event id
@ -208,4 +149,65 @@ namespace skyline::service::nvdrv::device {
return; 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;
}
} }

View File

@ -3,7 +3,7 @@
#pragma once #pragma once
#include <services/nvdrv/fence.h> #include <services/common/fence.h>
#include "nvdevice.h" #include "nvdevice.h"
namespace skyline { namespace skyline {
@ -12,6 +12,9 @@ namespace skyline {
} }
namespace service::nvdrv::device { namespace service::nvdrv::device {
/**
* @brief This represents a single registered event with an attached fence
*/
class NvHostEvent { class NvHostEvent {
private: private:
u64 waiterId{}; u64 waiterId{};
@ -57,6 +60,8 @@ namespace skyline {
* @brief This holds metadata about an event, it is used by QueryEvent and EventWait * @brief This holds metadata about an event, it is used by QueryEvent and EventWait
*/ */
union EventValue { union EventValue {
u32 val;
struct { struct {
u8 _pad0_ : 4; u8 _pad0_ : 4;
u32 syncpointIdAsync : 28; u32 syncpointIdAsync : 28;
@ -71,8 +76,6 @@ namespace skyline {
bool nonAsync : 1; bool nonAsync : 1;
u8 _pad12_ : 3; u8 _pad12_ : 3;
}; };
u32 val;
}; };
static_assert(sizeof(EventValue) == sizeof(u32)); static_assert(sizeof(EventValue) == sizeof(u32));

View File

@ -9,7 +9,6 @@
namespace skyline::service::nvdrv { namespace skyline::service::nvdrv {
/** /**
* @todo Implement the GPU side of this
* @brief NvHostSyncpoint handles allocating and accessing host1x syncpoints * @brief NvHostSyncpoint handles allocating and accessing host1x syncpoints
* @url https://http.download.nvidia.com/tegra-public-appnotes/host1x.html * @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 * @url https://github.com/Jetson-TX1-AndroidTV/android_kernel_jetson_tx1_hdmi_primary/blob/jetson-tx1/drivers/video/tegra/host/nvhost_syncpt.c

View File

@ -28,7 +28,7 @@ namespace skyline::vfs {
* @param size The size of the file to create * @param size The size of the file to create
* @return Whether creating the file succeeded * @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"); 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 * @param parents Whether all parent directories in the given path should be created
* @return Whether creating the directory succeeded * @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"); 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 * @param mode The mode to open the file with
* @return A shared pointer to a Backing object of the file * @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 * @brief Queries the type of the entry given by path
* @param path The path to the entry * @param path The path to the entry
* @return The type of the entry, if present * @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 * @brief Checks if a given file exists in a filesystem
* @param path The path to the file * @param path The path to the file
* @return Whether the file exists * @return Whether the file exists
*/ */
inline bool FileExists(std::string path) { inline bool FileExists(const std::string &path) {
auto entry = GetEntryType(path); auto entry = GetEntryType(path);
return entry && *entry == Directory::EntryType::File; return entry && *entry == Directory::EntryType::File;
} }
@ -72,7 +72,7 @@ namespace skyline::vfs {
* @param path The path to the directory * @param path The path to the directory
* @return Whether the directory exists * @return Whether the directory exists
*/ */
inline bool DirectoryExists(std::string path) { inline bool DirectoryExists(const std::string &path) {
auto entry = GetEntryType(path); auto entry = GetEntryType(path);
return entry && *entry == Directory::EntryType::Directory; return entry && *entry == Directory::EntryType::Directory;
} }
@ -83,7 +83,7 @@ namespace skyline::vfs {
* @param listMode The list mode for the directory * @param listMode The list mode for the directory
* @return A shared pointer to a Directory object of 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"); throw exception("This filesystem does not support opening directories");
}; };
}; };

View File

@ -10,13 +10,13 @@
#include "os_filesystem.h" #include "os_filesystem.h"
namespace skyline::vfs { 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 (!DirectoryExists(basePath))
if (!CreateDirectory(basePath, true)) if (!CreateDirectory(basePath, true))
throw exception("Error creating the OS filesystem backing directory"); 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; auto fullPath = basePath + path;
// Create a directory that will hold the file // Create a directory that will hold the file
@ -39,7 +39,7 @@ namespace skyline::vfs {
return true; return true;
} }
bool OsFileSystem::CreateDirectory(std::string path, bool parents) { bool OsFileSystem::CreateDirectory(const std::string &path, bool parents) {
if (!parents) { if (!parents) {
int ret = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); int ret = mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
return ret == 0 || errno == EEXIST; return ret == 0 || errno == EEXIST;
@ -59,7 +59,7 @@ namespace skyline::vfs {
return true; 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)) if (!(mode.read || mode.write))
throw exception("Cannot open a file that is neither readable or writable"); 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); 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 fullPath = basePath + path;
auto directory = opendir(fullPath.c_str()); auto directory = opendir(fullPath.c_str());

View File

@ -14,14 +14,14 @@ namespace skyline::vfs {
std::string basePath; //!< The base path for filesystem operations std::string basePath; //!< The base path for filesystem operations
public: 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);
}; };
} }

View File

@ -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 { try {
auto &entry = fileMap.at(path); auto &entry = fileMap.at(path);
return std::make_shared<RegionBacking>(backing, fileDataOffset + entry.offset, entry.size, mode); 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)) if (fileMap.count(path))
return Directory::EntryType::File; return Directory::EntryType::File;
return std::nullopt; 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 // PFS doesn't have directories
if (path != "") if (path != "")
return nullptr; return nullptr;

View File

@ -52,11 +52,11 @@ namespace skyline::vfs {
public: public:
PartitionFileSystem(std::shared_ptr<Backing> backing); 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);
}; };
/** /**

View File

@ -11,7 +11,7 @@ namespace skyline::vfs {
TraverseDirectory(0, ""); TraverseDirectory(0, "");
} }
void RomFileSystem::TraverseFiles(u32 offset, std::string path) { void RomFileSystem::TraverseFiles(u32 offset, const std::string &path) {
RomFsFileEntry entry{}; RomFsFileEntry entry{};
do { do {
@ -29,7 +29,7 @@ namespace skyline::vfs {
} while (offset != constant::RomFsEmptyEntry); } while (offset != constant::RomFsEmptyEntry);
} }
void RomFileSystem::TraverseDirectory(u32 offset, std::string path) { void RomFileSystem::TraverseDirectory(u32 offset, const std::string &path) {
RomFsDirectoryEntry entry{}; RomFsDirectoryEntry entry{};
backing->Read(&entry, header.dirMetaTableOffset + offset); backing->Read(&entry, header.dirMetaTableOffset + offset);
@ -52,7 +52,7 @@ namespace skyline::vfs {
TraverseDirectory(entry.siblingOffset, path); 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 { try {
const auto &entry = fileMap.at(path); const auto &entry = fileMap.at(path);
return std::make_shared<RegionBacking>(backing, header.dataOffset + entry.offset, entry.size, mode); 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)) if (fileMap.count(path))
return Directory::EntryType::File; return Directory::EntryType::File;
else if (directoryMap.count(path)) else if (directoryMap.count(path))
@ -70,7 +70,7 @@ namespace skyline::vfs {
return std::nullopt; 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 { try {
auto &entry = directoryMap.at(path); auto &entry = directoryMap.at(path);
return std::make_shared<RomFileSystemDirectory>(backing, header, entry, listMode); return std::make_shared<RomFileSystemDirectory>(backing, header, entry, listMode);

View File

@ -23,14 +23,14 @@ namespace skyline {
* @param offset The offset of the file entry to traverses the siblings of * @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 * @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 * @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 offset The offset of the directory entry to traverses the directories in
* @param path The path to the supplied directory entry * @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: public:
/** /**
@ -79,11 +79,11 @@ namespace skyline {
RomFileSystem(std::shared_ptr<Backing> backing); 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);
}; };
/** /**