Fix metal renderer compilation

This commit is contained in:
wheremyfoodat 2025-08-03 16:58:02 +03:00
commit 964b5632d1
6 changed files with 153 additions and 151 deletions

View file

@ -1,27 +1,42 @@
#pragma once #pragma once
#include <list> #include <list>
#include <memory> #include <memory>
#include "kernel_types.hpp"
#include "helpers.hpp"
class Memory; class Memory;
enum class FcramRegion {
App = 0x100,
Sys = 0x200,
Base = 0x300,
};
struct FcramBlock {
u32 paddr;
s32 pages;
FcramBlock(u32 paddr, s32 pages) : paddr(paddr), pages(pages) {}
};
using FcramBlockList = std::list<FcramBlock>; using FcramBlockList = std::list<FcramBlock>;
class KFcram { class KFcram {
struct Region { struct Region {
struct Block { struct Block {
s32 pages; s32 pages;
s32 pageOffs; s32 pageOffset;
bool used; bool used;
Block(s32 pages, u32 pageOffs) : pages(pages), pageOffs(pageOffs), used(false) {} Block(s32 pages, u32 pageOffset) : pages(pages), pageOffset(pageOffset), used(false) {}
}; };
std::list<Block> blocks; std::list<Block> blocks;
u32 start; u32 start;
s32 pages; s32 pages;
s32 freePages; s32 freePages;
public:
public:
Region() : start(0), pages(0) {} Region() : start(0), pages(0) {}
void reset(u32 start, size_t size); void reset(u32 start, size_t size);
void alloc(std::list<FcramBlock>& out, s32 pages, bool linear); void alloc(std::list<FcramBlock>& out, s32 pages, bool linear);
@ -35,7 +50,8 @@ class KFcram {
Region appRegion, sysRegion, baseRegion; Region appRegion, sysRegion, baseRegion;
uint8_t* fcram; uint8_t* fcram;
std::unique_ptr<u32> refs; std::unique_ptr<u32> refs;
public:
public:
KFcram(Memory& memory); KFcram(Memory& memory);
void reset(size_t ramSize, size_t appSize, size_t sysSize, size_t baseSize); void reset(size_t ramSize, size_t appSize, size_t sysSize, size_t baseSize);
void alloc(FcramBlockList& out, s32 pages, FcramRegion region, bool linear); void alloc(FcramBlockList& out, s32 pages, FcramRegion region, bool linear);

View file

@ -1,99 +1,97 @@
#pragma once #pragma once
#include <array> #include <array>
#include <cstring> #include <cstring>
#include "fs/archive_base.hpp" #include "fs/archive_base.hpp"
#include "handles.hpp" #include "handles.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "result/result.hpp" #include "result/result.hpp"
enum class KernelObjectType : u8 { enum class KernelObjectType : u8 {
AddressArbiter, Archive, Directory, File, MemoryBlock, Process, ResourceLimit, Session, Dummy, AddressArbiter,
// Bundle waitable objects together in the enum to let the compiler optimize certain checks better Archive,
Event, Mutex, Port, Semaphore, Timer, Thread Directory,
File,
MemoryBlock,
Process,
ResourceLimit,
Session,
Dummy,
// Bundle waitable objects together in the enum to let the compiler optimize certain checks better
Event,
Mutex,
Port,
Semaphore,
Timer,
Thread
}; };
enum class ResourceLimitCategory : int { enum class ResourceLimitCategory : int { Application = 0, SystemApplet = 1, LibraryApplet = 2, Misc = 3 };
Application = 0,
SystemApplet = 1,
LibraryApplet = 2,
Misc = 3
};
// Reset types (for use with events and timers) // Reset types (for use with events and timers)
enum class ResetType { enum class ResetType {
OneShot = 0, // When the primitive is signaled, it will wake up exactly one thread and will clear itself automatically. OneShot = 0, // When the primitive is signaled, it will wake up exactly one thread and will clear itself automatically.
Sticky = 1, // When the primitive is signaled, it will wake up all threads and it won't clear itself automatically. Sticky = 1, // When the primitive is signaled, it will wake up all threads and it won't clear itself automatically.
Pulse = 2, // Only meaningful for timers: same as ONESHOT but it will periodically signal the timer instead of just once. Pulse = 2, // Only meaningful for timers: same as ONESHOT but it will periodically signal the timer instead of just once.
}; };
enum class ArbitrationType { enum class ArbitrationType { Signal = 0, WaitIfLess = 1, DecrementAndWaitIfLess = 2, WaitIfLessTimeout = 3, DecrementAndWaitIfLessTimeout = 4 };
Signal = 0,
WaitIfLess = 1,
DecrementAndWaitIfLess = 2,
WaitIfLessTimeout = 3,
DecrementAndWaitIfLessTimeout = 4
};
enum class ProcessorID : s32 { enum class ProcessorID : s32 {
AllCPUs = -1, AllCPUs = -1,
Default = -2, Default = -2,
AppCore = 0,
Syscore = 1,
New3DSExtra1 = 2,
New3DSExtra2 = 3
};
enum class FcramRegion { AppCore = 0,
App = 0x100, Syscore = 1,
Sys = 0x200, New3DSExtra1 = 2,
Base = 0x300 New3DSExtra2 = 3
}; };
struct AddressArbiter {}; struct AddressArbiter {};
struct ResourceLimits { struct ResourceLimits {
HorizonHandle handle; HorizonHandle handle;
s32 currentCommit = 0; s32 currentCommit = 0;
}; };
struct Process { struct Process {
// Resource limits for this process // Resource limits for this process
ResourceLimits limits; ResourceLimits limits;
// Process ID // Process ID
u32 id; u32 id;
Process(u32 id) : id(id) {} Process(u32 id) : id(id) {}
}; };
struct Event { struct Event {
// Some events (for now, only the DSP semaphore events) need to execute a callback when signalled // Some events (for now, only the DSP semaphore events) need to execute a callback when signalled
// This enum stores what kind of callback they should execute // This enum stores what kind of callback they should execute
enum class CallbackType : u32 { enum class CallbackType : u32 {
None, DSPSemaphore, None,
}; DSPSemaphore,
};
u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event u64 waitlist; // A bitfield where each bit symbolizes if the thread with thread with the corresponding index is waiting on the event
ResetType resetType = ResetType::OneShot; ResetType resetType = ResetType::OneShot;
CallbackType callback = CallbackType::None; CallbackType callback = CallbackType::None;
bool fired = false; bool fired = false;
Event(ResetType resetType) : resetType(resetType), waitlist(0) {} Event(ResetType resetType) : resetType(resetType), waitlist(0) {}
Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {} Event(ResetType resetType, CallbackType cb) : resetType(resetType), waitlist(0), callback(cb) {}
}; };
struct Port { struct Port {
static constexpr u32 maxNameLen = 11; static constexpr u32 maxNameLen = 11;
char name[maxNameLen + 1] = {}; char name[maxNameLen + 1] = {};
bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort. bool isPublic = false; // Setting name=NULL creates a private port not accessible from svcConnectToPort.
Port(const char* name) { Port(const char* name) {
// If the name is empty (ie the first char is the null terminator) then the port is private // If the name is empty (ie the first char is the null terminator) then the port is private
isPublic = name[0] != '\0'; isPublic = name[0] != '\0';
std::strncpy(this->name, name, maxNameLen); std::strncpy(this->name, name, maxNameLen);
} }
}; };
struct Session { struct Session {
@ -151,92 +149,90 @@ struct Thread {
}; };
static const char* kernelObjectTypeToString(KernelObjectType t) { static const char* kernelObjectTypeToString(KernelObjectType t) {
switch (t) { switch (t) {
case KernelObjectType::AddressArbiter: return "address arbiter"; case KernelObjectType::AddressArbiter: return "address arbiter";
case KernelObjectType::Archive: return "archive"; case KernelObjectType::Archive: return "archive";
case KernelObjectType::Directory: return "directory"; case KernelObjectType::Directory: return "directory";
case KernelObjectType::Event: return "event"; case KernelObjectType::Event: return "event";
case KernelObjectType::File: return "file"; case KernelObjectType::File: return "file";
case KernelObjectType::MemoryBlock: return "memory block"; case KernelObjectType::MemoryBlock: return "memory block";
case KernelObjectType::Port: return "port"; case KernelObjectType::Port: return "port";
case KernelObjectType::Process: return "process"; case KernelObjectType::Process: return "process";
case KernelObjectType::ResourceLimit: return "resource limit"; case KernelObjectType::ResourceLimit: return "resource limit";
case KernelObjectType::Session: return "session"; case KernelObjectType::Session: return "session";
case KernelObjectType::Mutex: return "mutex"; case KernelObjectType::Mutex: return "mutex";
case KernelObjectType::Semaphore: return "semaphore"; case KernelObjectType::Semaphore: return "semaphore";
case KernelObjectType::Thread: return "thread"; case KernelObjectType::Thread: return "thread";
case KernelObjectType::Dummy: return "dummy"; case KernelObjectType::Dummy: return "dummy";
default: return "unknown"; default: return "unknown";
} }
} }
struct Mutex { struct Mutex {
using Handle = HorizonHandle; using Handle = HorizonHandle;
u64 waitlist; // Refer to the getWaitlist function below for documentation u64 waitlist; // Refer to the getWaitlist function below for documentation
Handle ownerThread = 0; // Index of the thread that holds the mutex if it's locked Handle ownerThread = 0; // Index of the thread that holds the mutex if it's locked
Handle handle; // Handle of the mutex itself Handle handle; // Handle of the mutex itself
u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked u32 lockCount; // Number of times this mutex has been locked by its daddy. 0 = not locked
bool locked; bool locked;
Mutex(bool lock, Handle handle) : locked(lock), waitlist(0), lockCount(lock ? 1 : 0), handle(handle) {} Mutex(bool lock, Handle handle) : locked(lock), waitlist(0), lockCount(lock ? 1 : 0), handle(handle) {}
}; };
struct Semaphore { struct Semaphore {
u64 waitlist; // Refer to the getWaitlist function below for documentation u64 waitlist; // Refer to the getWaitlist function below for documentation
s32 availableCount; s32 availableCount;
s32 maximumCount; s32 maximumCount;
Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {} Semaphore(s32 initialCount, s32 maximumCount) : availableCount(initialCount), maximumCount(maximumCount), waitlist(0) {}
}; };
struct Timer { struct Timer {
u64 waitlist; // Refer to the getWaitlist function below for documentation u64 waitlist; // Refer to the getWaitlist function below for documentation
ResetType resetType = ResetType::OneShot; ResetType resetType = ResetType::OneShot;
u64 fireTick; // CPU tick the timer will be fired u64 fireTick; // CPU tick the timer will be fired
u64 interval; // Number of ns until the timer fires for the second and future times u64 interval; // Number of ns until the timer fires for the second and future times
bool fired; // Has this timer been signalled? bool fired; // Has this timer been signalled?
bool running; // Is this timer running or stopped? bool running; // Is this timer running or stopped?
Timer(ResetType type) : resetType(type), fireTick(0), interval(0), waitlist(0), fired(false), running(false) {} Timer(ResetType type) : resetType(type), fireTick(0), interval(0), waitlist(0), fired(false), running(false) {}
}; };
struct MemoryBlock { struct MemoryBlock {
u32 addr = 0; u32 addr = 0;
u32 size = 0; u32 size = 0;
u32 myPermission = 0; u32 myPermission = 0;
u32 otherPermission = 0; u32 otherPermission = 0;
bool mapped = false; bool mapped = false;
MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm) : addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm), MemoryBlock(u32 addr, u32 size, u32 myPerm, u32 otherPerm)
mapped(false) {} : addr(addr), size(size), myPermission(myPerm), otherPermission(otherPerm), mapped(false) {}
}; };
// Generic kernel object class // Generic kernel object class
struct KernelObject { struct KernelObject {
using Handle = HorizonHandle; using Handle = HorizonHandle;
Handle handle = 0; // A u32 the OS will use to identify objects Handle handle = 0; // A u32 the OS will use to identify objects
void* data = nullptr; void* data = nullptr;
KernelObjectType type; KernelObjectType type;
KernelObject(Handle handle, KernelObjectType type) : handle(handle), type(type) {} KernelObject(Handle handle, KernelObjectType type) : handle(handle), type(type) {}
// Our destructor does not free the data in order to avoid it being freed when our std::vector is expanded // Our destructor does not free the data in order to avoid it being freed when our std::vector is expanded
// Thus, the kernel needs to delete it when appropriate // Thus, the kernel needs to delete it when appropriate
~KernelObject() {} ~KernelObject() {}
template <typename T> template <typename T>
T* getData() { T* getData() {
return static_cast<T*>(data); return static_cast<T*>(data);
} }
const char* getTypeName() const { const char* getTypeName() const { return kernelObjectTypeToString(type); }
return kernelObjectTypeToString(type);
}
// Retrieves a reference to the waitlist for a specified object // Retrieves a reference to the waitlist for a specified object
// We return a reference because this function is only called in the kernel threading internals // We return a reference because this function is only called in the kernel threading internals
// We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits. // We want the kernel to be able to easily manage waitlists, by reading/parsing them or setting/clearing bits.
// As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient. // As we mention in the definition of the "Event" struct, the format for wailists is very simple and made to be efficient.
@ -252,15 +248,7 @@ struct KernelObject {
case KernelObjectType::Timer: return getData<Timer>()->waitlist; case KernelObjectType::Timer: return getData<Timer>()->waitlist;
// This should be unreachable once we fully implement sync objects // This should be unreachable once we fully implement sync objects
default: [[unlikely]] default: [[unlikely]] Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName());
Helpers::panic("Called GetWaitList on kernel object without a waitlist (Type: %s)", getTypeName());
} }
} }
}; };
struct FcramBlock {
u32 paddr;
s32 pages;
FcramBlock(u32 paddr, s32 pages) : paddr(paddr), pages(pages) {}
};

