Metal renderer & iOS frontend fixes

This commit is contained in:
wheremyfoodat 2025-06-29 23:20:35 +03:00
parent 630952f36b
commit adcd03d31e
8 changed files with 71 additions and 36 deletions

View file

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

View file

@ -40,7 +40,13 @@ namespace PICA {
};
void checkForMTLPixelFormatSupport(MTL::Device* device) {
if (!device->supportsFamily(MTL::GPUFamilyApple1)) {
#ifndef PANDA3DS_IOS_SIMULATOR
const bool supportsApple1 = device->supportsFamily(MTL::GPUFamilyApple1);
#else
// iOS simulator claims to support Apple1, yet doesn't support a bunch of texture formats from it...
const bool supportsApple1 = false;
#endif
if (!supportsApple1) {
mtlPixelFormatInfos[2] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelA1BGR5ToRGBA8};
mtlPixelFormatInfos[3] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelB5G6R5ToRGBA8};
mtlPixelFormatInfos[4] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelABGR4ToRGBA8};

View file

@ -130,7 +130,7 @@ void RendererMTL::display() {
// Top screen
if (topScreen) {
renderCommandEncoder->setViewport(
MTL::Viewport{blitInfo.topScreenX, blitInfo.topScreenY + 240 * blitInfo.scale, 400 * blitInfo.scale, 240 * blitInfo.scale, 0.0f, 1.0f}
MTL::Viewport{blitInfo.topScreenX, blitInfo.topScreenY, 400 * blitInfo.scale, 240 * blitInfo.scale, 0.0f, 1.0f}
);
renderCommandEncoder->setFragmentTexture(topScreen->get().texture, 0);
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));

View file

@ -10,8 +10,8 @@ struct BasicVertexOut {
};
struct NDCViewport {
float2 offset;
float2 scale;
float2 offset;
float2 scale;
};
vertex BasicVertexOut vertexBlit(uint vid [[vertex_id]], constant NDCViewport& viewport [[buffer(0)]]) {

View file

@ -33,6 +33,10 @@ IOS_EXPORT void iosRunFrame(CAMetalLayer* layer) {
}
IOS_EXPORT void iosLoadROM(NSString* pathNS) {
auto path = std::filesystem::path([pathNS UTF8String]);
emulator->loadROM(path);
auto path = std::filesystem::path([pathNS UTF8String]);
emulator->loadROM(path);
}
IOS_EXPORT void iosSetOutputSize(uint32_t width, uint32_t height) {
emulator->setOutputSize(width, height);
}

View file

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

View file

@ -3,27 +3,42 @@ import SwiftUI
import MetalKit
import Darwin
final class DrawableSize {
var width: UInt32 = 0
var height: UInt32 = 0
var sizeChanged = false
}
var emulatorLock = NSLock()
var drawableSize = DrawableSize()
class ResizeAwareMTKView: MTKView {
var onResize: ((CGSize) -> Void)?
override func layoutSubviews() {
super.layoutSubviews()
onResize?(self.drawableSize)
}
}
class DocumentViewController: UIViewController, DocumentDelegate {
var documentPicker: DocumentPicker!
override func viewDidLoad() {
super.viewDidLoad()
/// set up the document picker
documentPicker = DocumentPicker(presentationController: self, delegate: self)
/// When the view loads (ie user opens the app) show the file picker
show()
}
/// callback from the document picker
/// Callback from the document picker
func didPickDocument(document: Document?) {
if let pickedDoc = document {
let fileURL = pickedDoc.fileURL
print("Loading ROM", fileURL)
emulatorLock.lock()
print(fileURL.path(percentEncoded: false))
iosLoadROM(fileURL.path(percentEncoded: false))
emulatorLock.unlock()
}
@ -36,25 +51,15 @@ class DocumentViewController: UIViewController, DocumentDelegate {
struct DocumentView: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> DocumentViewController {
return DocumentViewController()
DocumentViewController()
}
func updateUIViewController(_ uiViewController: DocumentViewController, context: Context) {
// No update needed
}
func updateUIViewController(_ uiViewController: DocumentViewController, context: Context) {}
}
struct ContentView: UIViewRepresentable {
@State var showFileImporter = true
/*
func makeCoordinator() -> Renderer {
Renderer(self)
}
*/
func makeUIView(context: UIViewRepresentableContext<ContentView>) -> MTKView {
let mtkView = MTKView()
func makeUIView(context: Context) -> ResizeAwareMTKView {
let mtkView = ResizeAwareMTKView()
mtkView.preferredFramesPerSecond = 60
mtkView.enableSetNeedsDisplay = true
mtkView.isPaused = true
@ -64,17 +69,34 @@ struct ContentView: UIViewRepresentable {
}
mtkView.framebufferOnly = false
mtkView.drawableSize = mtkView.frame.size
mtkView.onResize = { newDrawableSize in
let newWidth = UInt32(newDrawableSize.width)
let newHeight = UInt32(newDrawableSize.height)
emulatorLock.lock()
if drawableSize.width != newWidth || drawableSize.height != newHeight {
drawableSize.width = newWidth
drawableSize.height = newHeight
drawableSize.sizeChanged = true
}
emulatorLock.unlock()
}
let dispatchQueue = DispatchQueue(label: "QueueIdentification", qos: .background)
let metalLayer = mtkView.layer as! CAMetalLayer;
let metalLayer = mtkView.layer as! CAMetalLayer
dispatchQueue.async{
dispatchQueue.async {
iosCreateEmulator()
while (true) {
emulatorLock.lock()
iosRunFrame(metalLayer);
if drawableSize.sizeChanged {
drawableSize.sizeChanged = false
iosSetOutputSize(drawableSize.width, drawableSize.height)
}
iosRunFrame(metalLayer)
emulatorLock.unlock()
}
}
@ -82,14 +104,13 @@ struct ContentView: UIViewRepresentable {
return mtkView
}
func updateUIView(_ uiView: MTKView, context: UIViewRepresentableContext<ContentView>) {
print("Updating MTKView");
}
func updateUIView(_ uiView: ResizeAwareMTKView, context: Context) {}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
DocumentView();
ContentView();
DocumentView()
ContentView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}