Add Metal support to Qt frontend and clean up renderer creation code (#795)

* Qt: Initial support for Metal renderer

* Clean up graphics context code

* Nits

* More nits

* Qt: Move screen-related stuff to own folder

* Qt: Make screen widget polymorphic

* Qt: Re-add Metal

* Add factory for screen widget

* Qt: Support compilation without Metal

* Qt: Fix build without Metal

* Oops

* oops
This commit is contained in:
wheremyfoodat
2025-07-26 23:13:08 +03:00
committed by GitHub
parent 8b0b1939cf
commit 0446bcdaa1
23 changed files with 331 additions and 146 deletions

View File

@@ -191,7 +191,7 @@ void RendererGL::initGraphicsContextInternal() {
// The OpenGL renderer doesn't need to do anything with the GL context (For Qt frontend) or the SDL window (For SDL frontend)
// So we just call initGraphicsContextInternal for both
void RendererGL::initGraphicsContext([[maybe_unused]] SDL_Window* window) { initGraphicsContextInternal(); }
void RendererGL::initGraphicsContext([[maybe_unused]] void* context) { initGraphicsContextInternal(); }
// Set up the OpenGL blending context to match the emulated PICA
void RendererGL::setupBlending() {

View File

@@ -10,8 +10,8 @@
#include "PICA/gpu.hpp"
#include "PICA/pica_hash.hpp"
#include "screen_layout.hpp"
#include "SDL_metal.h"
#include "screen_layout.hpp"
using namespace PICA;
@@ -57,9 +57,7 @@ void RendererMTL::reset() {
colorRenderTargetCache.reset();
}
void RendererMTL::setMTKLayer(void* layer) {
metalLayer = (CA::MetalLayer*)layer;
}
void RendererMTL::setMTKLayer(void* layer) { metalLayer = (CA::MetalLayer*)layer; }
void RendererMTL::display() {
CA::MetalDrawable* drawable = metalLayer->nextDrawable();
@@ -151,13 +149,13 @@ void RendererMTL::display() {
drawable->release();
}
void RendererMTL::initGraphicsContext(SDL_Window* window) {
// On iOS, the SwiftUI side handles the MetalLayer
#ifdef PANDA3DS_IOS
void RendererMTL::initGraphicsContext(void* window) {
// On Qt and IOS, the frontend handles the MetalLayer
#if defined(PANDA3DS_FRONTEND_QT) || defined(PANDA3DS_IOS)
device = MTL::CreateSystemDefaultDevice();
#else
// TODO: what should be the type of the view?
void* view = SDL_Metal_CreateView(window);
void* view = SDL_Metal_CreateView((SDL_Window*)window);
metalLayer = (CA::MetalLayer*)SDL_Metal_GetLayer(view);
device = MTL::CreateSystemDefaultDevice();
metalLayer->setDevice(device);

View File

@@ -6,7 +6,7 @@ RendererNull::~RendererNull() {}
void RendererNull::reset() {}
void RendererNull::display() {}
void RendererNull::initGraphicsContext(SDL_Window* window) {}
void RendererNull::initGraphicsContext(void* context) {}
void RendererNull::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) {}
void RendererNull::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {}
void RendererNull::textureCopy(u32 inputAddr, u32 outputAddr, u32 totalBytes, u32 inputSize, u32 outputSize, u32 flags) {}

View File

@@ -7,7 +7,7 @@ RendererSw::~RendererSw() {}
void RendererSw::reset() { printf("RendererSW: Unimplemented reset call\n"); }
void RendererSw::display() { printf("RendererSW: Unimplemented display call\n"); }
void RendererSw::initGraphicsContext(SDL_Window* window) { printf("RendererSW: Unimplemented initGraphicsContext call\n"); }
void RendererSw::initGraphicsContext(void* context) { printf("RendererSW: Unimplemented initGraphicsContext call\n"); }
void RendererSw::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 control) { printf("RendererSW: Unimplemented clearBuffer call\n"); }
void RendererSw::displayTransfer(u32 inputAddr, u32 outputAddr, u32 inputSize, u32 outputSize, u32 flags) {

View File

@@ -173,7 +173,8 @@ std::tuple<vk::UniquePipeline, vk::UniquePipelineLayout> createGraphicsPipeline(
vk::PipelineDynamicStateCreateInfo dynamicState = {};
static vk::DynamicState dynamicStates[] = {// The viewport and scissor of the framebuffer will be dynamic at
// run-time
vk::DynamicState::eViewport, vk::DynamicState::eScissor};
vk::DynamicState::eViewport, vk::DynamicState::eScissor
};
dynamicState.dynamicStateCount = std::size(dynamicStates);
dynamicState.pDynamicStates = dynamicStates;
@@ -469,7 +470,8 @@ vk::RenderPass RendererVK::getRenderPass(vk::Format colorFormat, std::optional<v
vk::SubpassDependency(
0, VK_SUBPASS_EXTERNAL, vk::PipelineStageFlagBits::eAllGraphics, vk::PipelineStageFlagBits::eAllGraphics,
vk::AccessFlagBits::eColorAttachmentWrite, vk::AccessFlagBits::eColorAttachmentWrite, vk::DependencyFlagBits::eByRegion
)};
)
};
renderPassInfo.setDependencies(subpassDependencies);
@@ -892,8 +894,8 @@ using VulkanDynamicLoader = vk::detail::DynamicLoader;
using VulkanDynamicLoader = vk::DynamicLoader;
#endif
void RendererVK::initGraphicsContext(SDL_Window* window) {
targetWindow = window;
void RendererVK::initGraphicsContext(void* windowPointer) {
targetWindow = (SDL_Window*)windowPointer;
// Resolve all instance function pointers
static VulkanDynamicLoader dl;
VULKAN_HPP_DEFAULT_DISPATCHER.init(dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr"));
@@ -978,8 +980,8 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
}
// Create surface
if (window) {
if (VkSurfaceKHR newSurface; SDL_Vulkan_CreateSurface(window, instance.get(), &newSurface)) {
if (targetWindow) {
if (VkSurfaceKHR newSurface; SDL_Vulkan_CreateSurface(targetWindow, instance.get(), &newSurface)) {
swapchainSurface = newSurface;
} else {
Helpers::warn("Error creating Vulkan surface");
@@ -1127,7 +1129,7 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
vk::Extent2D swapchainExtent;
{
int windowWidth, windowHeight;
SDL_Vulkan_GetDrawableSize(window, &windowWidth, &windowHeight);
SDL_Vulkan_GetDrawableSize(targetWindow, &windowWidth, &windowHeight);
swapchainExtent.width = windowWidth;
swapchainExtent.height = windowHeight;
}
@@ -1275,7 +1277,8 @@ void RendererVK::initGraphicsContext(SDL_Window* window) {
static vk::DescriptorSetLayoutBinding displayShaderLayout[] = {
{// Just a singular texture slot
0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment},
0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment
},
};
if (auto createResult = Vulkan::DescriptorUpdateBatch::create(device.get()); createResult.has_value()) {
@@ -1407,7 +1410,8 @@ void RendererVK::clearBuffer(u32 startAddress, u32 endAddress, u32 value, u32 co
static vk::ImageSubresourceRange depthStencilRanges[2] = {
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eDepth, 0, 1, 0, 1),
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eStencil, 0, 1, 0, 1)};
vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eStencil, 0, 1, 0, 1)
};
// Clear RenderTarget
getCurrentCommandBuffer().clearDepthStencilImage(