View file

@ -11,7 +11,7 @@
#include "handles.hpp" #include "handles.hpp"
#include "helpers.hpp" #include "helpers.hpp"
#include "host_memory/host_memory.h" #include "host_memory/host_memory.h"
#include "kernel/kernel_types.hpp" #include "kernel/fcram.hpp"
#include "loader/3dsx.hpp" #include "loader/3dsx.hpp"
#include "loader/ncsd.hpp" #include "loader/ncsd.hpp"
#include "result/result.hpp" #include "result/result.hpp"

View file

@ -14,10 +14,10 @@ namespace PICA {
bool needsSwizzle = false; bool needsSwizzle = false;
MTL::TextureSwizzleChannels swizzle{ MTL::TextureSwizzleChannels swizzle{
.red = MTL::TextureSwizzleRed, MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleGreen, MTL::TextureSwizzleGreen,
.blue = MTL::TextureSwizzleBlue, MTL::TextureSwizzleBlue,
.alpha = MTL::TextureSwizzleAlpha, MTL::TextureSwizzleAlpha,
}; };
}; };
@ -33,7 +33,7 @@ namespace PICA {
case ColorFmt::RGBA5551: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatBGR5A1Unorm? case ColorFmt::RGBA5551: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatBGR5A1Unorm?
case ColorFmt::RGB565: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatB5G6R5Unorm? case ColorFmt::RGB565: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatB5G6R5Unorm?
#ifdef PANDA3DS_IOS #ifdef PANDA3DS_IOS
case ColorFmt::RGBA4: return MTL::PixelFormatRGBA8Unorm; // IOS + Metal doesn't support AGBR4 properly, at least on simulator case ColorFmt::RGBA4: return MTL::PixelFormatRGBA8Unorm; // IOS + Metal doesn't support AGBR4 properly, at least on simulator
#else #else
case ColorFmt::RGBA4: return MTL::PixelFormatABGR4Unorm; case ColorFmt::RGBA4: return MTL::PixelFormatABGR4Unorm;
#endif #endif
@ -130,8 +130,7 @@ namespace PICA {
case PrimType::TriangleFan: case PrimType::TriangleFan:
Helpers::warn("Triangle fans are not supported on Metal, using triangles instead"); Helpers::warn("Triangle fans are not supported on Metal, using triangles instead");
return MTL::PrimitiveTypeTriangle; return MTL::PrimitiveTypeTriangle;
case PrimType::GeometryPrimitive: case PrimType::GeometryPrimitive: return MTL::PrimitiveTypeTriangle;
return MTL::PrimitiveTypeTriangle;
} }
} }

