diff --git a/.github/workflows/Android_Build.yml b/.github/workflows/Android_Build.yml index 1bc8db83..37a5eb45 100644 --- a/.github/workflows/Android_Build.yml +++ b/.github/workflows/Android_Build.yml @@ -39,7 +39,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang @@ -107,7 +107,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.github/workflows/HTTP_Build.yml b/.github/workflows/HTTP_Build.yml index c7ec9679..11bf27eb 100644 --- a/.github/workflows/HTTP_Build.yml +++ b/.github/workflows/HTTP_Build.yml @@ -32,7 +32,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.github/workflows/Hydra_Build.yml b/.github/workflows/Hydra_Build.yml index b3cbebac..dbdfbf1b 100644 --- a/.github/workflows/Hydra_Build.yml +++ b/.github/workflows/Hydra_Build.yml @@ -22,7 +22,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang @@ -65,7 +65,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang @@ -118,7 +118,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang @@ -165,7 +165,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.github/workflows/Linux_AppImage_Build.yml b/.github/workflows/Linux_AppImage_Build.yml index dcac7681..9e46072f 100644 --- a/.github/workflows/Linux_AppImage_Build.yml +++ b/.github/workflows/Linux_AppImage_Build.yml @@ -35,7 +35,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.github/workflows/Linux_Build.yml b/.github/workflows/Linux_Build.yml index 712894a3..d0ddfecf 100644 --- a/.github/workflows/Linux_Build.yml +++ b/.github/workflows/Linux_Build.yml @@ -35,7 +35,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.github/workflows/MacOS_Build.yml b/.github/workflows/MacOS_Build.yml index dd43a149..a405e788 100644 --- a/.github/workflows/MacOS_Build.yml +++ b/.github/workflows/MacOS_Build.yml @@ -27,7 +27,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.github/workflows/Qt_Build.yml b/.github/workflows/Qt_Build.yml index e36f2023..3db1e4f3 100644 --- a/.github/workflows/Qt_Build.yml +++ b/.github/workflows/Qt_Build.yml @@ -28,7 +28,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang @@ -66,7 +66,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang @@ -164,7 +164,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.github/workflows/Windows_Build.yml b/.github/workflows/Windows_Build.yml index 54eac5dd..caa3d806 100644 --- a/.github/workflows/Windows_Build.yml +++ b/.github/workflows/Windows_Build.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.github/workflows/iOS_Build.yml b/.github/workflows/iOS_Build.yml index f930ef3e..7d7e604b 100644 --- a/.github/workflows/iOS_Build.yml +++ b/.github/workflows/iOS_Build.yml @@ -31,7 +31,7 @@ jobs: - name: Setup Vulkan SDK uses: humbletim/setup-vulkan-sdk@main with: - vulkan-query-version: 1.3.296.0 + vulkan-query-version: latest vulkan-use-cache: true vulkan-components: Vulkan-Headers, Vulkan-Loader, SPIRV-Tools, Glslang diff --git a/.gitignore b/.gitignore index 817786a3..5fdf525d 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,12 @@ fb.bat config.toml CMakeSettings.json + +# IDE files + +# KDevelop files +*.kdev4 +# IDEA/Clion files +.idea/ +# VSC files +/.vscode/ \ No newline at end of file diff --git a/include/ios_driver.h b/include/ios_driver.h index 7f783970..85b3be2c 100644 --- a/include/ios_driver.h +++ b/include/ios_driver.h @@ -1,7 +1,9 @@ #pragma once #include #include +#include void iosCreateEmulator(); void iosLoadROM(NSString* pathNS); -void iosRunFrame(CAMetalLayer* layer); \ No newline at end of file +void iosRunFrame(CAMetalLayer* layer); +void iosSetOutputSize(uint32_t width, uint32_t height); \ No newline at end of file diff --git a/include/renderer.hpp b/include/renderer.hpp index 0798184d..753bc319 100644 --- a/include/renderer.hpp +++ b/include/renderer.hpp @@ -53,10 +53,12 @@ class Renderer { // Should hw renderers hash textures? Stored separately from emulatorConfig because we'll be accessing it constantly, might be merged eventually bool hashTextures = false; + bool outputSizeChanged = true; 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& internalRegs, const std::array& externalRegs); virtual ~Renderer(); @@ -121,6 +123,7 @@ class Renderer { void setDepthBufferLoc(u32 loc) { depthBufferLoc = loc; } void setOutputSize(u32 width, u32 height) { + outputSizeChanged = true; outputWindowWidth = width; outputWindowHeight = height; } diff --git a/include/renderer_gl/renderer_gl.hpp b/include/renderer_gl/renderer_gl.hpp index a862cd26..5476843f 100644 --- a/include/renderer_gl/renderer_gl.hpp +++ b/include/renderer_gl/renderer_gl.hpp @@ -40,7 +40,7 @@ class RendererGL final : public Renderer { OpenGL::VertexArray hwShaderVAO; OpenGL::VertexBuffer vbo; - // Data + // Data that will be uploaded to the ubershader struct { // TEV configuration uniform locations GLint textureEnvSourceLoc = -1; @@ -146,6 +146,15 @@ class RendererGL final : public Renderer { PICA::ShaderGen::FragmentGenerator fragShaderGen; OpenGL::Driver driverInfo; + // Information about the final 3DS screen -> Window blit, accounting for things like scaling and shifting the output based on + // the window's dimensions. + struct { + int destX = 0; + int destY = 0; + int destWidth = 400; + int destHeight = 480; + } blitInfo; + MAKE_LOG_FUNCTION(log, rendererLogger) void setupBlending(); void setupStencilTest(bool stencilEnable); diff --git a/include/renderer_mtl/renderer_mtl.hpp b/include/renderer_mtl/renderer_mtl.hpp index 10fac7cd..add02d52 100644 --- a/include/renderer_mtl/renderer_mtl.hpp +++ b/include/renderer_mtl/renderer_mtl.hpp @@ -88,6 +88,16 @@ class RendererMTL final : public Renderer { MTL::Texture* lastColorTexture = nullptr; MTL::Texture* lastDepthTexture = nullptr; + // Information about the final 3DS screen -> Window blit, accounting for things like scaling and shifting the output based on + // the window's dimensions. + struct { + float topScreenX = 0; + float topScreenY = 0; + float bottomScreenX = 40; + float bottomScreenY = 240; + float scale = 1.0; + } blitInfo; + // Debug std::string nextRenderPassName; diff --git a/include/renderer_vk/vk_debug.hpp b/include/renderer_vk/vk_debug.hpp index ed712269..0cf3f938 100644 --- a/include/renderer_vk/vk_debug.hpp +++ b/include/renderer_vk/vk_debug.hpp @@ -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, ...); diff --git a/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp b/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp index ddec3a36..5cc37878 100644 --- a/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp +++ b/src/core/PICA/dynapica/shader_rec_emitter_x64.cpp @@ -27,6 +27,7 @@ static constexpr Xmm scratch2 = xmm1; static constexpr Xmm src1_xmm = xmm2; static constexpr Xmm src2_xmm = xmm3; static constexpr Xmm src3_xmm = xmm4; +static constexpr Xmm scratch3 = xmm5; #if defined(PANDA3DS_MS_ABI) // Register that points to PICA state. Must be volatile for the aforementioned reasons @@ -382,20 +383,12 @@ void ShaderEmitter::storeRegister(Xmm source, const PICAShader& shader, u32 dest (((writeMask & 0b0010) ? 0 : 1) << 4) | (((writeMask & 0b0001) ? 2 : 3) << 6); - // Reorder instructions based on whether the source == scratch1. This is to avoid overwriting scratch1 if it's the source, - // While also having the memory load come first to mitigate execution hazards and give the load more time to complete before reading if possible - if (source != scratch1) { - movaps(scratch1, xword[statePointer + offset]); - movaps(scratch2, source); - } else { - movaps(scratch2, source); - movaps(scratch1, xword[statePointer + offset]); - } - - unpckhps(scratch2, scratch1); // Unpack X/Y components of source and destination - unpcklps(scratch1, source); // Unpack Z/W components of source and destination - shufps(scratch1, scratch2, selector); // "merge-shuffle" dest and source using selecto - movaps(xword[statePointer + offset], scratch1); // Write back + movaps(scratch3, xword[statePointer + offset]); + movaps(scratch2, source); + unpckhps(scratch2, scratch3); // Unpack X/Y components of source and destination + unpcklps(scratch3, source); // Unpack Z/W components of source and destination + shufps(scratch3, scratch2, selector); // "merge-shuffle" dest and source using selecto + movaps(xword[statePointer + offset], scratch3); // Write back } } diff --git a/src/core/kernel/file_operations.cpp b/src/core/kernel/file_operations.cpp index 2b2020d1..52dd7493 100644 --- a/src/core/kernel/file_operations.cpp +++ b/src/core/kernel/file_operations.cpp @@ -14,7 +14,6 @@ namespace FileOps { }; } - void Kernel::handleFileOperation(u32 messagePointer, Handle file) { const u32 cmd = mem.read32(messagePointer); switch (cmd) { @@ -89,6 +88,7 @@ void Kernel::readFile(u32 messagePointer, Handle fileHandle) { if (file->fd) { std::unique_ptr data(new u8[size]); IOFile f(file->fd); + f.seek(offset); auto [success, bytesRead] = f.readBytes(data.get(), size); @@ -146,6 +146,7 @@ void Kernel::writeFile(u32 messagePointer, Handle fileHandle) { } IOFile f(file->fd); + f.seek(offset); auto [success, bytesWritten] = f.writeBytes(data.get(), size); // TODO: Should this check only the byte? diff --git a/src/core/renderer_gl/renderer_gl.cpp b/src/core/renderer_gl/renderer_gl.cpp index 828ca120..9dbc0434 100644 --- a/src/core/renderer_gl/renderer_gl.cpp +++ b/src/core/renderer_gl/renderer_gl.cpp @@ -582,7 +582,33 @@ void RendererGL::display() { if constexpr (!Helpers::isHydraCore()) { glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); screenFramebuffer.bind(OpenGL::ReadFramebuffer); - glBlitFramebuffer(0, 0, 400, 480, 0, 0, outputWindowWidth, outputWindowHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + if (outputSizeChanged) { + outputSizeChanged = false; + + const float srcAspect = 400.0f / 480.0f; // 3DS aspect ratio + const float dstAspect = float(outputWindowWidth) / float(outputWindowHeight); + + blitInfo.destWidth = outputWindowWidth; + blitInfo.destHeight = outputWindowHeight; + blitInfo.destX = 0; + blitInfo.destY = 0; + + if (dstAspect > srcAspect) { + // Window is wider than source + blitInfo.destWidth = int(outputWindowHeight * srcAspect + 0.5f); + blitInfo.destX = (outputWindowWidth - blitInfo.destWidth) / 2; + } else { + // Window is taller than source + blitInfo.destHeight = int(outputWindowWidth / srcAspect + 0.5f); + blitInfo.destY = (outputWindowHeight - blitInfo.destHeight) / 2; + } + } + + glBlitFramebuffer( + 0, 0, 400, 480, blitInfo.destX, blitInfo.destY, blitInfo.destX + blitInfo.destWidth, blitInfo.destY + blitInfo.destHeight, + GL_COLOR_BUFFER_BIT, GL_LINEAR + ); } } diff --git a/src/core/renderer_mtl/pica_to_mtl.cpp b/src/core/renderer_mtl/pica_to_mtl.cpp index 538e6d52..973ad1bf 100644 --- a/src/core/renderer_mtl/pica_to_mtl.cpp +++ b/src/core/renderer_mtl/pica_to_mtl.cpp @@ -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}; diff --git a/src/core/renderer_mtl/renderer_mtl.cpp b/src/core/renderer_mtl/renderer_mtl.cpp index e2e4c085..bbc8212a 100644 --- a/src/core/renderer_mtl/renderer_mtl.cpp +++ b/src/core/renderer_mtl/renderer_mtl.cpp @@ -103,16 +103,44 @@ void RendererMTL::display() { renderCommandEncoder->setRenderPipelineState(displayPipeline); renderCommandEncoder->setFragmentSamplerState(nearestSampler, 0); + if (outputSizeChanged) { + outputSizeChanged = false; + + const float srcAspect = 400.0 / 480.0; + const float destAspect = float(outputWindowWidth) / float(outputWindowHeight); + int destX = 0, destY = 0, destWidth = outputWindowWidth, destHeight = outputWindowHeight; + + if (destAspect > srcAspect) { + // Window is wider than source + destWidth = int(outputWindowHeight * srcAspect + 0.5f); + destX = (outputWindowWidth - destWidth) / 2; + } else { + // Window is taller than source + destHeight = int(outputWindowWidth / srcAspect + 0.5f); + destY = (outputWindowHeight - destHeight) / 2; + } + + blitInfo.scale = float(destWidth) / 400.0f; + blitInfo.topScreenX = float(destX); + blitInfo.topScreenY = float(destY + (destHeight - int(480 * blitInfo.scale)) / 2); + blitInfo.bottomScreenX = float(destX) + 40 * blitInfo.scale; + blitInfo.bottomScreenY = blitInfo.topScreenY + 240 * blitInfo.scale; + } + // Top screen if (topScreen) { - renderCommandEncoder->setViewport(MTL::Viewport{0, 0, 400, 240, 0.0f, 1.0f}); + renderCommandEncoder->setViewport( + 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)); } // Bottom screen if (bottomScreen) { - renderCommandEncoder->setViewport(MTL::Viewport{40, 240, 320, 240, 0.0f, 1.0f}); + renderCommandEncoder->setViewport( + MTL::Viewport{blitInfo.bottomScreenX, blitInfo.bottomScreenY, 320 * blitInfo.scale, 240 * blitInfo.scale, 0.0f, 1.0f} + ); renderCommandEncoder->setFragmentTexture(bottomScreen->get().texture, 0); renderCommandEncoder->drawPrimitives(MTL::PrimitiveTypeTriangleStrip, NS::UInteger(0), NS::UInteger(4)); } diff --git a/src/core/renderer_vk/vk_debug.cpp b/src/core/renderer_vk/vk_debug.cpp index f3f099c8..5e26e77a 100644 --- a/src/core/renderer_vk/vk_debug.cpp +++ b/src/core/renderer_vk/vk_debug.cpp @@ -61,8 +61,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 ) { debugMessageCallback( vk::DebugUtilsMessageSeverityFlagBitsEXT(messageSeverity), vk::DebugUtilsMessageTypeFlagsEXT(messageType), *callbackData @@ -70,7 +70,7 @@ namespace Vulkan { return VK_FALSE; } - #ifdef GPU_DEBUG_INFO +#ifdef GPU_DEBUG_INFO void setObjectName(vk::Device device, vk::ObjectType objectType, const void* objectHandle, const char* format, ...) { va_list args; va_start(args, format); diff --git a/src/core/services/gsp_gpu.cpp b/src/core/services/gsp_gpu.cpp index 5c6ab3d6..37964d83 100644 --- a/src/core/services/gsp_gpu.cpp +++ b/src/core/services/gsp_gpu.cpp @@ -66,7 +66,10 @@ void GPUService::handleSyncRequest(u32 messagePointer) { case ServiceCommands::WriteHwRegs: writeHwRegs(messagePointer); break; case ServiceCommands::WriteHwRegsWithMask: writeHwRegsWithMask(messagePointer); break; case ServiceCommands::InvalidateDataCache: invalidateDataCache(messagePointer); break; - default: Helpers::panic("GPU service requested. Command: %08X\n", command); + default: + Helpers::warn("GPU service requested. Command: %08X\n", command); + mem.write32(messagePointer + 4, Result::Success); + break; } } diff --git a/src/host_shaders/metal_blit.metal b/src/host_shaders/metal_blit.metal index 31b94ec4..6709c82a 100644 --- a/src/host_shaders/metal_blit.metal +++ b/src/host_shaders/metal_blit.metal @@ -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)]]) { diff --git a/src/ios_driver.mm b/src/ios_driver.mm index 8ec87436..cb98b269 100644 --- a/src/ios_driver.mm +++ b/src/ios_driver.mm @@ -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); } \ No newline at end of file diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index c060318e..ed6c2852 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -415,9 +415,8 @@ void MainWindow::dispatchMessage(const EmulatorMessage& message) { case MessageType::SetScreenSize: { const u32 width = message.screenSize.width; const u32 height = message.screenSize.height; - - emu->setOutputSize(width, height); screen->resizeSurface(width, height); + emu->setOutputSize(width, height); break; } @@ -566,7 +565,10 @@ void MainWindow::handleScreenResize(u32 width, u32 height) { message.screenSize.width = width; message.screenSize.height = height; - sendMessage(message); + if (messageQueueMutex.try_lock()) { + messageQueue.push_back(message); + messageQueueMutex.unlock(); + } } void MainWindow::initControllers() { diff --git a/src/pandios/Alber/Headers/ios_driver.h b/src/pandios/Alber/Headers/ios_driver.h index 7f783970..85b3be2c 100644 --- a/src/pandios/Alber/Headers/ios_driver.h +++ b/src/pandios/Alber/Headers/ios_driver.h @@ -1,7 +1,9 @@ #pragma once #include #include +#include void iosCreateEmulator(); void iosLoadROM(NSString* pathNS); -void iosRunFrame(CAMetalLayer* layer); \ No newline at end of file +void iosRunFrame(CAMetalLayer* layer); +void iosSetOutputSize(uint32_t width, uint32_t height); \ No newline at end of file diff --git a/src/pandios/Pandios.xcodeproj/project.xcworkspace/xcuserdata/giorgos.xcuserdatad/UserInterfaceState.xcuserstate b/src/pandios/Pandios.xcodeproj/project.xcworkspace/xcuserdata/giorgos.xcuserdatad/UserInterfaceState.xcuserstate index 38cf2747..3009b72b 100644 Binary files a/src/pandios/Pandios.xcodeproj/project.xcworkspace/xcuserdata/giorgos.xcuserdatad/UserInterfaceState.xcuserstate and b/src/pandios/Pandios.xcodeproj/project.xcworkspace/xcuserdata/giorgos.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/src/pandios/Pandios/ContentView.swift b/src/pandios/Pandios/ContentView.swift index 16a64f7c..d8f321f9 100644 --- a/src/pandios/Pandios/ContentView.swift +++ b/src/pandios/Pandios/ContentView.swift @@ -3,21 +3,35 @@ 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 @@ -28,7 +42,7 @@ class DocumentViewController: UIViewController, DocumentDelegate { emulatorLock.unlock() } } - + func show() { documentPicker.displayPicker() } @@ -36,25 +50,15 @@ class DocumentViewController: UIViewController, DocumentDelegate { struct DocumentView: UIViewControllerRepresentable { func makeUIViewController(context: Context) -> DocumentViewController { - return DocumentViewController() - } - - func updateUIViewController(_ uiViewController: DocumentViewController, context: Context) { - // No update needed + DocumentViewController() } + + func updateUIViewController(_ uiViewController: DocumentViewController, context: Context) {} } struct ContentView: UIViewRepresentable { - @State var showFileImporter = true - - /* - func makeCoordinator() -> Renderer { - Renderer(self) - } - */ - - func makeUIView(context: UIViewRepresentableContext) -> MTKView { - let mtkView = MTKView() + func makeUIView(context: Context) -> ResizeAwareMTKView { + let mtkView = ResizeAwareMTKView() mtkView.preferredFramesPerSecond = 60 mtkView.enableSetNeedsDisplay = true mtkView.isPaused = true @@ -65,16 +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) { - 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) } }