Update home menu branch (#759)

* Fix typo (#680)

Co-authored-by: Noumi <139501014+noumidev@users.noreply.github.com>

* More PTM stuff

Co-Authored-By: Noumi <139501014+noumidev@users.noreply.github.com>

* Make system language configurable

* Fix building crypto++ for x64 target on Apple silicon MacOS

* Attempt to switch to M1 runners again

* Prevent selecting Vulkan renderer in Qt frontend and present a message

* Libretro: Add system language option

* Only enable audio by default on libretro for now

* CMake: Bump version

* Store configuration file in AppData root if not in working directory (#693)

* Store configuration file in AppData root if not in working directory

This fixes MacOS app bundles, as the emulator cannot write the config
file into the app bundle.

* Remove duplicate fs calls

* I'm an idiot sandwich

---------

Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>

* GL: Add usingGLES to driverInfo struct (#694)

* Wayland fixes part 1

* Support GLES on desktop

* Qt: Fix Wayland support

Qt will only create a Wayland surface when show() is called on the main
window and on the ScreenWidget. Thus, call the function before creating
the GL context.

Doesn't cause regressions on XWayland, untested in other platforms.

Fixes #586

* No need to call screen->show() twice

* Fix disabling Wayland & building on some distros (#700)

* GLES: Properly stub out logic ops

* Fix git versioning

* Android_Build: Implement ccache (#703)

* Android_Build: Implement ccache

* Update Android_Build.yml

* Update Android_Build.yml

---------

Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>

* Removed dead Citra link in readme (#706)

* CRO: Lighter icache flushes

* Implement Luma icache SVCs

* Add missing SVC logs

* GPU: Add sw texture copies

* Use vk::detail::DynamicLoader instead of vk::DynamicLoader (#710)

* Use vk::detail::DynamicLoader instead of vk::DynamicLoader

* Update renderer_vk.cpp

* Vk: Fix typo

* Vk: Lock CI runners to SDK version 1.3.301 temporarily

* Vk: Fixing CI pt 2

* Vulkan: Fixing CI pt 3

* Vk: Fix typo

* Temporarily give 80MB to all processes (#715)

* Try to cross-compile Libretro core for arm64 (#717)

* Try to cross-compile Libretro core for arm64

* Bonk

* Update Hydra_Build.yml

* [WIP] Libretro: Add audio support (#714)

* Libretro: Add audio support

* Adding audio interface part 1

* Audio device pt 2

* More audio device

* More audio device

* Morea uudi odevice

* More audio device

* More audio device

* More audio device

---------

Co-authored-by: wheremyfoodat <44909372+wheremyfoodat@users.noreply.github.com>

* Libretro audio device: Fix frame count

* Mark audio devices as final

* Add toggle for libretro audio device (#719)

* Very important work (#720)

* Very important work

* Most important fix

* Add more HLE service calls for eshop (#721)

* CI: Fix Vulkan SDK action (#723)

* GPU registers: Fix writes to some registers ignoring the mask (#725)

Co-authored-by: henry <23128103+atem2069@users.noreply.github.com>

* OLED theme

* OLED theme config fix (#736)

Co-authored-by: smiRaphi <neogt404@gmail.com>

* Adding Swedish translation

* Fix Metal renderer compilation on iOS

* [Core] Improve iOS compilation workflow

* [Qt] Hook Swedish to UI

* AppDataDocumentProvider: Typo (#740)

* More iOS work

* More iOS progress

* More iOS work

* AppDataDocumentProvider: Add missing ``COLUMN_FLAGS`` in the default document projectation (#741)

Fixes unable to copy files from device to app's internal storage problem

* More iOS work

* ios: Simplify MTKView interface (still doesn't work though)

* ios: Pass CAMetalLayer instead of void* to Obj-C++ bridging header

* Fix bridging cast

* FINALLY IOS GRAPHICS

* ios: Remove printf spam

* Metal: Reimplement some texture formats on iOS

* metal: implement texture decoder

* metal: check for format support

* metal: implement texture swizzling

* metal: remove unused texture functions

* Shadergen types: Add Metal & MSL

* Format

* Undo submodule changes

* Readme: Add Chonkystation 3

* Metal: Use std::unique_ptr for texture decode

* AppDataDocumentProvider: Allow to remove documents (#744)

* AppDataDocumentProvider: Allow to remove documents

* Typo

* Metal renderer fixes for iOS

* iOS driver: Add doc comments

* iOS: Add frontend & frontend build files (#746)

* iOS: Add SwiftUI part to repo

* Add iOS build script

* Update SDL2 submodule

* Fix iOS build script

* CI: Update xcode tools for iOS

* Update iOS_Build.yml

* Update iOS build

* Lower XCode version

* A

* Update project.pbxproj

* Update iOS_Build.yml

* Update iOS_Build.yml

* Update build.sh

* iOS: Fail on build error

* iOS: Add file picker (#747)

* iOS: Add file picker

* Fix lock placement

* Qt: Add runpog icon (#752)

* Update discord-rpc submodule (#753)

* Remove cryptoppwin submodule (#754)

* Add optional texture hashing

* Fix build on new Vk SDK (#757)

Co-authored-by: Nadia Holmquist Pedersen <893884+nadiaholmquist@users.noreply.github.com>

* CI: Use new Vulkan SDK

---------

Co-authored-by: Noumi <139501014+noumidev@users.noreply.github.com>
Co-authored-by: Thomas <thomas@thomasw.dev>
Co-authored-by: Thomas <twvd@users.noreply.github.com>
Co-authored-by: Daniel López Guimaraes <danielectra@outlook.com>
Co-authored-by: Jonian Guveli <jonian@hardpixel.eu>
Co-authored-by: Ishan09811 <156402647+Ishan09811@users.noreply.github.com>
Co-authored-by: Auxy6858 <71662994+Auxy6858@users.noreply.github.com>
Co-authored-by: Paris Oplopoios <parisoplop@gmail.com>
Co-authored-by: henry <23128103+atem2069@users.noreply.github.com>
Co-authored-by: smiRaphi <neogt404@gmail.com>
Co-authored-by: smiRaphi <87574679+smiRaphi@users.noreply.github.com>
Co-authored-by: Daniel Nylander <po@danielnylander.se>
Co-authored-by: Samuliak <samuliak77@gmail.com>
Co-authored-by: Albert <45282415+ggrtk@users.noreply.github.com>
Co-authored-by: Nadia Holmquist Pedersen <893884+nadiaholmquist@users.noreply.github.com>
This commit is contained in:
wheremyfoodat 2025-06-23 04:59:22 +03:00 committed by GitHub
parent d5506f311f
commit 082b6216b3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
302 changed files with 55525 additions and 747 deletions

View file

@ -2,8 +2,8 @@
namespace PICA::ShaderGen {
// Graphics API this shader is targetting
enum class API { GL, GLES, Vulkan };
enum class API { GL, GLES, Vulkan, Metal };
// Shading language to use (Only GLSL for the time being)
enum class Language { GLSL };
} // namespace PICA::ShaderGen
// Shading language to use
enum class Language { GLSL, MSL };
} // namespace PICA::ShaderGen

View file

@ -0,0 +1,9 @@
#pragma once
#if defined(__LIBRETRO__) && defined(USE_LIBRETRO_AUDIO_DEVICE)
#include "audio/libretro_audio_device.hpp"
using AudioDevice = LibretroAudioDevice;
#else
#include "audio/miniaudio_device.hpp"
using AudioDevice = MiniAudioDevice;
#endif

View file

@ -0,0 +1,36 @@
#pragma once
#include <array>
#include "config.hpp"
#include "helpers.hpp"
#include "ring_buffer.hpp"
class AudioDeviceInterface {
protected:
static constexpr usize maxFrameCount = 0x2000;
using Samples = Common::RingBuffer<s16, maxFrameCount * 2>;
using RenderBatchCallback = usize (*)(const s16*, usize);
Samples* samples = nullptr;
const AudioDeviceConfig& audioSettings;
// Store the last stereo sample we output. We play this when underruning to avoid pops.
std::array<s16, 2> lastStereoSample{};
public:
AudioDeviceInterface(Samples* samples, const AudioDeviceConfig& audioSettings) : samples(samples), audioSettings(audioSettings) {}
bool running = false;
Samples* getSamples() { return samples; }
// If safe is on, we create a null audio device
virtual void init(Samples& samples, bool safe = false) = 0;
virtual void close() = 0;
virtual void start() = 0;
virtual void stop() = 0;
// Only used for audio devices that render multiple audio frames in one go, eg the libretro audio device.
virtual void renderBatch(RenderBatchCallback callback) {}
};

View file

@ -0,0 +1,61 @@
#pragma once
#include <cstring>
#include "audio/audio_device_interface.hpp"
class LibretroAudioDevice final : public AudioDeviceInterface {
bool initialized = false;
public:
LibretroAudioDevice(const AudioDeviceConfig& audioSettings) : AudioDeviceInterface(nullptr, audioSettings), initialized(false) {
running = false;
}
void init(Samples& samples, bool safe = false) override {
this->samples = &samples;
initialized = true;
running = false;
}
void close() override {
initialized = false;
running = false;
};
void start() override { running = true; }
void stop() override { running = false; };
void renderBatch(RenderBatchCallback callback) override {
if (running) {
static constexpr usize sampleRate = 32768; // 3DS samples per second
static constexpr usize frameCount = sampleRate / 60; // 3DS samples per video frame
static constexpr usize channelCount = 2;
static s16 audioBuffer[frameCount * channelCount];
usize samplesWritten = 0;
samplesWritten += samples->pop(audioBuffer, frameCount * channelCount);
// Get the last sample for underrun handling
if (samplesWritten != 0) {
std::memcpy(&lastStereoSample[0], &audioBuffer[(samplesWritten - 1) * 2], sizeof(lastStereoSample));
}
// If underruning, copy the last output sample
{
s16* pointer = &audioBuffer[samplesWritten * 2];
s16 l = lastStereoSample[0];
s16 r = lastStereoSample[1];
for (usize i = samplesWritten; i < frameCount; i++) {
*pointer++ = l;
*pointer++ = r;
}
}
callback(audioBuffer, sizeof(audioBuffer) / (channelCount * sizeof(s16)));
}
}
bool isInitialized() const { return initialized; }
};

View file

@ -3,39 +3,31 @@
#include <string>
#include <vector>
#include "config.hpp"
#include "helpers.hpp"
#include "audio/audio_device_interface.hpp"
#include "miniaudio.h"
#include "ring_buffer.hpp"
class MiniAudioDevice {
using Samples = Common::RingBuffer<ma_int16, 0x2000 * 2>;
class MiniAudioDevice final : public AudioDeviceInterface {
static constexpr ma_uint32 sampleRate = 32768; // 3DS sample rate
static constexpr ma_uint32 channelCount = 2; // Audio output is stereo
bool initialized = false;
ma_device device;
ma_context context;
ma_device_config deviceConfig;
Samples* samples = nullptr;
const AudioDeviceConfig& audioSettings;
bool initialized = false;
bool running = false;
// Store the last stereo sample we output. We play this when underruning to avoid pops.
std::array<s16, 2> lastStereoSample;
std::vector<std::string> audioDevices;
public:
MiniAudioDevice(const AudioDeviceConfig& audioSettings);
// If safe is on, we create a null audio device
void init(Samples& samples, bool safe = false);
void close();
void init(Samples& samples, bool safe = false) override;
void close() override;
void start();
void stop();
void start() override;
void stop() override;
bool isInitialized() const { return initialized; }
};

View file

@ -5,6 +5,7 @@
#include "audio/dsp_core.hpp"
#include "frontend_settings.hpp"
#include "renderer.hpp"
#include "services/region_codes.hpp"
struct AudioDeviceConfig {
// Audio curve to use for volumes between 0-100
@ -48,9 +49,26 @@ struct EmulatorConfig {
#endif
static constexpr bool accelerateShadersDefault = true;
#if defined(__LIBRETRO__)
static constexpr bool audioEnabledDefault = true;
#else
static constexpr bool audioEnabledDefault = false;
#endif
// We default to OpenGL on all platforms other than iOS
#if defined(PANDA3DS_IOS)
static constexpr RendererType rendererDefault = RendererType::Metal;
#else
static constexpr RendererType rendererDefault = RendererType::OpenGL;
#endif
static constexpr bool hashTexturesDefault = true;
bool shaderJitEnabled = shaderJitDefault;
bool useUbershaders = ubershaderDefault;
bool accelerateShaders = accelerateShadersDefault;
bool hashTextures = hashTexturesDefault;
bool accurateShaderMul = false;
bool discordRpcEnabled = false;
@ -58,14 +76,14 @@ struct EmulatorConfig {
bool forceShadergenForLights = true;
int lightShadergenThreshold = 1;
RendererType rendererType = RendererType::OpenGL;
RendererType rendererType = rendererDefault;
Audio::DSPCore::Type dspType = Audio::DSPCore::Type::HLE;
bool sdCardInserted = true;
bool sdWriteProtected = false;
bool usePortableBuild = false;
bool audioEnabled = false;
bool audioEnabled = audioEnabledDefault;
bool vsyncEnabled = true;
bool aacEnabled = true; // Enable AAC audio?
@ -77,6 +95,8 @@ struct EmulatorConfig {
// Default to 3% battery to make users suffer
int batteryPercentage = 3;
LanguageCodes systemLanguage = LanguageCodes::English;
// Default ROM path to open in Qt and misc frontends
std::filesystem::path defaultRomPath = "";
std::filesystem::path filePath;
@ -104,4 +124,7 @@ struct EmulatorConfig {
EmulatorConfig(const std::filesystem::path& path);
void load();
void save();
static LanguageCodes languageCodeFromString(std::string inString);
static const char* languageCodeToString(LanguageCodes code);
};

View file

@ -181,5 +181,7 @@ class CPU {
void addTicks(u64 ticks) { env.AddTicks(ticks); }
void clearCache() { jit->ClearCache(); }
void clearCacheRange(u32 start, u32 size) { jit->InvalidateCacheRange(start, size); }
void runFrame();
};

View file

@ -7,8 +7,8 @@
#include <span>
#include "PICA/gpu.hpp"
#include "audio/audio_device.hpp"
#include "audio/dsp_core.hpp"
#include "audio/miniaudio_device.hpp"
#include "cheats.hpp"
#include "config.hpp"
#include "cpu.hpp"
@ -48,14 +48,14 @@ class Emulator {
Scheduler scheduler;
Crypto::AESEngine aesEngine;
MiniAudioDevice audioDevice;
AudioDevice audioDevice;
Cheats cheats;
public:
static constexpr u32 width = 400;
static constexpr u32 height = 240 * 2; // * 2 because 2 screens
ROMType romType = ROMType::None;
bool running = false; // Is the emulator running a game?
bool running = false; // Is the emulator running a game?
private:
#ifdef PANDA3DS_ENABLE_HTTP_SERVER
@ -89,7 +89,6 @@ class Emulator {
~Emulator();
void step();
void render();
void reset(ReloadOption reload);
void runFrame();
// Poll the scheduler for events
@ -127,6 +126,7 @@ class Emulator {
LuaManager& getLua() { return lua; }
Scheduler& getScheduler() { return scheduler; }
Memory& getMemory() { return memory; }
AudioDeviceInterface& getAudioDevice() { return audioDevice; }
RendererType getRendererType() const { return config.rendererType; }
Renderer* getRenderer() { return gpu.getRenderer(); }

View file

@ -11,6 +11,7 @@ struct FrontendSettings {
Dark = 2,
GreetingsCat = 3,
Cream = 4,
Oled = 5,
};
// Different panda-themed window icons
@ -20,6 +21,7 @@ struct FrontendSettings {
Rnap = 2,
Rcow = 3,
SkyEmu = 4,
Runpog = 5,
};
Theme theme = Theme::Dark;

7
include/ios_driver.h Normal file
View file

@ -0,0 +1,7 @@
#pragma once
#include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h>
void iosCreateEmulator();
void iosLoadROM(NSString* pathNS);
void iosRunFrame(CAMetalLayer* layer);

View file

@ -5,7 +5,7 @@ namespace IPC {
namespace BufferType {
enum : std::uint32_t {
Send = 1,
Receive,
Receive = 2,
};
}

View file

@ -20,6 +20,7 @@ namespace KernelHandles {
CFG_U, // CFG service (Console & region info)
CFG_I,
CFG_S, // Used by most system apps in lieu of cfg:u
CFG_NOR, // Used by system settings app
CSND, // Plays audio directly from PCM samples
DLP_SRVR, // Download Play: Server. Used for network play.
DSP, // DSP service (Used for audio decoding and output)
@ -39,9 +40,10 @@ namespace KernelHandles {
NIM_U, // Updates
NDM, // ?????
NS_S, // Nintendo Shell service
NWM_EXT, // ?????
NWM_UDS, // Local multiplayer
NEWS_S, // news:u on steroids
NEWS_U, // This service literally has 1 command (AddNotification) and I don't even understand what it does
NEWS_U, // This service literally has 1 command (AddNotification)
PTM_U, // PTM service (Used for accessing various console info, such as battery, shell and pedometer state)
PTM_SYSM, // PTM system service
PTM_PLAY, // PTM Play service, used for retrieving play history
@ -85,6 +87,8 @@ namespace KernelHandles {
case CECD: return "CECD";
case CFG_U: return "CFG:U";
case CFG_I: return "CFG:I";
case CFG_S: return "CFG:S";
case CFG_NOR: return "CFG:NOR";
case CSND: return "CSND";
case DSP: return "DSP";
case DLP_SRVR: return "DLP::SRVR";
@ -102,6 +106,7 @@ namespace KernelHandles {
case NDM: return "NDM";
case NEWS_S: return "NEWS_S";
case NEWS_U: return "NEWS_U";
case NWM_EXT: return "nwm::EXT";
case NWM_UDS: return "nwm::UDS";
case NFC: return "NFC";
case NIM_AOC: return "NIM:AOC";
@ -109,6 +114,7 @@ namespace KernelHandles {
case PTM_U: return "PTM:U";
case PTM_SYSM: return "PTM:SYSM";
case PTM_PLAY: return "PTM:PLAY";
case PTM_GETS: return "PTM:GETS";
case SOC: return "SOC";
case SSL: return "SSL";
case Y2R: return "Y2R";

View file

@ -175,6 +175,8 @@ public:
void svcSignalEvent();
void svcSetTimer();
void svcSleepThread();
void svcInvalidateInstructionCacheRange();
void svcInvalidateEntireInstructionCache();
void connectToPort();
void outputDebugString();
void waitSynchronization1();
@ -250,4 +252,6 @@ public:
void sendGPUInterrupt(GPUInterrupt type) { serviceManager.sendGPUInterrupt(type); }
void clearInstructionCache();
void clearInstructionCacheRange(u32 start, u32 size);
u32 getSharedFontVaddr();
};

View file

@ -19,7 +19,7 @@ struct ResourceLimitValues {
// APPLICATION resource limit
static constexpr ResourceLimitValues appResourceLimits = {
.maxPriority = 0x18,
.maxCommit = 0x4000000,
.maxCommit = 64_MB + 16_MB, // We're currently giving 80MB to all apps. TODO: Implement extended memory properly
.maxThreads = 0x20,
.maxEvents = 0x20,
.maxMutexes = 0x20,
@ -33,7 +33,7 @@ static constexpr ResourceLimitValues appResourceLimits = {
// SYS_APPLET resource limit
static constexpr ResourceLimitValues sysAppletResourceLimits = {
.maxPriority = 0x4,
.maxCommit = 0x5E00000,
.maxCommit = 0x5E00000 - 16_MB,
.maxThreads = 0x1D,
.maxEvents = 0xB,
.maxMutexes = 0x8,

View file

@ -132,7 +132,7 @@ public:
static constexpr u32 totalPageCount = 1 << (32 - pageShift);
static constexpr u32 FCRAM_SIZE = u32(128_MB);
static constexpr u32 FCRAM_APPLICATION_SIZE = u32(64_MB);
static constexpr u32 FCRAM_APPLICATION_SIZE = u32(80_MB);
static constexpr u32 FCRAM_PAGE_COUNT = FCRAM_SIZE / pageSize;
static constexpr u32 FCRAM_APPLICATION_PAGE_COUNT = FCRAM_APPLICATION_SIZE / pageSize;

View file

@ -51,8 +51,12 @@ class Renderer {
u32 outputWindowWidth = 400;
u32 outputWindowHeight = 240 * 2;
// Should hw renderers hash textures? Stored separately from emulatorConfig because we'll be accessing it constantly, might be merged eventually
bool hashTextures = false;
EmulatorConfig* emulatorConfig = nullptr;
void doSoftwareTextureCopy(u32 inputAddr, u32 outputAddr, u32 copySize, u32 inputWidth, u32 inputGap, u32 outputWidth, u32 outputGap);
public:
Renderer(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs);
virtual ~Renderer();
@ -81,6 +85,14 @@ class Renderer {
virtual std::string getUbershader() { return ""; }
virtual void setUbershader(const std::string& shader) {}
// Only relevant for OpenGL renderer and other OpenGL-based backends (eg software)
// Called to notify the core to use OpenGL ES and not desktop GL
virtual void setupGLES() {}
// Only relevant for Metal renderer on iOS
// Passes a SwiftUI MTKView's layer (CAMetalLayer) to the renderer
virtual void setMTKLayer(void* layer) {};
// This function is called on every draw call before parsing vertex data.
// It is responsible for things like looking up which vertex/fragment shaders to use, recompiling them if they don't exist, choosing between
// ubershaders and shadergen, and so on.
@ -114,4 +126,5 @@ class Renderer {
}
void setConfig(EmulatorConfig* config) { emulatorConfig = config; }
void setHashTextures(bool setting) { hashTextures = setting; }
};

View file

@ -4,6 +4,7 @@
// Stuff like whether specific extensions are supported, and potentially things like OpenGL context information
namespace OpenGL {
struct Driver {
bool usingGLES = false;
bool supportsExtFbFetch = false;
bool supportsArmFbFetch = false;

View file

@ -40,7 +40,7 @@ class RendererGL final : public Renderer {
OpenGL::VertexArray hwShaderVAO;
OpenGL::VertexBuffer vbo;
// Data
// Data
struct {
// TEV configuration uniform locations
GLint textureEnvSourceLoc = -1;
@ -157,6 +157,7 @@ class RendererGL final : public Renderer {
void initGraphicsContextInternal();
void accelerateVertexUpload(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel);
void compileDisplayShader();
public:
RendererGL(GPU& gpu, const std::array<u32, regNum>& internalRegs, const std::array<u32, extRegNum>& externalRegs)
@ -169,14 +170,15 @@ class RendererGL final : public Renderer {
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
void drawVertices(PICA::PrimType primType, std::span<const PICA::Vertex> vertices) override; // Draw the given vertices
void deinitGraphicsContext() override;
virtual bool supportsShaderReload() override { return true; }
virtual std::string getUbershader() override;
virtual void setUbershader(const std::string& shader) override;
virtual bool prepareForDraw(ShaderUnit& shaderUnit, PICA::DrawAcceleration* accel) override;
virtual void setupGLES() override;
std::optional<ColourBuffer> getColourBuffer(u32 addr, PICA::ColorFmt format, u32 width, u32 height, bool createIfnotFound = true);
// Note: The caller is responsible for deleting the currently bound FBO before calling this

View file

@ -1,6 +1,8 @@
#pragma once
#include <array>
#include <string>
#include "PICA/pica_hash.hpp"
#include "PICA/regs.hpp"
#include "boost/icl/interval.hpp"
#include "helpers.hpp"
@ -11,55 +13,55 @@ template <typename T>
using Interval = boost::icl::right_open_interval<T>;
struct Texture {
u32 location;
u32 config; // Magnification/minification filter, wrapping configs, etc
PICA::TextureFmt format;
OpenGL::uvec2 size;
bool valid;
using Hash = PICAHash::HashType;
// Range of VRAM taken up by buffer
Interval<u32> range;
// OpenGL resources allocated to buffer
OpenGL::Texture texture;
u32 location;
u32 config; // Magnification/minification filter, wrapping configs, etc
Hash hash = Hash(0);
Texture() : valid(false) {}
PICA::TextureFmt format;
OpenGL::uvec2 size;
bool valid;
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
// Range of VRAM taken up by buffer
Interval<u32> range;
// OpenGL resources allocated to buffer
OpenGL::Texture texture;
u64 endLoc = (u64)loc + sizeInBytes();
// Check if start and end are valid here
range = Interval<u32>(loc, (u32)endLoc);
}
Texture() : valid(false) {}
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
bool matches(Texture& other) {
return location == other.location && format == other.format &&
size.x() == other.size.x() && size.y() == other.size.y();
}
Texture(u32 loc, PICA::TextureFmt format, u32 x, u32 y, u32 config, bool valid = true)
: location(loc), format(format), size({x, y}), config(config), valid(valid) {
u64 endLoc = (u64)loc + sizeInBytes();
// Check if start and end are valid here
range = Interval<u32>(loc, (u32)endLoc);
}
void allocate();
void setNewConfig(u32 newConfig);
void decodeTexture(std::span<const u8> data);
void free();
u64 sizeInBytes();
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
bool matches(Texture& other) {
return location == other.location && hash == other.hash && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
}
u32 decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
void allocate();
void setNewConfig(u32 newConfig);
void decodeTexture(std::span<const u8> data);
void free();
u64 sizeInBytes();
// Get the morton interleave offset of a texel based on its U and V values
static u32 mortonInterleave(u32 u, u32 v);
// Get the byte offset of texel (u, v) in the texture
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
u32 decodeTexel(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
// Returns the format of this texture as a string
std::string_view formatToString() {
return PICA::textureFormatToString(format);
}
// Get the morton interleave offset of a texel based on its U and V values
static u32 mortonInterleave(u32 u, u32 v);
// Get the byte offset of texel (u, v) in the texture
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
// TODO: Make hasAlpha a template parameter
u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data);
u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData);
// Returns the format of this texture as a string
std::string_view formatToString() { return PICA::textureFormatToString(format); }
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
// TODO: Make hasAlpha a template parameter
u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data);
u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData);
};

View file

@ -57,7 +57,7 @@ namespace Metal {
} else if (std::is_same<Format_t, PICA::DepthFmt>::value) {
pixelFormat = PICA::toMTLPixelFormatDepth((PICA::DepthFmt)format);
} else {
panic("Invalid format type");
Helpers::panic("Invalid format type");
}
MTL::TextureDescriptor* descriptor = MTL::TextureDescriptor::alloc()->init();

View file

@ -4,22 +4,28 @@
#include <array>
#include <string>
#include "PICA/pica_hash.hpp"
#include "PICA/regs.hpp"
#include "boost/icl/interval.hpp"
#include "helpers.hpp"
#include "math_util.hpp"
#include "opengl.hpp"
#include "renderer_mtl/pica_to_mtl.hpp"
// TODO: remove dependency on OpenGL
#include "opengl.hpp"
template <typename T>
using Interval = boost::icl::right_open_interval<T>;
namespace Metal {
struct Texture {
using Hash = PICAHash::HashType;
MTL::Device* device;
u32 location;
u32 config; // Magnification/minification filter, wrapping configs, etc
Hash hash = Hash(0);
PICA::TextureFmt format;
OpenGL::uvec2 size;
bool valid;
@ -27,7 +33,8 @@ namespace Metal {
// Range of VRAM taken up by buffer
Interval<u32> range;
PICA::PixelFormatInfo formatInfo;
PICA::MTLPixelFormatInfo formatInfo;
MTL::Texture* base = nullptr;
MTL::Texture* texture = nullptr;
MTL::SamplerState* sampler = nullptr;
@ -43,7 +50,8 @@ namespace Metal {
// For 2 textures to "match" we only care about their locations, formats, and dimensions to match
// For other things, such as filtering mode, etc, we can just switch the attributes of the cached texture
bool matches(Texture& other) {
return location == other.location && format == other.format && size.x() == other.size.x() && size.y() == other.size.y();
return location == other.location && hash == other.hash && format == other.format && size.x() == other.size.x() &&
size.y() == other.size.y();
}
void allocate();
@ -52,22 +60,7 @@ namespace Metal {
void free();
u64 sizeInBytes();
u8 decodeTexelU8(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
u16 decodeTexelU16(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
u32 decodeTexelU32(u32 u, u32 v, PICA::TextureFmt fmt, std::span<const u8> data);
// Get the morton interleave offset of a texel based on its U and V values
static u32 mortonInterleave(u32 u, u32 v);
// Get the byte offset of texel (u, v) in the texture
static u32 getSwizzledOffset(u32 u, u32 v, u32 width, u32 bytesPerPixel);
static u32 getSwizzledOffset_4bpp(u32 u, u32 v, u32 width);
// Returns the format of this texture as a string
std::string_view formatToString() { return PICA::textureFormatToString(format); }
// Returns the texel at coordinates (u, v) of an ETC1(A4) texture
// TODO: Make hasAlpha a template parameter
u32 getTexelETC(bool hasAlpha, u32 u, u32 v, u32 width, std::span<const u8> data);
u32 decodeETC(u32 alpha, u32 u, u32 v, u64 colourData);
};
} // namespace Metal

View file

@ -3,31 +3,28 @@
#include <Metal/Metal.hpp>
#include "PICA/regs.hpp"
// TODO: remove dependency on OpenGL
#include "opengl.hpp"
namespace PICA {
struct PixelFormatInfo {
struct MTLPixelFormatInfo {
MTL::PixelFormat pixelFormat;
size_t bytesPerTexel;
void (*decoder)(OpenGL::uvec2, u32, u32, std::span<const u8>, u8*);
bool needsSwizzle = false;
MTL::TextureSwizzleChannels swizzle{
.red = MTL::TextureSwizzleRed,
.green = MTL::TextureSwizzleGreen,
.blue = MTL::TextureSwizzleBlue,
.alpha = MTL::TextureSwizzleAlpha,
};
};
constexpr PixelFormatInfo pixelFormatInfos[14] = {
{MTL::PixelFormatRGBA8Unorm, 4}, // RGBA8
{MTL::PixelFormatRGBA8Unorm, 4}, // RGB8
{MTL::PixelFormatBGR5A1Unorm, 2}, // RGBA5551
{MTL::PixelFormatB5G6R5Unorm, 2}, // RGB565
{MTL::PixelFormatABGR4Unorm, 2}, // RGBA4
{MTL::PixelFormatRGBA8Unorm, 4}, // IA8
{MTL::PixelFormatRG8Unorm, 2}, // RG8
{MTL::PixelFormatRGBA8Unorm, 4}, // I8
{MTL::PixelFormatA8Unorm, 1}, // A8
{MTL::PixelFormatABGR4Unorm, 2}, // IA4
{MTL::PixelFormatABGR4Unorm, 2}, // I4
{MTL::PixelFormatA8Unorm, 1}, // A4
{MTL::PixelFormatRGBA8Unorm, 4}, // ETC1
{MTL::PixelFormatRGBA8Unorm, 4}, // ETC1A4
};
extern MTLPixelFormatInfo mtlPixelFormatInfos[14];
inline PixelFormatInfo getPixelFormatInfo(TextureFmt format) { return pixelFormatInfos[static_cast<int>(format)]; }
void checkForMTLPixelFormatSupport(MTL::Device* device);
inline MTLPixelFormatInfo getMTLPixelFormatInfo(TextureFmt format) { return mtlPixelFormatInfos[static_cast<int>(format)]; }
inline MTL::PixelFormat toMTLPixelFormatColor(ColorFmt format) {
switch (format) {
@ -35,7 +32,11 @@ namespace PICA {
case ColorFmt::RGB8: return MTL::PixelFormatRGBA8Unorm;
case ColorFmt::RGBA5551: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatBGR5A1Unorm?
case ColorFmt::RGB565: return MTL::PixelFormatRGBA8Unorm; // TODO: use MTL::PixelFormatB5G6R5Unorm?
#ifdef PANDA3DS_IOS
case ColorFmt::RGBA4: return MTL::PixelFormatRGBA8Unorm; // IOS + Metal doesn't support AGBR4 properly, at least on simulator
#else
case ColorFmt::RGBA4: return MTL::PixelFormatABGR4Unorm;
#endif
}
}

View file

@ -42,11 +42,13 @@ class RendererMTL final : public Renderer {
virtual void initGraphicsContext([[maybe_unused]] GL::Context* context) override {}
#endif
private:
CA::MetalLayer* metalLayer;
virtual void setMTKLayer(void* layer) override;
MTL::Device* device;
MTL::CommandQueue* commandQueue;
private:
CA::MetalLayer* metalLayer = nullptr;
MTL::Device* device = nullptr;
MTL::CommandQueue* commandQueue = nullptr;
Metal::CommandEncoder commandEncoder;
@ -98,6 +100,7 @@ class RendererMTL final : public Renderer {
void endRenderPass() {
if (renderCommandEncoder) {
renderCommandEncoder->endEncoding();
renderCommandEncoder->release();
renderCommandEncoder = nullptr;
}
}

View file

@ -0,0 +1,24 @@
#pragma once
#include "helpers.hpp"
// TODO: remove dependency on OpenGL
#include "opengl.hpp"
void decodeTexelABGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelBGR8ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelA1BGR5ToBGR5A1(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelA1BGR5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelB5G6R5ToB5G6R5(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelB5G6R5ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelABGR4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelABGR4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelAI8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelGR8ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelI8ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelA8ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelAI4ToABGR4(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelAI4ToRG8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelI4ToR8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelA4ToA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelETC1ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);
void decodeTexelETC1A4ToRGBA8(OpenGL::uvec2 size, u32 u, u32 v, std::span<const u8> inData, u8* outData);

View file

@ -9,8 +9,8 @@
namespace Vulkan {
VKAPI_ATTR VkBool32 VKAPI_CALL debugMessageCallback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData
vk::DebugUtilsMessageSeverityFlagBitsEXT messageSeverity, vk::DebugUtilsMessageTypeFlagsEXT messageType,
const vk::DebugUtilsMessengerCallbackDataEXT* callbackData, void* userData
);
void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...);

View file

@ -1,6 +1,7 @@
#pragma once
#include <cstring>
#include "config.hpp"
#include "helpers.hpp"
#include "logger.hpp"
#include "memory.hpp"
@ -11,6 +12,8 @@ class CFGService {
using Handle = HorizonHandle;
Memory& mem;
const EmulatorConfig& settings;
CountryCodes country = CountryCodes::US; // Default to USA
MAKE_LOG_FUNCTION(log, cfgLogger)
@ -18,8 +21,9 @@ class CFGService {
// Service functions
void getConfigInfoBlk2(u32 messagePointer);
void getConfigInfoBlk8(u32 messagePointer);
void getConfigInfoBlk8(u32 messagePointer, u32 commandWord);
void getCountryCodeID(u32 messagePointer);
void getCountryCodeString(u32 messagePointer);
void getLocalFriendCodeSeed(u32 messagePointer);
void getRegionCanadaUSA(u32 messagePointer);
void getSystemModel(u32 messagePointer);
@ -29,6 +33,11 @@ class CFGService {
void setConfigInfoBlk4(u32 messagePointer);
void updateConfigNANDSavegame(u32 messagePointer);
void translateCountryInfo(u32 messagePointer);
void isFangateSupported(u32 messagePointer);
// cfg:nor functions
void norInitialize(u32 messagePointer);
void norReadData(u32 messagePointer);
void getConfigInfo(u32 output, u32 blockID, u32 size, u32 permissionMask);
@ -40,7 +49,7 @@ class CFGService {
NOR, // cfg:nor
};
CFGService(Memory& mem) : mem(mem) {}
CFGService(Memory& mem, const EmulatorConfig& settings) : mem(mem), settings(settings) {}
void reset();
void handleSyncRequest(u32 messagePointer, Type type);
};

View file

@ -121,4 +121,4 @@ class GPUService {
std::memset(ptr, 0, 0x1000);
}
}
};
};

View file

@ -6,9 +6,6 @@
#include "result/result.hpp"
class LCDService {
using Handle = HorizonHandle;
Handle handle = KernelHandles::LCD;
Memory& mem;
MAKE_LOG_FUNCTION(log, gspLCDLogger)

View file

@ -6,20 +6,20 @@
#include "result/result.hpp"
class NSService {
Memory& mem;
Memory& mem;
MAKE_LOG_FUNCTION(log, nsLogger)
// Service commands
void launchTitle(u32 messagePointer);
// Service commands
void launchTitle(u32 messagePointer);
public:
enum class Type {
S, // ns:s
P, // ns:p
C, // ns:c
};
public:
enum class Type {
S, // ns:s
P, // ns:p
C, // ns:c
};
NSService(Memory& mem) : mem(mem) {}
void reset();
NSService(Memory& mem) : mem(mem) {}
void reset();
void handleSyncRequest(u32 messagePointer, Type type);
};