View file

@ -2,7 +2,6 @@
#include "memory.hpp" #include "memory.hpp"
void KFcram::Region::reset(u32 start, size_t size) { void KFcram::Region::reset(u32 start, size_t size) {
this->start = start; this->start = start;
pages = size >> 12; pages = size >> 12;
@ -22,7 +21,7 @@ void KFcram::Region::alloc(std::list<FcramBlock>& out, s32 allocPages, bool line
// If the current block is bigger than the allocation, split it // If the current block is bigger than the allocation, split it
if (it->pages > allocPages) { if (it->pages > allocPages) {
Block newBlock(it->pages - allocPages, it->pageOffs + allocPages); Block newBlock(it->pages - allocPages, it->pageOffset + allocPages);
it->pages = allocPages; it->pages = allocPages;
blocks.insert(it, newBlock); blocks.insert(it, newBlock);
} }
@ -32,7 +31,7 @@ void KFcram::Region::alloc(std::list<FcramBlock>& out, s32 allocPages, bool line
allocPages -= it->pages; allocPages -= it->pages;
freePages -= it->pages; freePages -= it->pages;
u32 paddr = start + (it->pageOffs << 12); u32 paddr = start + (it->pageOffset << 12);
FcramBlock outBlock(paddr, it->pages); FcramBlock outBlock(paddr, it->pages);
out.push_back(outBlock); out.push_back(outBlock);

View file

@ -16,27 +16,27 @@ namespace PICA {
decodeTexelAI8ToRG8, decodeTexelAI8ToRG8,
true, true,
{ {
.red = MTL::TextureSwizzleRed, MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleRed, MTL::TextureSwizzleRed,
.blue = MTL::TextureSwizzleRed, MTL::TextureSwizzleRed,
.alpha = MTL::TextureSwizzleGreen, MTL::TextureSwizzleGreen,
}}, // IA8 }}, // IA8
{MTL::PixelFormatRG8Unorm, 2, decodeTexelGR8ToRG8}, // RG8 {MTL::PixelFormatRG8Unorm, 2, decodeTexelGR8ToRG8}, // RG8
{MTL::PixelFormatR8Unorm, {MTL::PixelFormatR8Unorm,
1, 1,
decodeTexelI8ToR8, decodeTexelI8ToR8,
true, true,
{.red = MTL::TextureSwizzleRed, .green = MTL::TextureSwizzleRed, .blue = MTL::TextureSwizzleRed, .alpha = MTL::TextureSwizzleOne}}, // I8 {MTL::TextureSwizzleRed, MTL::TextureSwizzleRed, MTL::TextureSwizzleRed, MTL::TextureSwizzleOne}}, // I8
{MTL::PixelFormatA8Unorm, 1, decodeTexelA8ToA8}, // A8 {MTL::PixelFormatA8Unorm, 1, decodeTexelA8ToA8}, // A8
{MTL::PixelFormatABGR4Unorm, 2, decodeTexelAI4ToABGR4}, // IA4 {MTL::PixelFormatABGR4Unorm, 2, decodeTexelAI4ToABGR4}, // IA4
{MTL::PixelFormatR8Unorm, {MTL::PixelFormatR8Unorm,
1, 1,
decodeTexelI4ToR8, decodeTexelI4ToR8,
true, true,
{.red = MTL::TextureSwizzleRed, .green = MTL::TextureSwizzleRed, .blue = MTL::TextureSwizzleRed, .alpha = MTL::TextureSwizzleOne}}, // I4 {MTL::TextureSwizzleRed, MTL::TextureSwizzleRed, MTL::TextureSwizzleRed, MTL::TextureSwizzleOne}}, // I4
{MTL::PixelFormatA8Unorm, 1, decodeTexelA4ToA8}, // A4 {MTL::PixelFormatA8Unorm, 1, decodeTexelA4ToA8}, // A4
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1ToRGBA8}, // ETC1 {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1ToRGBA8}, // ETC1
{MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1A4ToRGBA8}, // ETC1A4 {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelETC1A4ToRGBA8}, // ETC1A4
}; };
void checkForMTLPixelFormatSupport(MTL::Device* device) { void checkForMTLPixelFormatSupport(MTL::Device* device) {
@ -57,10 +57,10 @@ namespace PICA {
decodeTexelAI4ToRG8, decodeTexelAI4ToRG8,
true, true,
{ {
.red = MTL::TextureSwizzleRed, MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleRed, MTL::TextureSwizzleRed,
.blue = MTL::TextureSwizzleRed, MTL::TextureSwizzleRed,
.alpha = MTL::TextureSwizzleGreen, MTL::TextureSwizzleGreen,
} }
}; };
} }