Merge pull request #764 from wheremyfoodat/better-resizing

Metal renderer & iOS frontend fixes
This commit is contained in:
wheremyfoodat 2025-06-29 23:26:41 +03:00 committed by GitHub
commit 1c98c1147d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 71 additions and 35 deletions

View file

@ -1,7 +1,9 @@
#pragma once #pragma once
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h> #include <QuartzCore/QuartzCore.h>
#include <stdint.h>
void iosCreateEmulator(); void iosCreateEmulator();
void iosLoadROM(NSString* pathNS); void iosLoadROM(NSString* pathNS);
void iosRunFrame(CAMetalLayer* layer); 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) { 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[2] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelA1BGR5ToRGBA8};
mtlPixelFormatInfos[3] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelB5G6R5ToRGBA8}; mtlPixelFormatInfos[3] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelB5G6R5ToRGBA8};
mtlPixelFormatInfos[4] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelABGR4ToRGBA8}; mtlPixelFormatInfos[4] = {MTL::PixelFormatRGBA8Unorm, 4, decodeTexelABGR4ToRGBA8};

View file

@ -130,7 +130,7 @@ void RendererMTL::display() {
// Top screen // Top screen
if (topScreen) { if (topScreen) {
renderCommandEncoder->setViewport( 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->setFragmentTexture(topScreen->get().texture, 0);
renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4));

View file

@ -10,8 +10,8 @@ struct BasicVertexOut {
}; };
struct NDCViewport { struct NDCViewport {
float2 offset; float2 offset;
float2 scale; float2 scale;
}; };
vertex BasicVertexOut vertexBlit(uint vid [[vertex_id]], constant NDCViewport& viewport [[buffer(0)]]) { 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) { IOS_EXPORT void iosLoadROM(NSString* pathNS) {
auto path = std::filesystem::path([pathNS UTF8String]); auto path = std::filesystem::path([pathNS UTF8String]);
emulator->loadROM(path); 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 #pragma once
#include <Foundation/Foundation.h> #include <Foundation/Foundation.h>
#include <QuartzCore/QuartzCore.h> #include <QuartzCore/QuartzCore.h>
#include <stdint.h>
void iosCreateEmulator(); void iosCreateEmulator();
void iosLoadROM(NSString* pathNS); void iosLoadROM(NSString* pathNS);
void iosRunFrame(CAMetalLayer* layer); void iosRunFrame(CAMetalLayer* layer);
void iosSetOutputSize(uint32_t width, uint32_t height);

View file

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