mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-05-28 01:19:13 +12:00
Merge branch 'master' into tev-uniforms
This commit is contained in:
commit
6b8c7ede31
98 changed files with 1954 additions and 262544 deletions
|
@ -14,8 +14,11 @@
|
|||
|
||||
class GPU {
|
||||
static constexpr u32 regNum = 0x300;
|
||||
static constexpr u32 extRegNum = 0x1000;
|
||||
|
||||
using vec4f = std::array<Floats::f24, 4>;
|
||||
using Registers = std::array<u32, regNum>;
|
||||
using Registers = std::array<u32, regNum>; // Internal registers (named registers in short since they're the main ones)
|
||||
using ExternalRegisters = std::array<u32, extRegNum>;
|
||||
|
||||
Memory& mem;
|
||||
EmulatorConfig& config;
|
||||
|
@ -91,12 +94,16 @@ class GPU {
|
|||
void reset();
|
||||
|
||||
Registers& getRegisters() { return regs; }
|
||||
ExternalRegisters& getExtRegisters() { return externalRegs; }
|
||||
void startCommandList(u32 addr, u32 size);
|
||||
|
||||
// Used by the GSP GPU service for readHwRegs/writeHwRegs/writeHwRegsMasked
|
||||
u32 readReg(u32 address);
|
||||
void writeReg(u32 address, u32 value);
|
||||
|
||||
u32 readExternalReg(u32 index);
|
||||
void writeExternalReg(u32 index, u32 value);
|
||||
|
||||
// Used when processing GPU command lists
|
||||
u32 readInternalReg(u32 index);
|
||||
void writeInternalReg(u32 index, u32 value, u32 mask);
|
||||
|
@ -111,6 +118,10 @@ class GPU {
|
|||
renderer->displayTransfer(inputAddr, outputAddr, inputSize, outputSize, flags);
|
||||
}
|
||||
|
||||
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) {
|
||||
renderer->textureCopy(inputAddr, outputAddr, totalBytes, inputSize, outputSize, flags);
|
||||
}
|
||||
|
||||
// Read a value of type T from physical address paddr
|
||||
// This is necessary because vertex attribute fetching uses physical addresses
|
||||
template <typename T>
|
||||
|
@ -140,4 +151,10 @@ class GPU {
|
|||
Helpers::panic("[GPU] Tried to access unknown physical address: %08X", paddr);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// GPU external registers
|
||||
// We have them in the end of the struct for cache locality reasons. Tl;dr we want the more commonly used things to be packed in the start
|
||||
// Of the struct, instead of externalRegs being in the middle
|
||||
ExternalRegisters externalRegs;
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ namespace PICA {
|
|||
ShaderOutputCount = 0x4F,
|
||||
ShaderOutmap0 = 0x50,
|
||||
|
||||
ViewportXY = 0x68,
|
||||
DepthmapEnable = 0x6D,
|
||||
|
||||
// Texture registers
|
||||
|
@ -178,6 +179,53 @@ namespace PICA {
|
|||
};
|
||||
}
|
||||
|
||||
namespace ExternalRegs {
|
||||
enum : u32 {
|
||||
MemFill1BufferStartPaddr = 0x3,
|
||||
MemFill1BufferEndPAddr = 0x4,
|
||||
MemFill1Value = 0x5,
|
||||
MemFill1Control = 0x6,
|
||||
MemFill2BufferStartPaddr = 0x7,
|
||||
MemFill2BufferEndPAddr = 0x8,
|
||||
MemFill2Value = 0x9,
|
||||
MemFill2Control = 0xA,
|
||||
VramBankControl = 0xB,
|
||||
GPUBusy = 0xC,
|
||||
BacklightControl = 0xBC,
|
||||
Framebuffer0Size = 0x118,
|
||||
Framebuffer0AFirstAddr = 0x119,
|
||||
Framebuffer0ASecondAddr = 0x11A,
|
||||
Framebuffer0Config = 0x11B,
|
||||
Framebuffer0Select = 0x11D,
|
||||
Framebuffer0Stride = 0x123,
|
||||
Framebuffer0BFirstAddr = 0x124,
|
||||
Framebuffer0BSecondAddr = 0x125,
|
||||
Framebuffer1Size = 0x156,
|
||||
Framebuffer1AFirstAddr = 0x159,
|
||||
Framebuffer1ASecondAddr = 0x15A,
|
||||
Framebuffer1Config = 0x15B,
|
||||
Framebuffer1Select = 0x15D,
|
||||
Framebuffer1Stride = 0x163,
|
||||
Framebuffer1BFirstAddr = 0x164,
|
||||
Framebuffer1BSecondAddr = 0x165,
|
||||
TransferInputPAddr = 0x2FF,
|
||||
TransferOutputPAddr = 0x300,
|
||||
DisplayTransferOutputDim = 0x301,
|
||||
DisplayTransferInputDim = 0x302,
|
||||
TransferFlags = 0x303,
|
||||
TransferTrigger = 0x305,
|
||||
TextureCopyTotalBytes = 0x307,
|
||||
TextureCopyInputLineGap = 0x308,
|
||||
TextureCopyOutputLineGap = 0x309,
|
||||
};
|
||||
}
|
||||
|
||||
enum class Scaling : u32 {
|
||||
None = 0,
|
||||
X = 1,
|
||||
XY = 2,
|
||||
};
|
||||
|
||||
namespace Lights {
|
||||
enum : u32 {
|
||||
LUT_D0 = 0,
|
||||
|
|
|
@ -159,6 +159,7 @@ class PICAShader {
|
|||
void mul(u32 instruction);
|
||||
void rcp(u32 instruction);
|
||||
void rsq(u32 instruction);
|
||||
void sge(u32 instruction);
|
||||
void sgei(u32 instruction);
|
||||
void slt(u32 instruction);
|
||||
void slti(u32 instruction);
|
||||
|
|
|
@ -5,10 +5,17 @@
|
|||
|
||||
// Remember to initialize every field here to its default value otherwise bad things will happen
|
||||
struct EmulatorConfig {
|
||||
bool shaderJitEnabled = false;
|
||||
bool shaderJitEnabled = true;
|
||||
bool discordRpcEnabled = false;
|
||||
RendererType rendererType = RendererType::OpenGL;
|
||||
|
||||
bool sdCardInserted = true;
|
||||
bool sdWriteProtected = false;
|
||||
|
||||
bool chargerPlugged = true;
|
||||
// Default to 3% battery to make users suffer
|
||||
int batteryPercentage = 3;
|
||||
|
||||
EmulatorConfig(const std::filesystem::path& path);
|
||||
void load(const std::filesystem::path& path);
|
||||
void save(const std::filesystem::path& path);
|
||||
|
|
|
@ -105,6 +105,7 @@ namespace Crypto {
|
|||
AESEngine() {}
|
||||
void loadKeys(const std::filesystem::path& path);
|
||||
bool haveKeys() { return keysLoaded; }
|
||||
bool haveGenerator() { return m_generator.has_value(); }
|
||||
|
||||
constexpr bool hasKeyX(std::size_t slotId) {
|
||||
if (slotId >= AesKeySlotCount) {
|
||||
|
|
|
@ -5,7 +5,7 @@ class SaveDataArchive : public ArchiveBase {
|
|||
public:
|
||||
SaveDataArchive(Memory& mem) : ArchiveBase(mem) {}
|
||||
|
||||
u64 getFreeBytes() override { Helpers::panic("SaveData::GetFreeBytes unimplemented"); return 0; }
|
||||
u64 getFreeBytes() override { return 32_MB; }
|
||||
std::string name() override { return "SaveData"; }
|
||||
|
||||
HorizonResult createDirectory(const FSPath& path) override;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "termcolor.hpp"
|
||||
|
||||
|
@ -30,6 +31,17 @@ using s32 = std::int32_t;
|
|||
using s64 = std::int64_t;
|
||||
|
||||
namespace Helpers {
|
||||
template <class... Args>
|
||||
std::string format(const std::string& fmt, Args&&... args) {
|
||||
const int size = std::snprintf(nullptr, 0, fmt.c_str(), args...) + 1;
|
||||
if (size <= 0) {
|
||||
return {};
|
||||
}
|
||||
const auto buf = std::make_unique<char[]>(size);
|
||||
std::snprintf(buf.get(), size, fmt.c_str(), args ...);
|
||||
return std::string(buf.get(), buf.get() + size - 1);
|
||||
}
|
||||
|
||||
// Unconditional panic, unlike panicDev which does not panic on user builds
|
||||
template <class... Args>
|
||||
[[noreturn]] static void panic(const char* fmt, Args&&... args) {
|
||||
|
|
|
@ -19,23 +19,26 @@ namespace KernelHandles {
|
|||
CECD, // More Streetpass stuff?
|
||||
CFG_U, // CFG service (Console & region info)
|
||||
CFG_I,
|
||||
DLP_SRVR, // Download Play: Server. Used for network play.
|
||||
DSP, // DSP service (Used for audio decoding and output)
|
||||
HID, // HID service (Handles input-related things including gyro. Does NOT handle New3DS controls or CirclePadPro)
|
||||
HTTP, // HTTP service (Handles HTTP requests)
|
||||
IR_USER, // One of 3 infrared communication services
|
||||
FRD, // Friend service (Miiverse friend service)
|
||||
FS, // Filesystem service
|
||||
GPU, // GPU service
|
||||
LCD, // LCD service (Used for configuring the displays)
|
||||
LDR_RO, // Loader service. Used for loading CROs.
|
||||
MIC, // MIC service (Controls the microphone)
|
||||
NFC, // NFC (Duh), used for Amiibo
|
||||
NIM, // Updates, DLC, etc
|
||||
NDM, // ?????
|
||||
PTM, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state)
|
||||
SOC, // Socket service
|
||||
Y2R, // Also does camera stuff
|
||||
DLP_SRVR, // Download Play: Server. Used for network play.
|
||||
DSP, // DSP service (Used for audio decoding and output)
|
||||
HID, // HID service (Handles input-related things including gyro. Does NOT handle New3DS controls or CirclePadPro)
|
||||
HTTP, // HTTP service (Handles HTTP requests)
|
||||
IR_USER, // One of 3 infrared communication services
|
||||
FRD, // Friend service (Miiverse friend service)
|
||||
FS, // Filesystem service
|
||||
GPU, // GPU service
|
||||
LCD, // LCD service (Used for configuring the displays)
|
||||
LDR_RO, // Loader service. Used for loading CROs.
|
||||
MCU_HWC, // Used for various MCU hardware-related things like battery control
|
||||
MIC, // MIC service (Controls the microphone)
|
||||
NFC, // NFC (Duh), used for Amiibo
|
||||
NIM, // Updates, DLC, etc
|
||||
NDM, // ?????
|
||||
NEWS_U, // This service literally has 1 command (AddNotification) and I don't even understand what it does
|
||||
PTM, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state)
|
||||
SOC, // Socket service
|
||||
SSL, // SSL service (Totally didn't expect that)
|
||||
Y2R, // Also does camera stuff
|
||||
|
||||
MinServiceHandle = AC,
|
||||
MaxServiceHandle = Y2R,
|
||||
|
@ -80,12 +83,15 @@ namespace KernelHandles {
|
|||
case GPU: return "GSP::GPU";
|
||||
case LCD: return "GSP::LCD";
|
||||
case LDR_RO: return "LDR:RO";
|
||||
case MCU_HWC: return "MCU::HWC";
|
||||
case MIC: return "MIC";
|
||||
case NDM: return "NDM";
|
||||
case NEWS_U: return "NEWS_U";
|
||||
case NFC: return "NFC";
|
||||
case NIM: return "NIM";
|
||||
case PTM: return "PTM";
|
||||
case SOC: return "SOC";
|
||||
case SSL: return "SSL";
|
||||
case Y2R: return "Y2R";
|
||||
default: return "Unknown";
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
|
@ -60,13 +61,14 @@ class Kernel {
|
|||
Handle makeProcess(u32 id);
|
||||
Handle makePort(const char* name);
|
||||
Handle makeSession(Handle port);
|
||||
Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, s32 id, u32 arg,ThreadStatus status = ThreadStatus::Dormant);
|
||||
Handle makeThread(u32 entrypoint, u32 initialSP, u32 priority, ProcessorID id, u32 arg,ThreadStatus status = ThreadStatus::Dormant);
|
||||
Handle makeMemoryBlock(u32 addr, u32 size, u32 myPermission, u32 otherPermission);
|
||||
|
||||
public:
|
||||
Handle makeEvent(ResetType resetType); // Needs to be public to be accessible to the APT/HID services
|
||||
Handle makeMutex(bool locked = false); // Needs to be public to be accessible to the APT/DSP services
|
||||
Handle makeSemaphore(u32 initialCount, u32 maximumCount); // Needs to be public to be accessible to the service manager port
|
||||
Handle makeTimer(ResetType resetType);
|
||||
|
||||
// Signals an event, returns true on success or false if the event does not exist
|
||||
bool signalEvent(Handle e);
|
||||
|
@ -124,6 +126,7 @@ private:
|
|||
void exitThread();
|
||||
void mapMemoryBlock();
|
||||
void queryMemory();
|
||||
void getCurrentProcessorNumber();
|
||||
void getProcessID();
|
||||
void getProcessInfo();
|
||||
void getResourceLimit();
|
||||
|
@ -132,17 +135,22 @@ private:
|
|||
void getSystemInfo();
|
||||
void getSystemTick();
|
||||
void getThreadID();
|
||||
void getThreadIdealProcessor();
|
||||
void getThreadPriority();
|
||||
void sendSyncRequest();
|
||||
void setThreadPriority();
|
||||
void svcCancelTimer();
|
||||
void svcClearEvent();
|
||||
void svcClearTimer();
|
||||
void svcCloseHandle();
|
||||
void svcCreateEvent();
|
||||
void svcCreateMutex();
|
||||
void svcCreateSemaphore();
|
||||
void svcCreateTimer();
|
||||
void svcReleaseMutex();
|
||||
void svcReleaseSemaphore();
|
||||
void svcSignalEvent();
|
||||
void svcSetTimer();
|
||||
void svcSleepThread();
|
||||
void connectToPort();
|
||||
void outputDebugString();
|
||||
|
@ -166,7 +174,7 @@ private:
|
|||
void readDirectory(u32 messagePointer, Handle directory);
|
||||
|
||||
public:
|
||||
Kernel(CPU& cpu, Memory& mem, GPU& gpu);
|
||||
Kernel(CPU& cpu, Memory& mem, GPU& gpu, const EmulatorConfig& config);
|
||||
void initializeFS() { return serviceManager.initializeFS(); }
|
||||
void setVersion(u8 major, u8 minor);
|
||||
void serviceSVC(u32 svc);
|
||||
|
|
|
@ -34,6 +34,16 @@ enum class ArbitrationType {
|
|||
DecrementAndWaitIfLessTimeout = 4
|
||||
};
|
||||
|
||||
enum class ProcessorID : s32 {
|
||||
AllCPUs = -1,
|
||||
Default = -2,
|
||||
|
||||
AppCore = 0,
|
||||
Syscore = 1,
|
||||
New3DSExtra1 = 2,
|
||||
New3DSExtra2 = 3
|
||||
};
|
||||
|
||||
struct AddressArbiter {};
|
||||
|
||||
struct ResourceLimits {
|
||||
|
@ -95,7 +105,7 @@ struct Thread {
|
|||
u32 entrypoint; // Initial r15 value
|
||||
u32 priority;
|
||||
u32 arg;
|
||||
s32 processorID;
|
||||
ProcessorID processorID;
|
||||
ThreadStatus status;
|
||||
Handle handle; // OS handle for this thread
|
||||
int index; // Index of the thread. 0 for the first thread, 1 for the second, and so on
|
||||
|
|
|
@ -2,77 +2,84 @@
|
|||
#include <array>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include "io_file.hpp"
|
||||
#include "helpers.hpp"
|
||||
|
||||
#include "crypto/aes_engine.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "io_file.hpp"
|
||||
#include "services/region_codes.hpp"
|
||||
|
||||
struct NCCH {
|
||||
struct EncryptionInfo {
|
||||
Crypto::AESKey normalKey;
|
||||
Crypto::AESKey initialCounter;
|
||||
};
|
||||
struct EncryptionInfo {
|
||||
Crypto::AESKey normalKey;
|
||||
Crypto::AESKey initialCounter;
|
||||
};
|
||||
|
||||
struct FSInfo { // Info on the ExeFS/RomFS
|
||||
u64 offset = 0;
|
||||
u64 size = 0;
|
||||
u64 hashRegionSize = 0;
|
||||
std::optional<EncryptionInfo> encryptionInfo;
|
||||
};
|
||||
struct FSInfo { // Info on the ExeFS/RomFS
|
||||
u64 offset = 0;
|
||||
u64 size = 0;
|
||||
u64 hashRegionSize = 0;
|
||||
std::optional<EncryptionInfo> encryptionInfo;
|
||||
};
|
||||
|
||||
// Descriptions for .text, .data and .rodata sections
|
||||
struct CodeSetInfo {
|
||||
u32 address = 0;
|
||||
u32 pageCount = 0;
|
||||
u32 size = 0;
|
||||
// Descriptions for .text, .data and .rodata sections
|
||||
struct CodeSetInfo {
|
||||
u32 address = 0;
|
||||
u32 pageCount = 0;
|
||||
u32 size = 0;
|
||||
|
||||
// Extract the code set info from the relevant header data
|
||||
void extract(const u8* headerEntry) {
|
||||
address = *(u32*)&headerEntry[0];
|
||||
pageCount = *(u32*)&headerEntry[4];
|
||||
size = *(u32*)&headerEntry[8];
|
||||
}
|
||||
};
|
||||
// Extract the code set info from the relevant header data
|
||||
void extract(const u8 *headerEntry) {
|
||||
address = *(u32 *)&headerEntry[0];
|
||||
pageCount = *(u32 *)&headerEntry[4];
|
||||
size = *(u32 *)&headerEntry[8];
|
||||
}
|
||||
};
|
||||
|
||||
u64 partitionIndex = 0;
|
||||
u64 fileOffset = 0;
|
||||
u64 partitionIndex = 0;
|
||||
u64 fileOffset = 0;
|
||||
|
||||
bool isNew3DS = false;
|
||||
bool initialized = false;
|
||||
bool compressCode = false; // Shows whether the .code file in the ExeFS is compressed
|
||||
bool mountRomFS = false;
|
||||
bool encrypted = false;
|
||||
bool fixedCryptoKey = false;
|
||||
bool seedCrypto = false;
|
||||
u8 secondaryKeySlot = 0;
|
||||
bool isNew3DS = false;
|
||||
bool initialized = false;
|
||||
bool compressCode = false; // Shows whether the .code file in the ExeFS is compressed
|
||||
bool mountRomFS = false;
|
||||
bool encrypted = false;
|
||||
bool fixedCryptoKey = false;
|
||||
bool seedCrypto = false;
|
||||
u8 secondaryKeySlot = 0;
|
||||
|
||||
static constexpr u64 mediaUnit = 0x200;
|
||||
u64 size = 0; // Size of NCCH converted to bytes
|
||||
u32 stackSize = 0;
|
||||
u32 bssSize = 0;
|
||||
u32 exheaderSize = 0;
|
||||
static constexpr u64 mediaUnit = 0x200;
|
||||
u64 size = 0; // Size of NCCH converted to bytes
|
||||
u32 stackSize = 0;
|
||||
u32 bssSize = 0;
|
||||
u32 exheaderSize = 0;
|
||||
|
||||
FSInfo exheaderInfo;
|
||||
FSInfo exeFS;
|
||||
FSInfo romFS;
|
||||
CodeSetInfo text, data, rodata;
|
||||
FSInfo exheaderInfo;
|
||||
FSInfo exeFS;
|
||||
FSInfo romFS;
|
||||
CodeSetInfo text, data, rodata;
|
||||
|
||||
// Contents of the .code file in the ExeFS
|
||||
std::vector<u8> codeFile;
|
||||
// Contains of the cart's save data
|
||||
std::vector<u8> saveData;
|
||||
// Contents of the .code file in the ExeFS
|
||||
std::vector<u8> codeFile;
|
||||
// Contains of the cart's save data
|
||||
std::vector<u8> saveData;
|
||||
// The cart region. Only the CXI's region matters to us. Necessary to get past region locking
|
||||
std::optional<Regions> region = std::nullopt;
|
||||
|
||||
// Returns true on success, false on failure
|
||||
// Partition index/offset/size must have been set before this
|
||||
bool loadFromHeader(Crypto::AESEngine &aesEngine, IOFile& file, const FSInfo &info);
|
||||
// Returns true on success, false on failure
|
||||
// Partition index/offset/size must have been set before this
|
||||
bool loadFromHeader(Crypto::AESEngine &aesEngine, IOFile &file, const FSInfo &info);
|
||||
|
||||
bool hasExtendedHeader() { return exheaderSize != 0; }
|
||||
bool hasExeFS() { return exeFS.size != 0; }
|
||||
bool hasRomFS() { return romFS.size != 0; }
|
||||
bool hasCode() { return codeFile.size() != 0; }
|
||||
bool hasSaveData() { return saveData.size() != 0; }
|
||||
bool hasExtendedHeader() { return exheaderSize != 0; }
|
||||
bool hasExeFS() { return exeFS.size != 0; }
|
||||
bool hasRomFS() { return romFS.size != 0; }
|
||||
bool hasCode() { return codeFile.size() != 0; }
|
||||
bool hasSaveData() { return saveData.size() != 0; }
|
||||
|
||||
std::pair<bool, Crypto::AESKey> getPrimaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY);
|
||||
std::pair<bool, Crypto::AESKey> getSecondaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY);
|
||||
// Parse SMDH for region info and such. Returns false on failure, true on success
|
||||
bool parseSMDH(const std::vector<u8> &smdh);
|
||||
|
||||
std::pair<bool, std::size_t> readFromFile(IOFile& file, const FSInfo &info, u8 *dst, std::size_t offset, std::size_t size);
|
||||
std::pair<bool, Crypto::AESKey> getPrimaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY);
|
||||
std::pair<bool, Crypto::AESKey> getSecondaryKey(Crypto::AESEngine &aesEngine, const Crypto::AESKey &keyY);
|
||||
|
||||
std::pair<bool, std::size_t> readFromFile(IOFile &file, const FSInfo &info, u8 *dst, std::size_t offset, std::size_t size);
|
||||
};
|
|
@ -48,12 +48,15 @@ namespace Log {
|
|||
static Logger<false> gspGPULogger;
|
||||
static Logger<false> gspLCDLogger;
|
||||
static Logger<false> ldrLogger;
|
||||
static Logger<false> mcuLogger;
|
||||
static Logger<false> micLogger;
|
||||
static Logger<false> newsLogger;
|
||||
static Logger<false> nfcLogger;
|
||||
static Logger<false> nimLogger;
|
||||
static Logger<false> ndmLogger;
|
||||
static Logger<false> ptmLogger;
|
||||
static Logger<false> socLogger;
|
||||
static Logger<false> sslLogger;
|
||||
static Logger<false> y2rLogger;
|
||||
static Logger<false> srvLogger;
|
||||
|
||||
|
|
73
include/math_util.hpp
Normal file
73
include/math_util.hpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project / 2023 Panda3DS Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Math {
|
||||
|
||||
template <class T>
|
||||
struct Rectangle {
|
||||
T left{};
|
||||
T top{};
|
||||
T right{};
|
||||
T bottom{};
|
||||
|
||||
constexpr Rectangle() = default;
|
||||
|
||||
constexpr Rectangle(T left, T top, T right, T bottom)
|
||||
: left(left), top(top), right(right), bottom(bottom) {}
|
||||
|
||||
[[nodiscard]] constexpr bool operator==(const Rectangle<T>& rhs) const {
|
||||
return (left == rhs.left) && (top == rhs.top) && (right == rhs.right) &&
|
||||
(bottom == rhs.bottom);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr bool operator!=(const Rectangle<T>& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Rectangle<T> operator*(const T value) const {
|
||||
return Rectangle{left * value, top * value, right * value, bottom * value};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr Rectangle<T> operator/(const T value) const {
|
||||
return Rectangle{left / value, top / value, right / value, bottom / value};
|
||||
}
|
||||
|
||||
[[nodiscard]] T getWidth() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(right - left));
|
||||
}
|
||||
|
||||
[[nodiscard]] T getHeight() const {
|
||||
return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
|
||||
}
|
||||
|
||||
[[nodiscard]] T getArea() const {
|
||||
return getWidth() * getHeight();
|
||||
}
|
||||
|
||||
[[nodiscard]] Rectangle<T> translateX(const T x) const {
|
||||
return Rectangle{left + x, top, right + x, bottom};
|
||||
}
|
||||
|
||||
[[nodiscard]] Rectangle<T> translateY(const T y) const {
|
||||
return Rectangle{left, top + y, right, bottom + y};
|
||||
}
|
||||
|
||||
[[nodiscard]] Rectangle<T> scale(const float s) const {
|
||||
return Rectangle{left, top, static_cast<T>(left + getWidth() * s),
|
||||
static_cast<T>(top + getHeight() * s)};
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
Rectangle(T, T, T, T) -> Rectangle<T>;
|
||||
|
||||
template <typename T>
|
||||
using Rect = Rectangle<T>;
|
||||
|
||||
} // end namespace Math
|
|
@ -5,11 +5,13 @@
|
|||
#include <fstream>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "config.hpp"
|
||||
#include "crypto/aes_engine.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "handles.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "loader/ncsd.hpp"
|
||||
#include "services/shared_font.hpp"
|
||||
#include "services/region_codes.hpp"
|
||||
|
||||
namespace PhysicalAddrs {
|
||||
enum : u32 {
|
||||
|
@ -110,7 +112,7 @@ class Memory {
|
|||
std::vector<KernelMemoryTypes::MemoryInfo> memoryInfo;
|
||||
|
||||
std::array<SharedMemoryBlock, 3> sharedMemBlocks = {
|
||||
SharedMemoryBlock(0, u32(_shared_font_len), KernelHandles::FontSharedMemHandle), // Shared memory for the system font
|
||||
SharedMemoryBlock(0, 0, KernelHandles::FontSharedMemHandle), // Shared memory for the system font (size is 0 because we read the size from the cmrc filesystem
|
||||
SharedMemoryBlock(0, 0x1000, KernelHandles::GSPSharedMemHandle), // GSP shared memory
|
||||
SharedMemoryBlock(0, 0x1000, KernelHandles::HIDSharedMemHandle) // HID shared memory
|
||||
};
|
||||
|
@ -151,13 +153,16 @@ private:
|
|||
|
||||
// Values taken from 3DBrew and Citra
|
||||
static constexpr FirmwareInfo firm{.unk = 0, .revision = 0, .minor = 0x34, .major = 2, .syscoreVer = 2, .sdkVer = 0x0000F297};
|
||||
// Adjusted upon loading a ROM based on the ROM header. Used by CFG::SecureInfoGetArea to get past region locks
|
||||
Regions region = Regions::USA;
|
||||
const EmulatorConfig& config;
|
||||
|
||||
public:
|
||||
u16 kernelVersion = 0;
|
||||
u32 usedUserMemory = u32(0_MB); // How much of the APPLICATION FCRAM range is used (allocated to the appcore)
|
||||
u32 usedSystemMemory = u32(0_MB); // Similar for the SYSTEM range (reserved for the syscore)
|
||||
|
||||
Memory(u64& cpuTicks);
|
||||
Memory(u64& cpuTicks, const EmulatorConfig& config);
|
||||
void reset();
|
||||
void* getReadPointer(u32 address);
|
||||
void* getWritePointer(u32 address);
|
||||
|
@ -261,4 +266,6 @@ public:
|
|||
|
||||
void setVRAM(u8* pointer) { vram = pointer; }
|
||||
bool allocateMainThreadStack(u32 size);
|
||||
};
|
||||
Regions getConsoleRegion();
|
||||
void copySharedFont(u8* ptr);
|
||||
};
|
||||
|
|
|
@ -21,8 +21,11 @@ struct SDL_Window;
|
|||
class Renderer {
|
||||
protected:
|
||||
GPU& gpu;
|
||||
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
|
||||
static constexpr u32 regNum = 0x300; // Number of internal PICA registers
|
||||
static constexpr u32 extRegNum = 0x1000; // Number of external PICA registers
|
||||
|
||||
const std::array<u32, regNum>& regs;
|
||||
const std::array<u32, extRegNum>& externalRegs;
|
||||
|
||||
std::array<u32, 2> fbSize; // The size of the framebuffer (ie both the colour and depth buffer)'
|
||||
|
||||
|
@ -34,7 +37,7 @@ class Renderer {
|
|||
PICA::DepthFmt depthBufferFormat;
|
||||
|
||||
public:
|
||||
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs);
|
||||
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
||||
virtual ~Renderer();
|
||||
|
||||
static constexpr u32 vertexBufferSize = 0x10000;
|
||||
|
@ -46,6 +49,7 @@ class Renderer {
|
|||
virtual void initGraphicsContext(SDL_Window* window) = 0; // Initialize graphics context
|
||||
virtual void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) = 0; // Clear a GPU buffer in VRAM
|
||||
virtual void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) = 0; // Perform display transfer
|
||||
virtual void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) = 0;
|
||||
virtual void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) = 0; // Draw the given vertices
|
||||
|
||||
virtual void screenshot(const std::string& name) = 0;
|
||||
|
@ -65,4 +69,4 @@ class Renderer {
|
|||
|
||||
void setColourBufferLoc(u32 loc) { colourBufferLoc = loc; }
|
||||
void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; }
|
||||
};
|
||||
};
|
||||
|
|
|
@ -34,6 +34,8 @@ struct GLStateManager {
|
|||
bool redMask, greenMask, blueMask, alphaMask;
|
||||
bool depthMask;
|
||||
|
||||
float clearRed, clearBlue, clearGreen, clearAlpha;
|
||||
|
||||
GLuint stencilMask;
|
||||
GLuint boundVAO;
|
||||
GLuint boundVBO;
|
||||
|
@ -44,6 +46,7 @@ struct GLStateManager {
|
|||
|
||||
void reset();
|
||||
void resetBlend();
|
||||
void resetClearing();
|
||||
void resetClipping();
|
||||
void resetColourMask();
|
||||
void resetDepth();
|
||||
|
@ -209,6 +212,17 @@ struct GLStateManager {
|
|||
}
|
||||
}
|
||||
|
||||
void setClearColour(float r, float g, float b, float a) {
|
||||
if (clearRed != r || clearGreen != g || clearBlue != b || clearAlpha != a) {
|
||||
clearRed = r;
|
||||
clearGreen = g;
|
||||
clearBlue = b;
|
||||
clearAlpha = a;
|
||||
|
||||
OpenGL::setClearColor(r, g, b, a);
|
||||
}
|
||||
}
|
||||
|
||||
void setDepthFunc(OpenGL::DepthFunc func) { setDepthFunc(static_cast<GLenum>(func)); }
|
||||
};
|
||||
|
||||
|
|
|
@ -1,697 +0,0 @@
|
|||
/***************************************************************************
|
||||
* Copyright (C) 2022 PCSX-Redux & Panda3DS authors *
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
* This program is distributed in the hope that it will be useful, *
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
||||
* GNU General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU General Public License *
|
||||
* along with this program; if not, write to the *
|
||||
* Free Software Foundation, Inc., *
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
|
||||
***************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdarg>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <glad/gl.h>
|
||||
|
||||
// Check if we have C++20. If yes, we can add C++20 std::span support
|
||||
#ifdef _MSVC_LANG // MSVC does not properly define __cplusplus without a compiler flag...
|
||||
#if _MSVC_LANG >= 202002L
|
||||
#define OPENGL_HAVE_CPP20
|
||||
#endif
|
||||
#elif __cplusplus >= 202002L
|
||||
#define OPENGL_HAVE_CPP20
|
||||
#endif // MSVC_LANG
|
||||
|
||||
#ifdef OPENGL_HAVE_CPP20
|
||||
#include <span>
|
||||
#endif
|
||||
|
||||
#if defined(GPU_DEBUG_INFO)
|
||||
#define OPENGL_DEBUG_INFO
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#include <sal.h>
|
||||
#define OPENGL_PRINTF_FORMAT _Printf_format_string_
|
||||
#define OPENGL_PRINTF_FORMAT_ATTR(format_arg_index, dots_arg_index)
|
||||
#else
|
||||
#define OPENGL_PRINTF_FORMAT
|
||||
#define OPENGL_PRINTF_FORMAT_ATTR(format_arg_index, dots_arg_index) __attribute__((__format__(__printf__, format_arg_index, dots_arg_index)))
|
||||
#endif
|
||||
|
||||
// Uncomment the following define if you want GL objects to automatically free themselves when their lifetime ends
|
||||
// #define OPENGL_DESTRUCTORS
|
||||
|
||||
namespace OpenGL {
|
||||
|
||||
// Workaround for using static_assert inside constexpr if
|
||||
// https://stackoverflow.com/questions/53945490/how-to-assert-that-a-constexpr-if-else-clause-never-happen
|
||||
template <class...>
|
||||
constexpr std::false_type AlwaysFalse{};
|
||||
|
||||
OPENGL_PRINTF_FORMAT_ATTR(3, 4)
|
||||
static void setObjectLabel(GLenum identifier, GLuint name, OPENGL_PRINTF_FORMAT const char* format, ...) {
|
||||
#if defined(OPENGL_DEBUG_INFO)
|
||||
GLchar label[256] = {};
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const GLsizei length = vsnprintf(label, std::size(label), format, args);
|
||||
va_end(args);
|
||||
glObjectLabel(identifier, name, length, label);
|
||||
#endif
|
||||
}
|
||||
|
||||
class DebugScope {
|
||||
#if defined(OPENGL_DEBUG_INFO)
|
||||
inline static GLuint scopeDepth = 0;
|
||||
const GLuint m_scope_depth;
|
||||
#endif
|
||||
|
||||
public:
|
||||
OPENGL_PRINTF_FORMAT_ATTR(2, 3)
|
||||
DebugScope(OPENGL_PRINTF_FORMAT const char* format, ...)
|
||||
#if defined(OPENGL_DEBUG_INFO)
|
||||
: m_scope_depth(scopeDepth++) {
|
||||
GLchar message[256] = {};
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
const GLsizei length = vsnprintf(message, std::size(message), format, args);
|
||||
va_end(args);
|
||||
glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, m_scope_depth, length, message);
|
||||
}
|
||||
#else
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
~DebugScope() {
|
||||
#if defined(OPENGL_DEBUG_INFO)
|
||||
glPopDebugGroup();
|
||||
scopeDepth--;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
struct VertexArray {
|
||||
GLuint m_handle = 0;
|
||||
|
||||
void create() {
|
||||
if (m_handle == 0) {
|
||||
glGenVertexArrays(1, &m_handle);
|
||||
}
|
||||
}
|
||||
VertexArray(bool shouldCreate = false) {
|
||||
if (shouldCreate) {
|
||||
create();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OPENGL_DESTRUCTORS
|
||||
~VertexArray() { free(); }
|
||||
#endif
|
||||
GLuint handle() const { return m_handle; }
|
||||
bool exists() const { return m_handle != 0; }
|
||||
void bind() const { glBindVertexArray(m_handle); }
|
||||
|
||||
template <typename T>
|
||||
void setAttributeFloat(GLuint index, GLint size, GLsizei stride, const void* offset, bool normalized = GL_FALSE) {
|
||||
if constexpr (std::is_same<T, GLfloat>()) {
|
||||
glVertexAttribPointer(index, size, GL_FLOAT, normalized, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLbyte>()) {
|
||||
glVertexAttribPointer(index, size, GL_BYTE, normalized, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLubyte>()) {
|
||||
glVertexAttribPointer(index, size, GL_UNSIGNED_BYTE, normalized, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLshort>()) {
|
||||
glVertexAttribPointer(index, size, GL_SHORT, normalized, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLushort>()) {
|
||||
glVertexAttribPointer(index, size, GL_UNSIGNED_SHORT, normalized, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLint>()) {
|
||||
glVertexAttribPointer(index, size, GL_INT, normalized, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLuint>()) {
|
||||
glVertexAttribPointer(index, size, GL_UNSIGNED_INT, normalized, stride, offset);
|
||||
}
|
||||
else {
|
||||
static_assert(AlwaysFalse<T>, "Unimplemented type for OpenGL::setAttributeFloat");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setAttributeInt(GLuint index, GLint size, GLsizei stride, const void* offset) {
|
||||
if constexpr (std::is_same<T, GLbyte>()) {
|
||||
glVertexAttribIPointer(index, size, GL_BYTE, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLubyte>()) {
|
||||
glVertexAttribIPointer(index, size, GL_UNSIGNED_BYTE, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLshort>()) {
|
||||
glVertexAttribIPointer(index, size, GL_SHORT, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLushort>()) {
|
||||
glVertexAttribIPointer(index, size, GL_UNSIGNED_SHORT, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLint>()) {
|
||||
glVertexAttribIPointer(index, size, GL_INT, stride, offset);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLuint>()) {
|
||||
glVertexAttribIPointer(index, size, GL_UNSIGNED_INT, stride, offset);
|
||||
}
|
||||
else {
|
||||
static_assert(AlwaysFalse<T>, "Unimplemented type for OpenGL::setAttributeInt");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setAttributeFloat(GLuint index, GLint size, GLsizei stride, size_t offset, bool normalized = false) {
|
||||
setAttributeFloat<T>(index, size, stride, reinterpret_cast<const void*>(offset), normalized);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setAttributeInt(GLuint index, GLint size, GLsizei stride, size_t offset) {
|
||||
setAttributeInt<T>(index, size, stride, reinterpret_cast<const void*>(offset));
|
||||
}
|
||||
|
||||
void enableAttribute(GLuint index) { glEnableVertexAttribArray(index); }
|
||||
void disableAttribute(GLuint index) { glDisableVertexAttribArray(index); }
|
||||
|
||||
void free() {
|
||||
glDeleteVertexArrays(1, &m_handle);
|
||||
}
|
||||
};
|
||||
|
||||
enum FramebufferTypes {
|
||||
DrawFramebuffer = GL_DRAW_FRAMEBUFFER,
|
||||
ReadFramebuffer = GL_READ_FRAMEBUFFER,
|
||||
DrawAndReadFramebuffer = GL_FRAMEBUFFER
|
||||
};
|
||||
|
||||
// Texture filters
|
||||
enum Filters {
|
||||
Nearest = GL_NEAREST,
|
||||
Linear = GL_LINEAR,
|
||||
NearestMipmapNearest = GL_NEAREST_MIPMAP_NEAREST,
|
||||
NearestMipmapLinear = GL_NEAREST_MIPMAP_LINEAR,
|
||||
LinearMipmapNearest = GL_LINEAR_MIPMAP_NEAREST,
|
||||
LinearMipmapLinear = GL_LINEAR_MIPMAP_LINEAR
|
||||
};
|
||||
|
||||
// Wrapping mode for texture UVs
|
||||
enum WrappingMode {
|
||||
ClampToEdge = GL_CLAMP_TO_EDGE,
|
||||
ClampToBorder = GL_CLAMP_TO_BORDER,
|
||||
RepeatMirrored = GL_MIRRORED_REPEAT,
|
||||
Repeat = GL_REPEAT,
|
||||
MirrorClampToEdge = GL_MIRROR_CLAMP_TO_EDGE
|
||||
};
|
||||
|
||||
struct Texture {
|
||||
GLuint m_handle = 0;
|
||||
int m_width, m_height;
|
||||
GLenum m_binding;
|
||||
int m_samples = 1; // For MSAA
|
||||
|
||||
void create(int width, int height, GLint internalFormat, GLenum binding = GL_TEXTURE_2D, int samples = 1) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_binding = binding;
|
||||
|
||||
glGenTextures(1, &m_handle);
|
||||
bind();
|
||||
|
||||
if (binding == GL_TEXTURE_2D_MULTISAMPLE) {
|
||||
if (!glTexStorage2DMultisample) { // Assert that glTexStorage2DMultisample has been loaded
|
||||
throw std::runtime_error("MSAA is not supported on this platform");
|
||||
}
|
||||
|
||||
int maxSampleCount;
|
||||
glGetIntegerv(GL_MAX_INTEGER_SAMPLES, &maxSampleCount);
|
||||
if (samples > maxSampleCount) {
|
||||
samples = maxSampleCount;
|
||||
}
|
||||
|
||||
glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, internalFormat, width, height, GL_TRUE);
|
||||
}
|
||||
else {
|
||||
glTexStorage2D(binding, 1, internalFormat, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
void createMSAA(int width, int height, GLint internalFormat, int samples) {
|
||||
create(width, height, internalFormat, GL_TEXTURE_2D_MULTISAMPLE, samples);
|
||||
}
|
||||
|
||||
// Creates a depth, stencil or depth-stencil texture
|
||||
void createDSTexture(int width, int height, GLenum internalFormat, GLenum format, const void* data = nullptr,
|
||||
GLenum type = GL_FLOAT, GLenum binding = GL_TEXTURE_2D) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_binding = binding;
|
||||
|
||||
glGenTextures(1, &m_handle);
|
||||
bind();
|
||||
glTexImage2D(binding, 0, internalFormat, width, height, 0, format, type, data);
|
||||
}
|
||||
|
||||
void setWrapS(WrappingMode mode) {
|
||||
glTexParameteri(m_binding, GL_TEXTURE_WRAP_S, static_cast<GLint>(mode));
|
||||
}
|
||||
|
||||
void setWrapT(WrappingMode mode) {
|
||||
glTexParameteri(m_binding, GL_TEXTURE_WRAP_T, static_cast<GLint>(mode));
|
||||
}
|
||||
|
||||
void setWrapR(WrappingMode mode) {
|
||||
glTexParameteri(m_binding, GL_TEXTURE_WRAP_R, static_cast<GLint>(mode));
|
||||
}
|
||||
|
||||
void setMinFilter(Filters filter) {
|
||||
glTexParameteri(m_binding, GL_TEXTURE_MIN_FILTER, static_cast<GLint>(filter));
|
||||
}
|
||||
|
||||
void setMagFilter(Filters filter) {
|
||||
glTexParameteri(m_binding, GL_TEXTURE_MAG_FILTER, static_cast<GLint>(filter));
|
||||
}
|
||||
|
||||
#ifdef OPENGL_DESTRUCTORS
|
||||
~Texture() { free(); }
|
||||
#endif
|
||||
GLuint handle() const { return m_handle; }
|
||||
bool exists() const { return m_handle != 0; }
|
||||
void bind() const { glBindTexture(m_binding, m_handle); }
|
||||
int width() const { return m_width; }
|
||||
int height() const { return m_height; }
|
||||
|
||||
void free() { glDeleteTextures(1, &m_handle); }
|
||||
};
|
||||
|
||||
struct Framebuffer {
|
||||
GLuint m_handle = 0;
|
||||
GLenum m_textureType; // GL_TEXTURE_2D or GL_TEXTURE_2D_MULTISAMPLE
|
||||
|
||||
void create() {
|
||||
if (m_handle == 0) {
|
||||
glGenFramebuffers(1, &m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
Framebuffer(bool shouldCreate = false) {
|
||||
if (shouldCreate) {
|
||||
create();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OPENGL_DESTRUCTORS
|
||||
~Framebuffer() { free(); }
|
||||
#endif
|
||||
GLuint handle() const { return m_handle; }
|
||||
bool exists() const { return m_handle != 0; }
|
||||
void bind(GLenum target) const { glBindFramebuffer(target, m_handle); }
|
||||
void bind(FramebufferTypes target) const { bind(static_cast<GLenum>(target)); }
|
||||
void free() { glDeleteFramebuffers(1, &m_handle); }
|
||||
|
||||
void createWithTexture(Texture& tex, GLenum mode = GL_FRAMEBUFFER, GLenum attachment = GL_COLOR_ATTACHMENT0, GLenum textureType = GL_TEXTURE_2D) {
|
||||
m_textureType = textureType;
|
||||
create();
|
||||
bind(mode);
|
||||
glFramebufferTexture2D(mode, attachment, textureType, tex.handle(), 0);
|
||||
}
|
||||
|
||||
void createWithReadTexture(Texture& tex, GLenum attachment = GL_COLOR_ATTACHMENT0, GLenum textureType = GL_TEXTURE_2D) {
|
||||
createWithTexture(tex, GL_READ_FRAMEBUFFER, attachment, textureType);
|
||||
}
|
||||
void createWithDrawTexture(Texture& tex, GLenum attachment = GL_COLOR_ATTACHMENT0, GLenum textureType = GL_TEXTURE_2D) {
|
||||
createWithTexture(tex, GL_DRAW_FRAMEBUFFER, attachment, textureType);
|
||||
}
|
||||
|
||||
void createWithTextureMSAA(Texture& tex, GLenum mode = GL_FRAMEBUFFER) {
|
||||
m_textureType = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
create();
|
||||
bind(mode);
|
||||
glFramebufferTexture2D(mode, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex.handle(), 0);
|
||||
}
|
||||
};
|
||||
|
||||
enum ShaderType {
|
||||
Fragment = GL_FRAGMENT_SHADER,
|
||||
Vertex = GL_VERTEX_SHADER,
|
||||
Geometry = GL_GEOMETRY_SHADER,
|
||||
Compute = GL_COMPUTE_SHADER,
|
||||
TessControl = GL_TESS_CONTROL_SHADER,
|
||||
TessEvaluation = GL_TESS_EVALUATION_SHADER
|
||||
};
|
||||
|
||||
struct Shader {
|
||||
GLuint m_handle = 0;
|
||||
|
||||
Shader() {}
|
||||
Shader(const std::string_view source, ShaderType type) { create(source, static_cast<GLenum>(type)); }
|
||||
|
||||
// Returns whether compilation failed or not
|
||||
bool create(const std::string_view source, GLenum type) {
|
||||
m_handle = glCreateShader(type);
|
||||
const GLchar* const sources[1] = { source.data() };
|
||||
|
||||
glShaderSource(m_handle, 1, sources, nullptr);
|
||||
glCompileShader(m_handle);
|
||||
|
||||
GLint success;
|
||||
glGetShaderiv(m_handle, GL_COMPILE_STATUS, &success);
|
||||
if (success == GL_FALSE) {
|
||||
char buf[4096];
|
||||
glGetShaderInfoLog(m_handle, 4096, nullptr, buf);
|
||||
fprintf(stderr, "Failed to compile shader\nError: %s\n", buf);
|
||||
glDeleteShader(m_handle);
|
||||
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
GLuint handle() const { return m_handle; }
|
||||
bool exists() const { return m_handle != 0; }
|
||||
};
|
||||
|
||||
struct Program {
|
||||
GLuint m_handle = 0;
|
||||
|
||||
bool create(std::initializer_list<std::reference_wrapper<Shader>> shaders) {
|
||||
m_handle = glCreateProgram();
|
||||
for (const auto& shader : shaders) {
|
||||
glAttachShader(m_handle, shader.get().handle());
|
||||
}
|
||||
|
||||
glLinkProgram(m_handle);
|
||||
GLint success;
|
||||
glGetProgramiv(m_handle, GL_LINK_STATUS, &success);
|
||||
|
||||
if (!success) {
|
||||
char buf[4096];
|
||||
glGetProgramInfoLog(m_handle, 4096, nullptr, buf);
|
||||
fprintf(stderr, "Failed to link program\nError: %s\n", buf);
|
||||
glDeleteProgram(m_handle);
|
||||
|
||||
m_handle = 0;
|
||||
}
|
||||
|
||||
return m_handle != 0;
|
||||
}
|
||||
|
||||
GLuint handle() const { return m_handle; }
|
||||
bool exists() const { return m_handle != 0; }
|
||||
void use() const { glUseProgram(m_handle); }
|
||||
};
|
||||
|
||||
static void dispatchCompute(GLuint groupsX = 1, GLuint groupsY = 1, GLuint groupsZ = 1) {
|
||||
glDispatchCompute(groupsX, groupsY, groupsZ);
|
||||
}
|
||||
|
||||
struct VertexBuffer {
|
||||
GLuint m_handle = 0;
|
||||
|
||||
void create() {
|
||||
if (m_handle == 0) {
|
||||
glGenBuffers(1, &m_handle);
|
||||
}
|
||||
}
|
||||
|
||||
void createFixedSize(GLsizei size, GLenum usage = GL_DYNAMIC_DRAW) {
|
||||
create();
|
||||
bind();
|
||||
glBufferData(GL_ARRAY_BUFFER, size, nullptr, usage);
|
||||
}
|
||||
|
||||
VertexBuffer(bool shouldCreate = false) {
|
||||
if (shouldCreate) {
|
||||
create();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef OPENGL_DESTRUCTORS
|
||||
~VertexBuffer() { free(); }
|
||||
#endif
|
||||
GLuint handle() const { return m_handle; }
|
||||
bool exists() const { return m_handle != 0; }
|
||||
void bind() const { glBindBuffer(GL_ARRAY_BUFFER, m_handle); }
|
||||
void free() { glDeleteBuffers(1, &m_handle); }
|
||||
|
||||
// Reallocates the buffer on every call. Prefer the sub version if possible.
|
||||
template <typename VertType>
|
||||
void bufferVerts(VertType* vertices, int vertCount, GLenum usage = GL_DYNAMIC_DRAW) {
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertCount, vertices, usage);
|
||||
}
|
||||
|
||||
// Only use if you used createFixedSize
|
||||
template <typename VertType>
|
||||
void bufferVertsSub(VertType* vertices, int vertCount, GLintptr offset = 0) {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertCount, vertices);
|
||||
}
|
||||
|
||||
// If C++20 is available, add overloads that take std::span instead of raw pointers
|
||||
#ifdef OPENGL_HAVE_CPP20
|
||||
template <typename VertType>
|
||||
void bufferVerts(std::span<const VertType> vertices, GLenum usage = GL_DYNAMIC_DRAW) {
|
||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VertType) * vertices.size(), vertices.data(), usage);
|
||||
}
|
||||
|
||||
template <typename VertType>
|
||||
void bufferVertsSub(std::span<const VertType> vertices, GLintptr offset = 0) {
|
||||
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(VertType) * vertices.size(), vertices.data());
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
enum DepthFunc {
|
||||
Never = GL_NEVER, // Depth test never passes
|
||||
Always = GL_ALWAYS, // Depth test always passes
|
||||
Equal = GL_EQUAL, // Depth test passes if frag z == depth buffer z
|
||||
NotEqual = GL_NOTEQUAL, // Depth test passes if frag z != depth buffer z
|
||||
Less = GL_LESS, // Depth test passes if frag z < depth buffer z
|
||||
Lequal = GL_LEQUAL, // Depth test passes if frag z <= depth buffer z
|
||||
Greater = GL_GREATER, // Depth test passes if frag z > depth buffer z
|
||||
Gequal = GL_GEQUAL, // Depth test passes if frag z >= depth buffer z
|
||||
};
|
||||
|
||||
static void setClearColor(float val) { glClearColor(val, val, val, val); }
|
||||
static void setClearColor(float r, float g, float b, float a) { glClearColor(r, g, b, a); }
|
||||
static void setClearDepth(float depth) { glClearDepthf(depth); }
|
||||
static void setClearStencil(GLint stencil) { glClearStencil(stencil); }
|
||||
static void clearColor() { glClear(GL_COLOR_BUFFER_BIT); }
|
||||
static void clearDepth() { glClear(GL_DEPTH_BUFFER_BIT); }
|
||||
static void clearStencil() { glClear(GL_STENCIL_BUFFER_BIT); }
|
||||
static void clearColorAndDepth() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); }
|
||||
static void clearColorAndStencil() { glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); }
|
||||
static void clearDepthAndStencil() { glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); }
|
||||
static void clearAll() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); }
|
||||
|
||||
static void setViewport(GLsizei width, GLsizei height) { glViewport(0, 0, width, height); }
|
||||
static void setViewport(GLsizei x, GLsizei y, GLsizei width, GLsizei height) { glViewport(x, y, width, height); }
|
||||
static void setScissor(GLsizei width, GLsizei height) { glScissor(0, 0, width, height); }
|
||||
static void setScissor(GLsizei x, GLsizei y, GLsizei width, GLsizei height) { glScissor(x, y, width, height); }
|
||||
static void setStencilMask(GLuint mask) { glStencilMask(mask); }
|
||||
|
||||
static void bindScreenFramebuffer() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
|
||||
static void enableScissor() { glEnable(GL_SCISSOR_TEST); }
|
||||
static void disableScissor() { glDisable(GL_SCISSOR_TEST); }
|
||||
static void enableBlend() { glEnable(GL_BLEND); }
|
||||
static void disableBlend() { glDisable(GL_BLEND); }
|
||||
static void enableLogicOp() { glEnable(GL_COLOR_LOGIC_OP); }
|
||||
static void disableLogicOp() { glDisable(GL_COLOR_LOGIC_OP); }
|
||||
static void enableDepth() { glEnable(GL_DEPTH_TEST); }
|
||||
static void disableDepth() { glDisable(GL_DEPTH_TEST); }
|
||||
static void enableStencil() { glEnable(GL_STENCIL_TEST); }
|
||||
static void disableStencil() { glDisable(GL_STENCIL_TEST); }
|
||||
|
||||
static void enableClipPlane(GLuint index) { glEnable(GL_CLIP_DISTANCE0 + index); }
|
||||
static void disableClipPlane(GLuint index) { glDisable(GL_CLIP_DISTANCE0 + index); }
|
||||
|
||||
static void setDepthFunc(DepthFunc func) { glDepthFunc(static_cast<GLenum>(func)); }
|
||||
static void setColourMask(GLboolean r, GLboolean g, GLboolean b, GLboolean a) { glColorMask(r, g, b, a); }
|
||||
static void setDepthMask(GLboolean mask) { glDepthMask(mask); }
|
||||
|
||||
// TODO: Add a proper enum for this
|
||||
static void setLogicOp(GLenum op) { glLogicOp(op); }
|
||||
|
||||
enum Primitives {
|
||||
Triangle = GL_TRIANGLES,
|
||||
Triangles = Triangle,
|
||||
Tri = Triangle,
|
||||
Tris = Triangle,
|
||||
TriangleStrip = GL_TRIANGLE_STRIP,
|
||||
TriangleFan = GL_TRIANGLE_FAN,
|
||||
Line = GL_LINES,
|
||||
Lines = Line,
|
||||
LineStrip = GL_LINE_STRIP,
|
||||
Point = GL_POINTS,
|
||||
Points = Point
|
||||
};
|
||||
|
||||
static void draw(Primitives prim, GLsizei vertexCount) { glDrawArrays(static_cast<GLenum>(prim), 0, vertexCount); }
|
||||
static void draw(Primitives prim, GLint first, GLsizei vertexCount) {
|
||||
glDrawArrays(static_cast<GLenum>(prim), first, vertexCount);
|
||||
}
|
||||
|
||||
enum FillMode { DrawPoints = GL_POINT, DrawWire = GL_LINE, FillPoly = GL_FILL };
|
||||
|
||||
static void setFillMode(GLenum mode) { glPolygonMode(GL_FRONT_AND_BACK, mode); }
|
||||
static void setFillMode(FillMode mode) { glPolygonMode(GL_FRONT_AND_BACK, static_cast<GLenum>(mode)); }
|
||||
static void drawWireframe() { setFillMode(DrawWire); }
|
||||
|
||||
template <typename T>
|
||||
T get(GLenum query) {
|
||||
T ret{};
|
||||
if constexpr (std::is_same<T, GLint>()) {
|
||||
glGetIntegerv(query, &ret);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLfloat>()) {
|
||||
glGetFloatv(query, &ret);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLdouble>()) {
|
||||
glGetDoublev(query, &ret);
|
||||
}
|
||||
else if constexpr (std::is_same<T, GLboolean>()) {
|
||||
glGetBooleanv(query, &ret);
|
||||
}
|
||||
else {
|
||||
static_assert(AlwaysFalse<T>, "Invalid type for OpenGL::get");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool isEnabled(GLenum query) { return glIsEnabled(query) != GL_FALSE; }
|
||||
|
||||
static GLint getDrawFramebuffer() { return get<GLint>(GL_DRAW_FRAMEBUFFER_BINDING); }
|
||||
static GLint maxSamples() { return get<GLint>(GL_MAX_INTEGER_SAMPLES); }
|
||||
static GLint getTex2D() { return get<GLint>(GL_TEXTURE_BINDING_2D); }
|
||||
static GLint getProgram() { return get<GLint>(GL_CURRENT_PROGRAM); }
|
||||
static bool scissorEnabled() { return isEnabled(GL_SCISSOR_TEST); }
|
||||
|
||||
[[nodiscard]] static GLint uniformLocation(GLuint program, const char* name) {
|
||||
return glGetUniformLocation(program, name);
|
||||
}
|
||||
[[nodiscard]] static GLint uniformLocation(Program& program, const char* name) {
|
||||
return glGetUniformLocation(program.handle(), name);
|
||||
}
|
||||
|
||||
enum BlendEquation {
|
||||
Add = GL_FUNC_ADD,
|
||||
Sub = GL_FUNC_SUBTRACT,
|
||||
ReverseSub = GL_FUNC_REVERSE_SUBTRACT,
|
||||
Min = GL_MIN,
|
||||
Max = GL_MAX
|
||||
};
|
||||
|
||||
static void setBlendColor(float r, float g, float b, float a = 1.0) { glBlendColor(r, g, b, a); }
|
||||
static void setBlendEquation(BlendEquation eq) { glBlendEquation(eq); }
|
||||
static void setBlendEquation(BlendEquation eq1, BlendEquation eq2) { glBlendEquationSeparate(eq1, eq2); }
|
||||
|
||||
static void setBlendFactor(GLenum fac1, GLenum fac2) { glBlendFunc(fac1, fac2); }
|
||||
static void setBlendFactor(GLenum fac1, GLenum fac2, GLenum fac3, GLenum fac4) {
|
||||
glBlendFuncSeparate(fac1, fac2, fac3, fac4);
|
||||
}
|
||||
|
||||
// Abstraction for GLSL vectors
|
||||
template <typename T, int size>
|
||||
class Vector {
|
||||
// A GLSL vector can only have 2, 3 or 4 elements
|
||||
static_assert(size == 2 || size == 3 || size == 4);
|
||||
T m_storage[size];
|
||||
|
||||
public:
|
||||
T& r() { return m_storage[0]; }
|
||||
T& g() { return m_storage[1]; }
|
||||
T& b() {
|
||||
static_assert(size >= 3, "Out of bounds OpenGL::Vector access");
|
||||
return m_storage[2];
|
||||
}
|
||||
T& a() {
|
||||
static_assert(size >= 4, "Out of bounds OpenGL::Vector access");
|
||||
return m_storage[3];
|
||||
}
|
||||
|
||||
T& x() { return r(); }
|
||||
T& y() { return g(); }
|
||||
T& z() { return b(); }
|
||||
T& w() { return a(); }
|
||||
T& operator[](size_t index) { return m_storage[index]; }
|
||||
const T& operator[](size_t index) const { return m_storage[index]; }
|
||||
|
||||
T& u() { return r(); }
|
||||
T& v() { return g(); }
|
||||
|
||||
T& s() { return r(); }
|
||||
T& t() { return g(); }
|
||||
T& p() { return b(); }
|
||||
T& q() { return a(); }
|
||||
|
||||
Vector(std::array<T, size> list) { std::copy(list.begin(), list.end(), &m_storage[0]); }
|
||||
|
||||
Vector() {}
|
||||
};
|
||||
|
||||
using vec2 = Vector<GLfloat, 2>;
|
||||
using vec3 = Vector<GLfloat, 3>;
|
||||
using vec4 = Vector<GLfloat, 4>;
|
||||
|
||||
using dvec2 = Vector<GLdouble, 2>;
|
||||
using dvec3 = Vector<GLdouble, 3>;
|
||||
using dvec4 = Vector<GLdouble, 4>;
|
||||
|
||||
using ivec2 = Vector<GLint, 2>;
|
||||
using ivec3 = Vector<GLint, 3>;
|
||||
using ivec4 = Vector<GLint, 4>;
|
||||
|
||||
using uvec2 = Vector<GLuint, 2>;
|
||||
using uvec3 = Vector<GLuint, 3>;
|
||||
using uvec4 = Vector<GLuint, 4>;
|
||||
|
||||
// A 2D rectangle, meant to be used for stuff like scissor rects or viewport rects
|
||||
// We're never supporting 3D rectangles, because rectangles were never meant to be 3D in the first place
|
||||
// x, y: Coords of the top left vertex
|
||||
// width, height: Dimensions of the rectangle. Initialized to 0 if not specified.
|
||||
template <typename T>
|
||||
struct Rectangle {
|
||||
T x, y, width, height;
|
||||
|
||||
std::pair<T, T> topLeft() const { return std::make_pair(x, y); }
|
||||
std::pair<T, T> topRight() const { return std::make_pair(x + width, y); }
|
||||
std::pair<T, T> bottomLeft() const { return std::make_pair(x, y + height); }
|
||||
std::pair<T, T> bottomRight() const { return std::make_pair(x + width, y + height); }
|
||||
|
||||
Rectangle() : x(0), y(0), width(0), height(0) {}
|
||||
Rectangle(T x, T y, T width, T height) : x(x), y(y), width(width), height(height) {}
|
||||
|
||||
bool isEmpty() const { return width == 0 && height == 0; }
|
||||
bool isLine() const { return (width == 0 && height != 0) || (width != 0 && height == 0); }
|
||||
|
||||
void setEmpty() { x = y = width = height = 0; }
|
||||
};
|
||||
|
||||
using Rect = Rectangle<GLuint>;
|
||||
|
||||
} // end namespace OpenGL
|
|
@ -37,8 +37,8 @@ class RendererGL final : public Renderer {
|
|||
float oldDepthOffset = 0.0;
|
||||
bool oldDepthmapEnable = false;
|
||||
|
||||
SurfaceCache<DepthBuffer, 10, true> depthBufferCache;
|
||||
SurfaceCache<ColourBuffer, 10, true> colourBufferCache;
|
||||
SurfaceCache<DepthBuffer, 16, true> depthBufferCache;
|
||||
SurfaceCache<ColourBuffer, 16, true> colourBufferCache;
|
||||
SurfaceCache<Texture, 256, true> textureCache;
|
||||
|
||||
// Dummy VAO/VBO for blitting the final output
|
||||
|
@ -48,6 +48,7 @@ class RendererGL final : public Renderer {
|
|||
OpenGL::Texture screenTexture;
|
||||
GLuint lightLUTTextureArray;
|
||||
OpenGL::Framebuffer screenFramebuffer;
|
||||
OpenGL::Texture blankTexture;
|
||||
|
||||
OpenGL::Framebuffer getColourFBO();
|
||||
OpenGL::Texture getTexture(Texture& tex);
|
||||
|
@ -60,7 +61,8 @@ class RendererGL final : public Renderer {
|
|||
void updateLightingLUT();
|
||||
|
||||
public:
|
||||
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs) : Renderer(gpu, internalRegs) {}
|
||||
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
|
||||
: Renderer(gpu, internalRegs, externalRegs) {}
|
||||
~RendererGL() override;
|
||||
|
||||
void reset() override;
|
||||
|
@ -68,8 +70,11 @@ class RendererGL final : public Renderer {
|
|||
void initGraphicsContext(SDL_Window* window) override; // Initialize graphics context
|
||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override; // Clear a GPU buffer in VRAM
|
||||
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override; // Perform display transfer
|
||||
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
|
||||
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
|
||||
|
||||
std::optional<ColourBuffer> getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
|
||||
|
||||
// Take a screenshot of the screen and store it in a file
|
||||
void screenshot(const std::string& name) override;
|
||||
};
|
||||
|
|
|
@ -76,6 +76,16 @@ public:
|
|||
|
||||
size++;
|
||||
|
||||
// Find an existing surface we completely invalidate and overwrite it with the new surface
|
||||
for (auto& e : buffer) {
|
||||
if (e.valid && e.range.lower() >= surface.range.lower() && e.range.upper() <= surface.range.upper()) {
|
||||
e.free();
|
||||
e = surface;
|
||||
e.allocate();
|
||||
return e;
|
||||
}
|
||||
}
|
||||
|
||||
// Find an invalid entry in the cache and overwrite it with the new surface
|
||||
for (auto& e : buffer) {
|
||||
if (!e.valid) {
|
||||
|
|
|
@ -2,62 +2,70 @@
|
|||
#include "PICA/regs.hpp"
|
||||
#include "boost/icl/interval.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "math_util.hpp"
|
||||
#include "opengl.hpp"
|
||||
|
||||
template <typename T>
|
||||
using Interval = boost::icl::right_open_interval<T>;
|
||||
|
||||
struct ColourBuffer {
|
||||
u32 location;
|
||||
PICA::ColorFmt format;
|
||||
OpenGL::uvec2 size;
|
||||
bool valid;
|
||||
u32 location;
|
||||
PICA::ColorFmt format;
|
||||
OpenGL::uvec2 size;
|
||||
bool valid;
|
||||
|
||||
// Range of VRAM taken up by buffer
|
||||
Interval<u32> range;
|
||||
// OpenGL resources allocated to buffer
|
||||
OpenGL::Texture texture;
|
||||
OpenGL::Framebuffer fbo;
|
||||
// Range of VRAM taken up by buffer
|
||||
Interval<u32> range;
|
||||
// OpenGL resources allocated to buffer
|
||||
OpenGL::Texture texture;
|
||||
OpenGL::Framebuffer fbo;
|
||||
|
||||
ColourBuffer() : valid(false) {}
|
||||
ColourBuffer() : valid(false) {}
|
||||
|
||||
ColourBuffer(u32 loc, PICA::ColorFmt format, u32 x, u32 y, bool valid = true)
|
||||
: location(loc), format(format), size({x, y}), valid(valid) {
|
||||
ColourBuffer(u32 loc, PICA::ColorFmt format, u32 x, u32 y, bool valid = true) : location(loc), format(format), size({x, y}), valid(valid) {
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
// Check if start and end are valid here
|
||||
range = Interval<u32>(loc, (u32)endLoc);
|
||||
}
|
||||
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
// Check if start and end are valid here
|
||||
range = Interval<u32>(loc, (u32)endLoc);
|
||||
}
|
||||
void allocate() {
|
||||
// Create texture for the FBO, setting up filters and the like
|
||||
// Reading back the current texture is slow, but allocate calls should be few and far between.
|
||||
// If this becomes a bottleneck, we can fix it semi-easily
|
||||
auto prevTexture = OpenGL::getTex2D();
|
||||
texture.create(size.x(), size.y(), GL_RGBA8);
|
||||
texture.bind();
|
||||
texture.setMinFilter(OpenGL::Linear);
|
||||
texture.setMagFilter(OpenGL::Linear);
|
||||
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||
|
||||
void allocate() {
|
||||
// Create texture for the FBO, setting up filters and the like
|
||||
// Reading back the current texture is slow, but allocate calls should be few and far between.
|
||||
// If this becomes a bottleneck, we can fix it semi-easily
|
||||
auto prevTexture = OpenGL::getTex2D();
|
||||
texture.create(size.x(), size.y(), GL_RGBA8);
|
||||
texture.bind();
|
||||
texture.setMinFilter(OpenGL::Linear);
|
||||
texture.setMagFilter(OpenGL::Linear);
|
||||
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||
#ifdef GPU_DEBUG_INFO
|
||||
const auto name = Helpers::format("Surface %dx%d %s from 0x%08X", size.x(), size.y(), PICA::textureFormatToString(format), location);
|
||||
OpenGL::setObjectLabel(GL_TEXTURE, texture.handle(), name.c_str());
|
||||
#endif
|
||||
|
||||
//Helpers::panic("Creating FBO: %d, %d\n", size.x(), size.y());
|
||||
fbo.createWithDrawTexture(texture);
|
||||
fbo.bind(OpenGL::DrawAndReadFramebuffer);
|
||||
|
||||
fbo.createWithDrawTexture(texture);
|
||||
fbo.bind(OpenGL::DrawAndReadFramebuffer);
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
Helpers::panic("Incomplete framebuffer");
|
||||
}
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
Helpers::panic("Incomplete framebuffer");
|
||||
// TODO: This should not clear the framebuffer contents. It should load them from VRAM.
|
||||
GLint oldViewport[4];
|
||||
GLfloat oldClearColour[4];
|
||||
|
||||
// TODO: This should not clear the framebuffer contents. It should load them from VRAM.
|
||||
GLint oldViewport[4];
|
||||
glGetIntegerv(GL_VIEWPORT, oldViewport);
|
||||
OpenGL::setViewport(size.x(), size.y());
|
||||
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
OpenGL::clearColor();
|
||||
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
|
||||
}
|
||||
glGetIntegerv(GL_VIEWPORT, oldViewport);
|
||||
glGetFloatv(GL_COLOR_CLEAR_VALUE, oldClearColour);
|
||||
|
||||
void free() {
|
||||
OpenGL::setViewport(size.x(), size.y());
|
||||
OpenGL::setClearColor(0.0, 0.0, 0.0, 1.0);
|
||||
OpenGL::clearColor();
|
||||
OpenGL::setViewport(oldViewport[0], oldViewport[1], oldViewport[2], oldViewport[3]);
|
||||
OpenGL::setClearColor(oldClearColour[0], oldClearColour[1], oldClearColour[2], oldClearColour[3]);
|
||||
}
|
||||
|
||||
void free() {
|
||||
valid = false;
|
||||
|
||||
if (texture.exists() || fbo.exists()) {
|
||||
|
@ -66,88 +74,102 @@ struct ColourBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
bool matches(ColourBuffer& other) {
|
||||
return location == other.location && format == other.format &&
|
||||
size.x() == other.size.x() && size.y() == other.size.y();
|
||||
}
|
||||
Math::Rect<u32> getSubRect(u32 inputAddress, u32 width, u32 height) {
|
||||
// PICA textures have top-left origin while OpenGL has bottom-left origin.
|
||||
// Flip the rectangle on the x axis to account for this.
|
||||
const u32 startOffset = (inputAddress - location) / sizePerPixel(format);
|
||||
const u32 x0 = (startOffset % (size.x() * 8)) / 8;
|
||||
const u32 y0 = (startOffset / (size.x() * 8)) * 8;
|
||||
return Math::Rect<u32>{x0, size.y() - y0, x0 + width, size.y() - height - y0};
|
||||
}
|
||||
|
||||
size_t sizeInBytes() {
|
||||
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||
}
|
||||
bool matches(ColourBuffer& other) {
|
||||
return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
|
||||
}
|
||||
|
||||
size_t sizeInBytes() {
|
||||
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||
}
|
||||
};
|
||||
|
||||
struct DepthBuffer {
|
||||
u32 location;
|
||||
PICA::DepthFmt format;
|
||||
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
|
||||
bool valid;
|
||||
u32 location;
|
||||
PICA::DepthFmt format;
|
||||
OpenGL::uvec2 size; // Implicitly set to the size of the framebuffer
|
||||
bool valid;
|
||||
|
||||
// Range of VRAM taken up by buffer
|
||||
Interval<u32> range;
|
||||
// OpenGL texture used for storing depth/stencil
|
||||
OpenGL::Texture texture;
|
||||
OpenGL::Framebuffer fbo;
|
||||
// Range of VRAM taken up by buffer
|
||||
Interval<u32> range;
|
||||
// OpenGL texture used for storing depth/stencil
|
||||
OpenGL::Texture texture;
|
||||
OpenGL::Framebuffer fbo;
|
||||
|
||||
DepthBuffer() : valid(false) {}
|
||||
DepthBuffer() : valid(false) {}
|
||||
|
||||
DepthBuffer(u32 loc, PICA::DepthFmt format, u32 x, u32 y, bool valid = true) :
|
||||
location(loc), format(format), size({x, y}), valid(valid) {
|
||||
DepthBuffer(u32 loc, PICA::DepthFmt format, u32 x, u32 y, bool valid = true) : location(loc), format(format), size({x, y}), valid(valid) {
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
// Check if start and end are valid here
|
||||
range = Interval<u32>(loc, (u32)endLoc);
|
||||
}
|
||||
|
||||
u64 endLoc = (u64)loc + sizeInBytes();
|
||||
// Check if start and end are valid here
|
||||
range = Interval<u32>(loc, (u32)endLoc);
|
||||
}
|
||||
void allocate() {
|
||||
// Create texture for the FBO, setting up filters and the like
|
||||
// Reading back the current texture is slow, but allocate calls should be few and far between.
|
||||
// If this becomes a bottleneck, we can fix it semi-easily
|
||||
auto prevTexture = OpenGL::getTex2D();
|
||||
|
||||
void allocate() {
|
||||
// Create texture for the FBO, setting up filters and the like
|
||||
// Reading back the current texture is slow, but allocate calls should be few and far between.
|
||||
// If this becomes a bottleneck, we can fix it semi-easily
|
||||
auto prevTexture = OpenGL::getTex2D();
|
||||
// Internal formats for the texture based on format
|
||||
static constexpr std::array<GLenum, 4> internalFormats = {
|
||||
GL_DEPTH_COMPONENT16,
|
||||
GL_DEPTH_COMPONENT24,
|
||||
GL_DEPTH_COMPONENT24,
|
||||
GL_DEPTH24_STENCIL8,
|
||||
};
|
||||
|
||||
// Internal formats for the texture based on format
|
||||
static constexpr std::array<GLenum, 4> internalFormats = {
|
||||
GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT24, GL_DEPTH24_STENCIL8
|
||||
};
|
||||
// Format of the texture
|
||||
static constexpr std::array<GLenum, 4> formats = {
|
||||
GL_DEPTH_COMPONENT,
|
||||
GL_DEPTH_COMPONENT,
|
||||
GL_DEPTH_COMPONENT,
|
||||
GL_DEPTH_STENCIL,
|
||||
};
|
||||
|
||||
static constexpr std::array<GLenum, 4> types = {
|
||||
GL_UNSIGNED_SHORT,
|
||||
GL_UNSIGNED_INT,
|
||||
GL_UNSIGNED_INT,
|
||||
GL_UNSIGNED_INT_24_8,
|
||||
};
|
||||
|
||||
// Format of the texture
|
||||
static constexpr std::array<GLenum, 4> formats = {
|
||||
GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL
|
||||
};
|
||||
auto internalFormat = internalFormats[(int)format];
|
||||
auto fmt = formats[(int)format];
|
||||
auto type = types[(int)format];
|
||||
|
||||
static constexpr std::array<GLenum, 4> types = {
|
||||
GL_UNSIGNED_SHORT, GL_UNSIGNED_INT, GL_UNSIGNED_INT, GL_UNSIGNED_INT_24_8
|
||||
};
|
||||
texture.createDSTexture(size.x(), size.y(), internalFormat, fmt, nullptr, type, GL_TEXTURE_2D);
|
||||
texture.bind();
|
||||
texture.setMinFilter(OpenGL::Nearest);
|
||||
texture.setMagFilter(OpenGL::Nearest);
|
||||
|
||||
auto internalFormat = internalFormats[(int)format];
|
||||
auto fmt = formats[(int)format];
|
||||
auto type = types[(int)format];
|
||||
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||
fbo.createWithDrawTexture(texture, fmt == GL_DEPTH_STENCIL ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT);
|
||||
|
||||
texture.createDSTexture(size.x(), size.y(), internalFormat, fmt, nullptr, type, GL_TEXTURE_2D);
|
||||
texture.bind();
|
||||
texture.setMinFilter(OpenGL::Nearest);
|
||||
texture.setMagFilter(OpenGL::Nearest);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||
|
||||
fbo.createWithDrawTexture(texture, fmt == GL_DEPTH_STENCIL ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
Helpers::panic("Incomplete framebuffer");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void free() {
|
||||
void free() {
|
||||
valid = false;
|
||||
if (texture.exists()) {
|
||||
texture.free();
|
||||
}
|
||||
}
|
||||
|
||||
bool matches(DepthBuffer& other) {
|
||||
return location == other.location && format == other.format &&
|
||||
size.x() == other.size.x() && size.y() == other.size.y();
|
||||
}
|
||||
bool matches(DepthBuffer& other) {
|
||||
return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
|
||||
}
|
||||
|
||||
size_t sizeInBytes() {
|
||||
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||
}
|
||||
size_t sizeInBytes() {
|
||||
return (size_t)size.x() * (size_t)size.y() * PICA::sizePerPixel(format);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "PICA/regs.hpp"
|
||||
#include "boost/icl/interval.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "math_util.hpp"
|
||||
#include "opengl.hpp"
|
||||
|
||||
template <typename T>
|
||||
|
@ -53,7 +54,7 @@ struct Texture {
|
|||
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
|
||||
|
||||
// Returns the format of this texture as a string
|
||||
std::string formatToString() {
|
||||
std::string_view formatToString() {
|
||||
return PICA::textureFormatToString(format);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ class GPU;
|
|||
|
||||
class RendererNull final : public Renderer {
|
||||
public:
|
||||
RendererNull(GPU& gpu, const std::array<u32, regNum>& internalRegs);
|
||||
RendererNull(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
||||
~RendererNull() override;
|
||||
|
||||
void reset() override;
|
||||
|
@ -12,6 +12,7 @@ class RendererNull final : public Renderer {
|
|||
void initGraphicsContext(SDL_Window* window) override;
|
||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override;
|
||||
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override;
|
||||
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
|
||||
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override;
|
||||
void screenshot(const std::string& name) override;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -4,7 +4,7 @@ class GPU;
|
|||
|
||||
class RendererSw final : public Renderer {
|
||||
public:
|
||||
RendererSw(GPU& gpu, const std::array<u32, regNum>& internalRegs);
|
||||
RendererSw(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
||||
~RendererSw() override;
|
||||
|
||||
void reset() override;
|
||||
|
@ -12,6 +12,7 @@ class RendererSw final : public Renderer {
|
|||
void initGraphicsContext(SDL_Window* window) override;
|
||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override;
|
||||
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override;
|
||||
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
|
||||
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override;
|
||||
void screenshot(const std::string& name) override;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -44,7 +44,7 @@ class RendererVK final : public Renderer {
|
|||
|
||||
u64 currentFrame = 0;
|
||||
public:
|
||||
RendererVK(GPU& gpu, const std::array<u32, regNum>& internalRegs);
|
||||
RendererVK(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
|
||||
~RendererVK() override;
|
||||
|
||||
void reset() override;
|
||||
|
@ -52,6 +52,7 @@ class RendererVK final : public Renderer {
|
|||
void initGraphicsContext(SDL_Window* window) override;
|
||||
void clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) override;
|
||||
void displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) override;
|
||||
void textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) override;
|
||||
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override;
|
||||
void screenshot(const std::string& name) override;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -11,7 +11,11 @@ class ACService {
|
|||
MAKE_LOG_FUNCTION(log, acLogger)
|
||||
|
||||
// Service commands
|
||||
void cancelConnectAsync(u32 messagePointer);
|
||||
void closeAsync(u32 messagePointer);
|
||||
void createDefaultConfig(u32 messagePointer);
|
||||
void getLastErrorCode(u32 messagePointer);
|
||||
void registerDisconnectEvent(u32 messagePointer);
|
||||
void setClientVersion(u32 messagePointer);
|
||||
|
||||
public:
|
||||
|
|
|
@ -12,6 +12,7 @@ class AMService {
|
|||
|
||||
// Service commands
|
||||
void getDLCTitleInfo(u32 messagePointer);
|
||||
void getPatchTitleInfo(u32 messagePointer);
|
||||
void listTitleInfo(u32 messagePointer);
|
||||
|
||||
public:
|
||||
|
|
|
@ -13,14 +13,22 @@ class BOSSService {
|
|||
// Service commands
|
||||
void cancelTask(u32 messagePointer);
|
||||
void initializeSession(u32 messagePointer);
|
||||
void getNsDataIdList(u32 messagePointer);
|
||||
void getErrorCode(u32 messagePointer);
|
||||
void getNsDataIdList(u32 messagePointer, u32 commandWord);
|
||||
void getOptoutFlag(u32 messagePointer);
|
||||
void getStorageEntryInfo(u32 messagePointer); // Unknown what this is, name taken from Citra
|
||||
void getTaskIdList(u32 messagePointer);
|
||||
void getTaskInfo(u32 messagePOinter);
|
||||
void getTaskInfo(u32 messagePointer);
|
||||
void getTaskServiceStatus(u32 messagePointer);
|
||||
void getTaskState(u32 messagePointer);
|
||||
void getTaskStatus(u32 messagePointer);
|
||||
void getTaskStorageInfo(u32 messagePointer);
|
||||
void receiveProperty(u32 messagePointer);
|
||||
void registerNewArrivalEvent(u32 messagePointer);
|
||||
void registerStorageEntry(u32 messagePointer);
|
||||
void registerTask(u32 messagePointer);
|
||||
void sendProperty(u32 messagePointer);
|
||||
void startTask(u32 messagePointer);
|
||||
void unregisterStorage(u32 messagePointer);
|
||||
void unregisterTask(u32 messagePointer);
|
||||
|
||||
|
|
|
@ -1,21 +1,33 @@
|
|||
#pragma once
|
||||
#include <array>
|
||||
#include <optional>
|
||||
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
#include "result/result.hpp"
|
||||
|
||||
// Yay, circular dependencies!
|
||||
class Kernel;
|
||||
|
||||
class CAMService {
|
||||
Handle handle = KernelHandles::CAM;
|
||||
Memory& mem;
|
||||
Kernel& kernel;
|
||||
MAKE_LOG_FUNCTION(log, camLogger)
|
||||
|
||||
using Event = std::optional<Handle>;
|
||||
static constexpr size_t portCount = 4; // PORT_NONE, PORT_CAM1, PORT_CAM2, PORT_BOTH
|
||||
std::array<Event, portCount> bufferErrorInterruptEvents;
|
||||
|
||||
// Service commands
|
||||
void driverInitialize(u32 messagePointer);
|
||||
void getMaxLines(u32 messagePointer);
|
||||
void getBufferErrorInterruptEvent(u32 messagePointer);
|
||||
|
||||
public:
|
||||
CAMService(Memory& mem) : mem(mem) {}
|
||||
public:
|
||||
CAMService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
};
|
|
@ -18,6 +18,7 @@ class CECDService {
|
|||
|
||||
// Service commands
|
||||
void getInfoEventHandle(u32 messagePointer);
|
||||
void openAndRead(u32 messagePointer);
|
||||
|
||||
public:
|
||||
CECDService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
|
|
|
@ -19,6 +19,8 @@ class FRDService {
|
|||
Memory& mem;
|
||||
MAKE_LOG_FUNCTION(log, frdLogger)
|
||||
|
||||
bool loggedIn = false;
|
||||
|
||||
// Service commands
|
||||
void attachToEventNotification(u32 messagePointer);
|
||||
void getFriendKeyList(u32 messagePointer);
|
||||
|
@ -27,8 +29,11 @@ class FRDService {
|
|||
void getMyPresence(u32 messagePointer);
|
||||
void getMyProfile(u32 messagePointer);
|
||||
void getMyScreenName(u32 messsagePointer);
|
||||
void hasLoggedIn(u32 messagePointer);
|
||||
void logout(u32 messagePointer);
|
||||
void setClientSDKVersion(u32 messagePointer);
|
||||
void setNotificationMask(u32 messagePointer);
|
||||
void updateGameModeDescription(u32 messagePointer);
|
||||
|
||||
public:
|
||||
FRDService(Memory& mem) : mem(mem) {}
|
||||
|
|
|
@ -37,9 +37,11 @@ class FSService {
|
|||
|
||||
// Service commands
|
||||
void createDirectory(u32 messagePointer);
|
||||
void createExtSaveData(u32 messagePointer);
|
||||
void createFile(u32 messagePointer);
|
||||
void closeArchive(u32 messagePointer);
|
||||
void controlArchive(u32 messagePointer);
|
||||
void deleteExtSaveData(u32 messagePointer);
|
||||
void deleteFile(u32 messagePointer);
|
||||
void formatSaveData(u32 messagePointer);
|
||||
void formatThisUserSaveData(u32 messagePointer);
|
||||
|
|
|
@ -40,11 +40,32 @@ class GPUService {
|
|||
MAKE_LOG_FUNCTION(log, gspGPULogger)
|
||||
void processCommandBuffer();
|
||||
|
||||
struct FramebufferInfo {
|
||||
u32 activeFb;
|
||||
u32 leftFramebufferVaddr;
|
||||
u32 rightFramebufferVaddr;
|
||||
u32 stride;
|
||||
u32 format;
|
||||
u32 displayFb;
|
||||
u32 attribute;
|
||||
};
|
||||
static_assert(sizeof(FramebufferInfo) == 28, "GSP::GPU::FramebufferInfo has the wrong size");
|
||||
|
||||
struct FramebufferUpdate {
|
||||
u8 index;
|
||||
u8 dirtyFlag;
|
||||
u16 pad0;
|
||||
std::array<FramebufferInfo, 2> framebufferInfo;
|
||||
u32 pad1;
|
||||
};
|
||||
static_assert(sizeof(FramebufferUpdate) == 64, "GSP::GPU::FramebufferUpdate has the wrong size");
|
||||
|
||||
// Service commands
|
||||
void acquireRight(u32 messagePointer);
|
||||
void flushDataCache(u32 messagePointer);
|
||||
void registerInterruptRelayQueue(u32 messagePointer);
|
||||
void setAxiConfigQoSMode(u32 messagePointer);
|
||||
void setBufferSwap(u32 messagePointer);
|
||||
void setInternalPriorities(u32 messagePointer);
|
||||
void setLCDForceBlack(u32 messagePointer);
|
||||
void storeDataCache(u32 messagePointer);
|
||||
|
@ -60,6 +81,8 @@ class GPUService {
|
|||
void triggerTextureCopy(u32* cmd);
|
||||
void flushCacheRegions(u32* cmd);
|
||||
|
||||
void setBufferSwapImpl(u32 screen_id, const FramebufferInfo& info);
|
||||
|
||||
public:
|
||||
GPUService(Memory& mem, GPU& gpu, Kernel& kernel, u32& currentPID) : mem(mem), gpu(gpu),
|
||||
kernel(kernel), currentPID(currentPID) {}
|
||||
|
@ -72,4 +95,4 @@ public:
|
|||
std::memset(ptr, 0, 0x1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
|
|
@ -71,6 +71,7 @@ class HIDService {
|
|||
void getGyroscopeLowCalibrateParam(u32 messagePointer);
|
||||
void getGyroscopeCoefficient(u32 messagePointer);
|
||||
void getIPCHandles(u32 messagePointer);
|
||||
void getSoundVolume(u32 messagePointer);
|
||||
|
||||
// Don't call these prior to initializing shared mem pls
|
||||
template <typename T>
|
||||
|
@ -141,4 +142,6 @@ class HIDService {
|
|||
void releaseTouchScreen() {
|
||||
touchScreenPressed = false;
|
||||
}
|
||||
|
||||
bool isTouchScreenPressed() { return touchScreenPressed; }
|
||||
};
|
||||
|
|
24
include/services/mcu/mcu_hwc.hpp
Normal file
24
include/services/mcu/mcu_hwc.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
namespace MCU {
|
||||
class HWCService {
|
||||
Handle handle = KernelHandles::MCU_HWC;
|
||||
Memory& mem;
|
||||
MAKE_LOG_FUNCTION(log, mcuLogger)
|
||||
|
||||
const EmulatorConfig& config;
|
||||
|
||||
// Service commands
|
||||
void getBatteryLevel(u32 messagePointer);
|
||||
|
||||
public:
|
||||
HWCService(Memory& mem, const EmulatorConfig& config) : mem(mem), config(config) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
};
|
||||
} // namespace MCU
|
|
@ -18,11 +18,13 @@ class MICService {
|
|||
void setIirFilter(u32 messagePointer);
|
||||
void setPower(u32 messagePointer);
|
||||
void startSampling(u32 messagePointer);
|
||||
void stopSampling(u32 messagePointer);
|
||||
void theCaptainToadFunction(u32 messagePointer);
|
||||
|
||||
u8 gain = 0; // How loud our microphone input signal is
|
||||
bool micEnabled = false;
|
||||
bool shouldClamp = false;
|
||||
bool isSampling = false;
|
||||
|
||||
public:
|
||||
MICService(Memory& mem) : mem(mem) {}
|
||||
|
|
|
@ -11,6 +11,7 @@ class NDMService {
|
|||
MAKE_LOG_FUNCTION(log, ndmLogger)
|
||||
|
||||
// Service commands
|
||||
void clearHalfAwakeMacFilter(u32 messagePointer);
|
||||
void overrideDefaultDaemons(u32 messagePointer);
|
||||
void resumeDaemons(u32 messagePointer);
|
||||
void resumeScheduler(u32 messagePointer);
|
||||
|
|
18
include/services/news_u.hpp
Normal file
18
include/services/news_u.hpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
class NewsUService {
|
||||
Handle handle = KernelHandles::NEWS_U;
|
||||
Memory& mem;
|
||||
MAKE_LOG_FUNCTION(log, newsLogger)
|
||||
|
||||
// Service commands
|
||||
|
||||
public:
|
||||
NewsUService(Memory& mem) : mem(mem) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
};
|
|
@ -14,13 +14,38 @@ class NFCService {
|
|||
Kernel& kernel;
|
||||
MAKE_LOG_FUNCTION(log, nfcLogger)
|
||||
|
||||
enum class Old3DSAdapterStatus : u32 {
|
||||
Idle = 0,
|
||||
AttemptingToInitialize = 1,
|
||||
InitializationComplete = 2,
|
||||
Active = 3,
|
||||
};
|
||||
|
||||
enum class TagStatus : u8 {
|
||||
NotInitialized = 0,
|
||||
Initialized = 1,
|
||||
Scanning = 2,
|
||||
InRange = 3,
|
||||
OutOfRange = 4,
|
||||
Loaded = 5,
|
||||
};
|
||||
|
||||
// Kernel events signaled when an NFC tag goes in and out of range respectively
|
||||
std::optional<Handle> tagInRangeEvent, tagOutOfRangeEvent;
|
||||
|
||||
Old3DSAdapterStatus adapterStatus;
|
||||
TagStatus tagStatus;
|
||||
bool initialized = false;
|
||||
|
||||
// Service commands
|
||||
void communicationGetResult(u32 messagePointer);
|
||||
void communicationGetStatus(u32 messagePointer);
|
||||
void initialize(u32 messagePointer);
|
||||
void getTagInRangeEvent(u32 messagePointer);
|
||||
void getTagOutOfRangeEvent(u32 messagePointer);
|
||||
void getTagState(u32 messagePointer);
|
||||
void startCommunication(u32 messagePointer);
|
||||
void stopCommunication(u32 messagePointer);
|
||||
|
||||
public:
|
||||
NFCService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include "config.hpp"
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
|
@ -10,13 +11,39 @@ class PTMService {
|
|||
Memory& mem;
|
||||
MAKE_LOG_FUNCTION(log, ptmLogger)
|
||||
|
||||
const EmulatorConfig& config;
|
||||
|
||||
// Service commands
|
||||
void configureNew3DSCPU(u32 messagePointer);
|
||||
void getAdapterState(u32 messagePointer);
|
||||
void getBatteryLevel(u32 messagePointer);
|
||||
void getStepHistory(u32 messagePointer);
|
||||
void getTotalStepCount(u32 messagePointer);
|
||||
|
||||
public:
|
||||
PTMService(Memory& mem) : mem(mem) {}
|
||||
PTMService(Memory& mem, const EmulatorConfig& config) : mem(mem), config(config) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
|
||||
// 0% -> 0 (shutting down)
|
||||
// 1-5% -> 1
|
||||
// 6-10% -> 2
|
||||
// 11-30% -> 3
|
||||
// 31-60% -> 4
|
||||
// 61-100% -> 5
|
||||
static constexpr u8 batteryPercentToLevel(u8 percent) {
|
||||
if (percent == 0) {
|
||||
return 0;
|
||||
} else if (percent >= 1 && percent <= 5) {
|
||||
return 1;
|
||||
} else if (percent >= 6 && percent <= 10) {
|
||||
return 2;
|
||||
} else if (percent >= 11 && percent <= 30) {
|
||||
return 3;
|
||||
} else if (percent >= 31 && percent <= 60) {
|
||||
return 4;
|
||||
} else {
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
};
|
|
@ -24,14 +24,18 @@
|
|||
#include "services/http.hpp"
|
||||
#include "services/ir_user.hpp"
|
||||
#include "services/ldr_ro.hpp"
|
||||
#include "services/mcu/mcu_hwc.hpp"
|
||||
#include "services/mic.hpp"
|
||||
#include "services/ndm.hpp"
|
||||
#include "services/news_u.hpp"
|
||||
#include "services/nfc.hpp"
|
||||
#include "services/nim.hpp"
|
||||
#include "services/ptm.hpp"
|
||||
#include "services/soc.hpp"
|
||||
#include "services/ssl.hpp"
|
||||
#include "services/y2r.hpp"
|
||||
|
||||
struct EmulatorConfig;
|
||||
// More circular dependencies!!
|
||||
class Kernel;
|
||||
|
||||
|
@ -63,13 +67,17 @@ class ServiceManager {
|
|||
LCDService gsp_lcd;
|
||||
LDRService ldr;
|
||||
MICService mic;
|
||||
NDMService ndm;
|
||||
NewsUService news_u;
|
||||
NFCService nfc;
|
||||
NIMService nim;
|
||||
NDMService ndm;
|
||||
PTMService ptm;
|
||||
SOCService soc;
|
||||
SSLService ssl;
|
||||
Y2RService y2r;
|
||||
|
||||
MCU::HWCService mcu_hwc;
|
||||
|
||||
// "srv:" commands
|
||||
void enableNotification(u32 messagePointer);
|
||||
void getServiceHandle(u32 messagePointer);
|
||||
|
@ -78,7 +86,7 @@ class ServiceManager {
|
|||
void subscribe(u32 messagePointer);
|
||||
|
||||
public:
|
||||
ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel);
|
||||
ServiceManager(std::span<u32, 16> regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config);
|
||||
void reset();
|
||||
void initializeFS() { fs.initializeFilesystem(); }
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
#include <cstddef>
|
||||
|
||||
extern unsigned char _shared_font_bin[];
|
||||
extern size_t _shared_font_len;
|
25
include/services/ssl.hpp
Normal file
25
include/services/ssl.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include "helpers.hpp"
|
||||
#include "kernel_types.hpp"
|
||||
#include "logger.hpp"
|
||||
#include "memory.hpp"
|
||||
|
||||
#include <random>
|
||||
|
||||
class SSLService {
|
||||
Handle handle = KernelHandles::SSL;
|
||||
Memory& mem;
|
||||
MAKE_LOG_FUNCTION(log, sslLogger)
|
||||
|
||||
std::mt19937 rng; // Use a Mersenne Twister for RNG since this service is supposed to have better rng than just rand()
|
||||
bool initialized;
|
||||
|
||||
// Service commands
|
||||
void initialize(u32 messagePointer);
|
||||
void generateRandomData(u32 messagePointer);
|
||||
|
||||
public:
|
||||
SSLService(Memory& mem) : mem(mem) {}
|
||||
void reset();
|
||||
void handleSyncRequest(u32 messagePointer);
|
||||
};
|
|
@ -75,6 +75,7 @@ class Y2RService {
|
|||
void setInputLineWidth(u32 messagePointer);
|
||||
void setInputLines(u32 messagePointer);
|
||||
void setOutputFormat(u32 messagePointer);
|
||||
void setPackageParameter(u32 messagePointer);
|
||||
void setReceiving(u32 messagePointer);
|
||||
void setRotation(u32 messagePointer);
|
||||
void setSendingY(u32 messagePointer);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue