mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	yuzu: Implement Vulkan frontend
Adds a Qt and SDL2 frontend for Vulkan. It also finishes the missing bits on Vulkan initialization.
This commit is contained in:
		
							parent
							
								
									8299f1ceef
								
							
						
					
					
						commit
						f92cbc5501
					
				@ -75,6 +75,13 @@ public:
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Returns if window is shown (not minimized)
 | 
			
		||||
    virtual bool IsShown() const = 0;
 | 
			
		||||
 | 
			
		||||
    /// Retrieves Vulkan specific handlers from the window
 | 
			
		||||
    virtual void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
 | 
			
		||||
                                        void* surface) const = 0;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Signal that a touch pressed event has occurred (e.g. mouse click pressed)
 | 
			
		||||
     * @param framebuffer_x Framebuffer x-coordinate that was pressed
 | 
			
		||||
 | 
			
		||||
@ -154,6 +154,7 @@ if (ENABLE_VULKAN)
 | 
			
		||||
        renderer_vulkan/maxwell_to_vk.cpp
 | 
			
		||||
        renderer_vulkan/maxwell_to_vk.h
 | 
			
		||||
        renderer_vulkan/renderer_vulkan.h
 | 
			
		||||
        renderer_vulkan/renderer_vulkan.cpp
 | 
			
		||||
        renderer_vulkan/vk_blit_screen.cpp
 | 
			
		||||
        renderer_vulkan/vk_blit_screen.h
 | 
			
		||||
        renderer_vulkan/vk_buffer_cache.cpp
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										265
									
								
								src/video_core/renderer_vulkan/renderer_vulkan.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								src/video_core/renderer_vulkan/renderer_vulkan.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,265 @@
 | 
			
		||||
// Copyright 2018 yuzu Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <optional>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/telemetry.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/core_timing.h"
 | 
			
		||||
#include "core/frontend/emu_window.h"
 | 
			
		||||
#include "core/memory.h"
 | 
			
		||||
#include "core/perf_stats.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "core/telemetry_session.h"
 | 
			
		||||
#include "video_core/gpu.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/declarations.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_blit_screen.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_device.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_memory_manager.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_rasterizer.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_scheduler.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_swapchain.h"
 | 
			
		||||
 | 
			
		||||
namespace Vulkan {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity_,
 | 
			
		||||
                       VkDebugUtilsMessageTypeFlagsEXT type,
 | 
			
		||||
                       const VkDebugUtilsMessengerCallbackDataEXT* data,
 | 
			
		||||
                       [[maybe_unused]] void* user_data) {
 | 
			
		||||
    const vk::DebugUtilsMessageSeverityFlagBitsEXT severity{severity_};
 | 
			
		||||
    const char* message{data->pMessage};
 | 
			
		||||
 | 
			
		||||
    if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError) {
 | 
			
		||||
        LOG_CRITICAL(Render_Vulkan, "{}", message);
 | 
			
		||||
    } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning) {
 | 
			
		||||
        LOG_WARNING(Render_Vulkan, "{}", message);
 | 
			
		||||
    } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo) {
 | 
			
		||||
        LOG_INFO(Render_Vulkan, "{}", message);
 | 
			
		||||
    } else if (severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose) {
 | 
			
		||||
        LOG_DEBUG(Render_Vulkan, "{}", message);
 | 
			
		||||
    }
 | 
			
		||||
    return VK_FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetReadableVersion(u32 version) {
 | 
			
		||||
    return fmt::format("{}.{}.{}", VK_VERSION_MAJOR(version), VK_VERSION_MINOR(version),
 | 
			
		||||
                       VK_VERSION_PATCH(version));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string GetDriverVersion(const VKDevice& device) {
 | 
			
		||||
    // Extracted from
 | 
			
		||||
    // https://github.com/SaschaWillems/vulkan.gpuinfo.org/blob/5dddea46ea1120b0df14eef8f15ff8e318e35462/functions.php#L308-L314
 | 
			
		||||
    const u32 version = device.GetDriverVersion();
 | 
			
		||||
 | 
			
		||||
    if (device.GetDriverID() == vk::DriverIdKHR::eNvidiaProprietary) {
 | 
			
		||||
        const u32 major = (version >> 22) & 0x3ff;
 | 
			
		||||
        const u32 minor = (version >> 14) & 0x0ff;
 | 
			
		||||
        const u32 secondary = (version >> 6) & 0x0ff;
 | 
			
		||||
        const u32 tertiary = version & 0x003f;
 | 
			
		||||
        return fmt::format("{}.{}.{}.{}", major, minor, secondary, tertiary);
 | 
			
		||||
    }
 | 
			
		||||
    if (device.GetDriverID() == vk::DriverIdKHR::eIntelProprietaryWindows) {
 | 
			
		||||
        const u32 major = version >> 14;
 | 
			
		||||
        const u32 minor = version & 0x3fff;
 | 
			
		||||
        return fmt::format("{}.{}", major, minor);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return GetReadableVersion(version);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_extensions) {
 | 
			
		||||
    std::sort(std::begin(available_extensions), std::end(available_extensions));
 | 
			
		||||
 | 
			
		||||
    static constexpr std::size_t AverageExtensionSize = 64;
 | 
			
		||||
    std::string separated_extensions;
 | 
			
		||||
    separated_extensions.reserve(available_extensions.size() * AverageExtensionSize);
 | 
			
		||||
 | 
			
		||||
    const auto end = std::end(available_extensions);
 | 
			
		||||
    for (auto extension = std::begin(available_extensions); extension != end; ++extension) {
 | 
			
		||||
        if (const bool is_last = extension + 1 == end; is_last) {
 | 
			
		||||
            separated_extensions += *extension;
 | 
			
		||||
        } else {
 | 
			
		||||
            separated_extensions += fmt::format("{},", *extension);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return separated_extensions;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
RendererVulkan::RendererVulkan(Core::Frontend::EmuWindow& window, Core::System& system)
 | 
			
		||||
    : RendererBase(window), system{system} {}
 | 
			
		||||
 | 
			
		||||
RendererVulkan::~RendererVulkan() {
 | 
			
		||||
    ShutDown();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
 | 
			
		||||
    const auto& layout = render_window.GetFramebufferLayout();
 | 
			
		||||
    if (framebuffer && layout.width > 0 && layout.height > 0 && render_window.IsShown()) {
 | 
			
		||||
        const VAddr framebuffer_addr = framebuffer->address + framebuffer->offset;
 | 
			
		||||
        const bool use_accelerated =
 | 
			
		||||
            rasterizer->AccelerateDisplay(*framebuffer, framebuffer_addr, framebuffer->stride);
 | 
			
		||||
        const bool is_srgb = use_accelerated && screen_info.is_srgb;
 | 
			
		||||
        if (swapchain->HasFramebufferChanged(layout) || swapchain->GetSrgbState() != is_srgb) {
 | 
			
		||||
            swapchain->Create(layout.width, layout.height, is_srgb);
 | 
			
		||||
            blit_screen->Recreate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        scheduler->WaitWorker();
 | 
			
		||||
 | 
			
		||||
        swapchain->AcquireNextImage();
 | 
			
		||||
        const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated);
 | 
			
		||||
 | 
			
		||||
        scheduler->Flush(false, render_semaphore);
 | 
			
		||||
 | 
			
		||||
        if (swapchain->Present(render_semaphore, fence)) {
 | 
			
		||||
            blit_screen->Recreate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        render_window.SwapBuffers();
 | 
			
		||||
        rasterizer->TickFrame();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    render_window.PollEvents();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RendererVulkan::Init() {
 | 
			
		||||
    PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
 | 
			
		||||
    render_window.RetrieveVulkanHandlers(&vkGetInstanceProcAddr, &instance, &surface);
 | 
			
		||||
    const vk::DispatchLoaderDynamic dldi(instance, vkGetInstanceProcAddr);
 | 
			
		||||
 | 
			
		||||
    std::optional<vk::DebugUtilsMessengerEXT> callback;
 | 
			
		||||
    if (Settings::values.renderer_debug && dldi.vkCreateDebugUtilsMessengerEXT) {
 | 
			
		||||
        callback = CreateDebugCallback(dldi);
 | 
			
		||||
        if (!callback) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!PickDevices(dldi)) {
 | 
			
		||||
        if (callback) {
 | 
			
		||||
            instance.destroy(*callback, nullptr, dldi);
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    debug_callback = UniqueDebugUtilsMessengerEXT(
 | 
			
		||||
        *callback, vk::ObjectDestroy<vk::Instance, vk::DispatchLoaderDynamic>(
 | 
			
		||||
                       instance, nullptr, device->GetDispatchLoader()));
 | 
			
		||||
 | 
			
		||||
    Report();
 | 
			
		||||
 | 
			
		||||
    memory_manager = std::make_unique<VKMemoryManager>(*device);
 | 
			
		||||
 | 
			
		||||
    resource_manager = std::make_unique<VKResourceManager>(*device);
 | 
			
		||||
 | 
			
		||||
    const auto& framebuffer = render_window.GetFramebufferLayout();
 | 
			
		||||
    swapchain = std::make_unique<VKSwapchain>(surface, *device);
 | 
			
		||||
    swapchain->Create(framebuffer.width, framebuffer.height, false);
 | 
			
		||||
 | 
			
		||||
    scheduler = std::make_unique<VKScheduler>(*device, *resource_manager);
 | 
			
		||||
 | 
			
		||||
    rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device,
 | 
			
		||||
                                                    *resource_manager, *memory_manager, *scheduler);
 | 
			
		||||
 | 
			
		||||
    blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device,
 | 
			
		||||
                                                 *resource_manager, *memory_manager, *swapchain,
 | 
			
		||||
                                                 *scheduler, screen_info);
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RendererVulkan::ShutDown() {
 | 
			
		||||
    if (!device) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const auto dev = device->GetLogical();
 | 
			
		||||
    const auto& dld = device->GetDispatchLoader();
 | 
			
		||||
    if (dev && dld.vkDeviceWaitIdle) {
 | 
			
		||||
        dev.waitIdle(dld);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rasterizer.reset();
 | 
			
		||||
    blit_screen.reset();
 | 
			
		||||
    scheduler.reset();
 | 
			
		||||
    swapchain.reset();
 | 
			
		||||
    memory_manager.reset();
 | 
			
		||||
    resource_manager.reset();
 | 
			
		||||
    device.reset();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::optional<vk::DebugUtilsMessengerEXT> RendererVulkan::CreateDebugCallback(
 | 
			
		||||
    const vk::DispatchLoaderDynamic& dldi) {
 | 
			
		||||
    const vk::DebugUtilsMessengerCreateInfoEXT callback_ci(
 | 
			
		||||
        {},
 | 
			
		||||
        vk::DebugUtilsMessageSeverityFlagBitsEXT::eError |
 | 
			
		||||
            vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning |
 | 
			
		||||
            vk::DebugUtilsMessageSeverityFlagBitsEXT::eInfo |
 | 
			
		||||
            vk::DebugUtilsMessageSeverityFlagBitsEXT::eVerbose,
 | 
			
		||||
        vk::DebugUtilsMessageTypeFlagBitsEXT::eGeneral |
 | 
			
		||||
            vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation |
 | 
			
		||||
            vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
 | 
			
		||||
        &DebugCallback, nullptr);
 | 
			
		||||
    vk::DebugUtilsMessengerEXT callback;
 | 
			
		||||
    if (instance.createDebugUtilsMessengerEXT(&callback_ci, nullptr, &callback, dldi) !=
 | 
			
		||||
        vk::Result::eSuccess) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Failed to create debug callback");
 | 
			
		||||
        return {};
 | 
			
		||||
    }
 | 
			
		||||
    return callback;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool RendererVulkan::PickDevices(const vk::DispatchLoaderDynamic& dldi) {
 | 
			
		||||
    const auto devices = instance.enumeratePhysicalDevices(dldi);
 | 
			
		||||
 | 
			
		||||
    // TODO(Rodrigo): Choose device from config file
 | 
			
		||||
    const s32 device_index = Settings::values.vulkan_device;
 | 
			
		||||
    if (device_index < 0 || device_index >= static_cast<s32>(devices.size())) {
 | 
			
		||||
        LOG_ERROR(Render_Vulkan, "Invalid device index {}!", device_index);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    const vk::PhysicalDevice physical_device = devices[device_index];
 | 
			
		||||
 | 
			
		||||
    if (!VKDevice::IsSuitable(dldi, physical_device, surface)) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    device = std::make_unique<VKDevice>(dldi, physical_device, surface);
 | 
			
		||||
    return device->Create(dldi, instance);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RendererVulkan::Report() const {
 | 
			
		||||
    const std::string vendor_name{device->GetVendorName()};
 | 
			
		||||
    const std::string model_name{device->GetModelName()};
 | 
			
		||||
    const std::string driver_version = GetDriverVersion(*device);
 | 
			
		||||
    const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version);
 | 
			
		||||
 | 
			
		||||
    const std::string api_version = GetReadableVersion(device->GetApiVersion());
 | 
			
		||||
 | 
			
		||||
    const std::string extensions = BuildCommaSeparatedExtensions(device->GetAvailableExtensions());
 | 
			
		||||
 | 
			
		||||
    LOG_INFO(Render_Vulkan, "Driver: {}", driver_name);
 | 
			
		||||
    LOG_INFO(Render_Vulkan, "Device: {}", model_name);
 | 
			
		||||
    LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
 | 
			
		||||
 | 
			
		||||
    auto& telemetry_session = system.TelemetrySession();
 | 
			
		||||
    constexpr auto field = Telemetry::FieldType::UserSystem;
 | 
			
		||||
    telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
 | 
			
		||||
    telemetry_session.AddField(field, "GPU_Model", model_name);
 | 
			
		||||
    telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
 | 
			
		||||
    telemetry_session.AddField(field, "GPU_Vulkan_Version", api_version);
 | 
			
		||||
    telemetry_session.AddField(field, "GPU_Vulkan_Extensions", extensions);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Vulkan
 | 
			
		||||
@ -3,19 +3,32 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "video_core/gpu_asynch.h"
 | 
			
		||||
#include "video_core/gpu_synch.h"
 | 
			
		||||
#include "video_core/renderer_base.h"
 | 
			
		||||
#include "video_core/renderer_opengl/renderer_opengl.h"
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
#include "video_core/renderer_vulkan/renderer_vulkan.h"
 | 
			
		||||
#endif
 | 
			
		||||
#include "video_core/video_core.h"
 | 
			
		||||
 | 
			
		||||
namespace VideoCore {
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<RendererBase> CreateRenderer(Core::Frontend::EmuWindow& emu_window,
 | 
			
		||||
                                             Core::System& system) {
 | 
			
		||||
    return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
 | 
			
		||||
    switch (Settings::values.renderer_backend) {
 | 
			
		||||
    case Settings::RendererBackend::OpenGL:
 | 
			
		||||
        return std::make_unique<OpenGL::RendererOpenGL>(emu_window, system);
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
    case Settings::RendererBackend::Vulkan:
 | 
			
		||||
        return std::make_unique<Vulkan::RendererVulkan>(emu_window, system);
 | 
			
		||||
#endif
 | 
			
		||||
    default:
 | 
			
		||||
        return nullptr;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Tegra::GPU> CreateGPU(Core::System& system) {
 | 
			
		||||
 | 
			
		||||
@ -200,3 +200,8 @@ if (MSVC)
 | 
			
		||||
    copy_yuzu_SDL_deps(yuzu)
 | 
			
		||||
    copy_yuzu_unicorn_deps(yuzu)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
if (ENABLE_VULKAN)
 | 
			
		||||
    target_include_directories(yuzu PRIVATE ../../externals/Vulkan-Headers/include)
 | 
			
		||||
    target_compile_definitions(yuzu PRIVATE HAS_VULKAN)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
@ -2,19 +2,30 @@
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <glad/glad.h>
 | 
			
		||||
 | 
			
		||||
#include <QApplication>
 | 
			
		||||
#include <QHBoxLayout>
 | 
			
		||||
#include <QKeyEvent>
 | 
			
		||||
#include <QMessageBox>
 | 
			
		||||
#include <QOffscreenSurface>
 | 
			
		||||
#include <QOpenGLWindow>
 | 
			
		||||
#include <QPainter>
 | 
			
		||||
#include <QScreen>
 | 
			
		||||
#include <QStringList>
 | 
			
		||||
#include <QWindow>
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
#include <QVulkanWindow>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/microprofile.h"
 | 
			
		||||
#include "common/scm_rev.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/frontend/framebuffer_layout.h"
 | 
			
		||||
#include "core/frontend/scope_acquire_window_context.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "input_common/keyboard.h"
 | 
			
		||||
#include "input_common/main.h"
 | 
			
		||||
@ -114,19 +125,10 @@ private:
 | 
			
		||||
    QOpenGLContext context;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
 | 
			
		||||
// context.
 | 
			
		||||
// The corresponding functionality is handled in EmuThread instead
 | 
			
		||||
class GGLWidgetInternal : public QOpenGLWindow {
 | 
			
		||||
class GWidgetInternal : public QWindow {
 | 
			
		||||
public:
 | 
			
		||||
    GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
 | 
			
		||||
        : QOpenGLWindow(shared_context), parent(parent) {}
 | 
			
		||||
 | 
			
		||||
    void paintEvent(QPaintEvent* ev) override {
 | 
			
		||||
        if (do_painting) {
 | 
			
		||||
            QPainter painter(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    GWidgetInternal(GRenderWindow* parent) : parent(parent) {}
 | 
			
		||||
    virtual ~GWidgetInternal() = default;
 | 
			
		||||
 | 
			
		||||
    void resizeEvent(QResizeEvent* ev) override {
 | 
			
		||||
        parent->OnClientAreaResized(ev->size().width(), ev->size().height());
 | 
			
		||||
@ -182,9 +184,43 @@ public:
 | 
			
		||||
        do_painting = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::pair<unsigned, unsigned> GetSize() const {
 | 
			
		||||
        return std::make_pair(width(), height());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    bool IsPaintingEnabled() const {
 | 
			
		||||
        return do_painting;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    GRenderWindow* parent;
 | 
			
		||||
    bool do_painting;
 | 
			
		||||
    bool do_painting = false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// This class overrides paintEvent and resizeEvent to prevent the GUI thread from stealing GL
 | 
			
		||||
// context.
 | 
			
		||||
// The corresponding functionality is handled in EmuThread instead
 | 
			
		||||
class GGLWidgetInternal final : public GWidgetInternal, public QOpenGLWindow {
 | 
			
		||||
public:
 | 
			
		||||
    GGLWidgetInternal(GRenderWindow* parent, QOpenGLContext* shared_context)
 | 
			
		||||
        : GWidgetInternal(parent), QOpenGLWindow(shared_context) {}
 | 
			
		||||
    ~GGLWidgetInternal() override = default;
 | 
			
		||||
 | 
			
		||||
    void paintEvent(QPaintEvent* ev) override {
 | 
			
		||||
        if (IsPaintingEnabled()) {
 | 
			
		||||
            QPainter painter(this);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class GVKWidgetInternal final : public GWidgetInternal {
 | 
			
		||||
public:
 | 
			
		||||
    GVKWidgetInternal(GRenderWindow* parent, QVulkanInstance* instance) : GWidgetInternal(parent) {
 | 
			
		||||
        setSurfaceType(QSurface::SurfaceType::VulkanSurface);
 | 
			
		||||
        setVulkanInstance(instance);
 | 
			
		||||
    }
 | 
			
		||||
    ~GVKWidgetInternal() override = default;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
 | 
			
		||||
@ -201,9 +237,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread)
 | 
			
		||||
 | 
			
		||||
GRenderWindow::~GRenderWindow() {
 | 
			
		||||
    InputCommon::Shutdown();
 | 
			
		||||
 | 
			
		||||
    // Avoid an unordered destruction that generates a segfault
 | 
			
		||||
    delete child;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::moveContext() {
 | 
			
		||||
    if (!context) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    DoneCurrent();
 | 
			
		||||
 | 
			
		||||
    // If the thread started running, move the GL Context to the new thread. Otherwise, move it
 | 
			
		||||
@ -215,8 +257,9 @@ void GRenderWindow::moveContext() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::SwapBuffers() {
 | 
			
		||||
    context->swapBuffers(child);
 | 
			
		||||
 | 
			
		||||
    if (context) {
 | 
			
		||||
        context->swapBuffers(child);
 | 
			
		||||
    }
 | 
			
		||||
    if (!first_frame) {
 | 
			
		||||
        first_frame = true;
 | 
			
		||||
        emit FirstFrameDisplayed();
 | 
			
		||||
@ -224,15 +267,38 @@ void GRenderWindow::SwapBuffers() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::MakeCurrent() {
 | 
			
		||||
    context->makeCurrent(child);
 | 
			
		||||
    if (context) {
 | 
			
		||||
        context->makeCurrent(child);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::DoneCurrent() {
 | 
			
		||||
    context->doneCurrent();
 | 
			
		||||
    if (context) {
 | 
			
		||||
        context->doneCurrent();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::PollEvents() {}
 | 
			
		||||
 | 
			
		||||
bool GRenderWindow::IsShown() const {
 | 
			
		||||
    return !isMinimized();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
 | 
			
		||||
                                           void* surface) const {
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
    const auto instance_proc_addr = vk_instance->getInstanceProcAddr("vkGetInstanceProcAddr");
 | 
			
		||||
    const VkInstance instance_copy = vk_instance->vkInstance();
 | 
			
		||||
    const VkSurfaceKHR surface_copy = vk_instance->surfaceForWindow(child);
 | 
			
		||||
 | 
			
		||||
    std::memcpy(get_instance_proc_addr, &instance_proc_addr, sizeof(instance_proc_addr));
 | 
			
		||||
    std::memcpy(instance, &instance_copy, sizeof(instance_copy));
 | 
			
		||||
    std::memcpy(surface, &surface_copy, sizeof(surface_copy));
 | 
			
		||||
#else
 | 
			
		||||
    UNREACHABLE_MSG("Executing Vulkan code without compiling Vulkan");
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// On Qt 5.0+, this correctly gets the size of the framebuffer (pixels).
 | 
			
		||||
//
 | 
			
		||||
// Older versions get the window size (density independent pixels),
 | 
			
		||||
@ -241,10 +307,9 @@ void GRenderWindow::PollEvents() {}
 | 
			
		||||
void GRenderWindow::OnFramebufferSizeChanged() {
 | 
			
		||||
    // Screen changes potentially incur a change in screen DPI, hence we should update the
 | 
			
		||||
    // framebuffer size
 | 
			
		||||
    const qreal pixel_ratio = GetWindowPixelRatio();
 | 
			
		||||
    const u32 width = child->QPaintDevice::width() * pixel_ratio;
 | 
			
		||||
    const u32 height = child->QPaintDevice::height() * pixel_ratio;
 | 
			
		||||
    UpdateCurrentFramebufferLayout(width, height);
 | 
			
		||||
    const qreal pixelRatio{GetWindowPixelRatio()};
 | 
			
		||||
    const auto size{child->GetSize()};
 | 
			
		||||
    UpdateCurrentFramebufferLayout(size.first * pixelRatio, size.second * pixelRatio);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::ForwardKeyPressEvent(QKeyEvent* event) {
 | 
			
		||||
@ -290,7 +355,7 @@ qreal GRenderWindow::GetWindowPixelRatio() const {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<u32, u32> GRenderWindow::ScaleTouch(const QPointF pos) const {
 | 
			
		||||
    const qreal pixel_ratio = GetWindowPixelRatio();
 | 
			
		||||
    const qreal pixel_ratio{GetWindowPixelRatio()};
 | 
			
		||||
    return {static_cast<u32>(std::max(std::round(pos.x() * pixel_ratio), qreal{0.0})),
 | 
			
		||||
            static_cast<u32>(std::max(std::round(pos.y() * pixel_ratio), qreal{0.0}))};
 | 
			
		||||
}
 | 
			
		||||
@ -356,50 +421,46 @@ std::unique_ptr<Core::Frontend::GraphicsContext> GRenderWindow::CreateSharedCont
 | 
			
		||||
    return std::make_unique<GGLContext>(context.get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::InitRenderTarget() {
 | 
			
		||||
bool GRenderWindow::InitRenderTarget() {
 | 
			
		||||
    shared_context.reset();
 | 
			
		||||
    context.reset();
 | 
			
		||||
 | 
			
		||||
    delete child;
 | 
			
		||||
    child = nullptr;
 | 
			
		||||
 | 
			
		||||
    delete container;
 | 
			
		||||
    container = nullptr;
 | 
			
		||||
 | 
			
		||||
    delete layout();
 | 
			
		||||
    if (child) {
 | 
			
		||||
        delete child;
 | 
			
		||||
    }
 | 
			
		||||
    if (container) {
 | 
			
		||||
        delete container;
 | 
			
		||||
    }
 | 
			
		||||
    if (layout()) {
 | 
			
		||||
        delete layout();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    first_frame = false;
 | 
			
		||||
 | 
			
		||||
    // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
 | 
			
		||||
    // WA_DontShowOnScreen, WA_DeleteOnClose
 | 
			
		||||
    QSurfaceFormat fmt;
 | 
			
		||||
    fmt.setVersion(4, 3);
 | 
			
		||||
    fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
 | 
			
		||||
    fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
 | 
			
		||||
    // TODO: expose a setting for buffer value (ie default/single/double/triple)
 | 
			
		||||
    fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
 | 
			
		||||
    shared_context = std::make_unique<QOpenGLContext>();
 | 
			
		||||
    shared_context->setFormat(fmt);
 | 
			
		||||
    shared_context->create();
 | 
			
		||||
    context = std::make_unique<QOpenGLContext>();
 | 
			
		||||
    context->setShareContext(shared_context.get());
 | 
			
		||||
    context->setFormat(fmt);
 | 
			
		||||
    context->create();
 | 
			
		||||
    fmt.setSwapInterval(0);
 | 
			
		||||
    switch (Settings::values.renderer_backend) {
 | 
			
		||||
    case Settings::RendererBackend::OpenGL:
 | 
			
		||||
        if (!InitializeOpenGL()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    case Settings::RendererBackend::Vulkan:
 | 
			
		||||
        if (!InitializeVulkan()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    child = new GGLWidgetInternal(this, shared_context.get());
 | 
			
		||||
    container = QWidget::createWindowContainer(child, this);
 | 
			
		||||
 | 
			
		||||
    QBoxLayout* layout = new QHBoxLayout(this);
 | 
			
		||||
 | 
			
		||||
    layout->addWidget(container);
 | 
			
		||||
    layout->setMargin(0);
 | 
			
		||||
    setLayout(layout);
 | 
			
		||||
 | 
			
		||||
    // Reset minimum size to avoid unwanted resizes when this function is called for a second time.
 | 
			
		||||
    // Reset minimum required size to avoid resizing issues on the main window after restarting.
 | 
			
		||||
    setMinimumSize(1, 1);
 | 
			
		||||
 | 
			
		||||
    // Show causes the window to actually be created and the OpenGL context as well, but we don't
 | 
			
		||||
    // want the widget to be shown yet, so immediately hide it.
 | 
			
		||||
    // Show causes the window to actually be created and the gl context as well, but we don't want
 | 
			
		||||
    // the widget to be shown yet, so immediately hide it.
 | 
			
		||||
    show();
 | 
			
		||||
    hide();
 | 
			
		||||
 | 
			
		||||
@ -410,9 +471,17 @@ void GRenderWindow::InitRenderTarget() {
 | 
			
		||||
    OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
 | 
			
		||||
 | 
			
		||||
    OnFramebufferSizeChanged();
 | 
			
		||||
    NotifyClientAreaSizeChanged(std::pair<unsigned, unsigned>(child->width(), child->height()));
 | 
			
		||||
    NotifyClientAreaSizeChanged(child->GetSize());
 | 
			
		||||
 | 
			
		||||
    BackupGeometry();
 | 
			
		||||
 | 
			
		||||
    if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
 | 
			
		||||
        if (!LoadOpenGL()) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::CaptureScreenshot(u32 res_scale, const QString& screenshot_path) {
 | 
			
		||||
@ -441,6 +510,113 @@ void GRenderWindow::OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal
 | 
			
		||||
    setMinimumSize(minimal_size.first, minimal_size.second);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GRenderWindow::InitializeOpenGL() {
 | 
			
		||||
    // TODO: One of these flags might be interesting: WA_OpaquePaintEvent, WA_NoBackground,
 | 
			
		||||
    // WA_DontShowOnScreen, WA_DeleteOnClose
 | 
			
		||||
    QSurfaceFormat fmt;
 | 
			
		||||
    fmt.setVersion(4, 3);
 | 
			
		||||
    fmt.setProfile(QSurfaceFormat::CompatibilityProfile);
 | 
			
		||||
    fmt.setOption(QSurfaceFormat::FormatOption::DeprecatedFunctions);
 | 
			
		||||
    // TODO: expose a setting for buffer value (ie default/single/double/triple)
 | 
			
		||||
    fmt.setSwapBehavior(QSurfaceFormat::DefaultSwapBehavior);
 | 
			
		||||
    shared_context = std::make_unique<QOpenGLContext>();
 | 
			
		||||
    shared_context->setFormat(fmt);
 | 
			
		||||
    shared_context->create();
 | 
			
		||||
    context = std::make_unique<QOpenGLContext>();
 | 
			
		||||
    context->setShareContext(shared_context.get());
 | 
			
		||||
    context->setFormat(fmt);
 | 
			
		||||
    context->create();
 | 
			
		||||
    fmt.setSwapInterval(false);
 | 
			
		||||
 | 
			
		||||
    child = new GGLWidgetInternal(this, shared_context.get());
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GRenderWindow::InitializeVulkan() {
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
    vk_instance = std::make_unique<QVulkanInstance>();
 | 
			
		||||
    vk_instance->setApiVersion(QVersionNumber(1, 1, 0));
 | 
			
		||||
    vk_instance->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
 | 
			
		||||
    if (Settings::values.renderer_debug) {
 | 
			
		||||
        const auto supported_layers{vk_instance->supportedLayers()};
 | 
			
		||||
        const bool found =
 | 
			
		||||
            std::find_if(supported_layers.begin(), supported_layers.end(), [](const auto& layer) {
 | 
			
		||||
                constexpr const char searched_layer[] = "VK_LAYER_LUNARG_standard_validation";
 | 
			
		||||
                return layer.name == searched_layer;
 | 
			
		||||
            });
 | 
			
		||||
        if (found) {
 | 
			
		||||
            vk_instance->setLayers(QByteArrayList() << "VK_LAYER_LUNARG_standard_validation");
 | 
			
		||||
            vk_instance->setExtensions(QByteArrayList() << VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    if (!vk_instance->create()) {
 | 
			
		||||
        QMessageBox::critical(
 | 
			
		||||
            this, tr("Error while initializing Vulkan 1.1!"),
 | 
			
		||||
            tr("Your OS doesn't seem to support Vulkan 1.1 instances, or you do not have the "
 | 
			
		||||
               "latest graphics drivers."));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    child = new GVKWidgetInternal(this, vk_instance.get());
 | 
			
		||||
    return true;
 | 
			
		||||
#else
 | 
			
		||||
    QMessageBox::critical(this, tr("Vulkan not available!"),
 | 
			
		||||
                          tr("yuzu has not been compiled with Vulkan support."));
 | 
			
		||||
    return false;
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GRenderWindow::LoadOpenGL() {
 | 
			
		||||
    Core::Frontend::ScopeAcquireWindowContext acquire_context{*this};
 | 
			
		||||
    if (!gladLoadGL()) {
 | 
			
		||||
        QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
 | 
			
		||||
                              tr("Your GPU may not support OpenGL 4.3, or you do not have the "
 | 
			
		||||
                                 "latest graphics driver."));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
 | 
			
		||||
    if (!unsupported_gl_extensions.empty()) {
 | 
			
		||||
        QMessageBox::critical(
 | 
			
		||||
            this, tr("Error while initializing OpenGL!"),
 | 
			
		||||
            tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
 | 
			
		||||
               "have the latest graphics driver.<br><br>Unsupported extensions:<br>") +
 | 
			
		||||
                unsupported_gl_extensions.join(QStringLiteral("<br>")));
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
 | 
			
		||||
    QStringList unsupported_ext;
 | 
			
		||||
 | 
			
		||||
    if (!GLAD_GL_ARB_buffer_storage)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_buffer_storage"));
 | 
			
		||||
    if (!GLAD_GL_ARB_direct_state_access)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_direct_state_access"));
 | 
			
		||||
    if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev"));
 | 
			
		||||
    if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge"));
 | 
			
		||||
    if (!GLAD_GL_ARB_multi_bind)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
 | 
			
		||||
    if (!GLAD_GL_ARB_clip_control)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_clip_control"));
 | 
			
		||||
 | 
			
		||||
    // Extensions required to support some texture formats.
 | 
			
		||||
    if (!GLAD_GL_EXT_texture_compression_s3tc)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
 | 
			
		||||
    if (!GLAD_GL_ARB_texture_compression_rgtc)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
 | 
			
		||||
    if (!GLAD_GL_ARB_depth_buffer_float)
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
 | 
			
		||||
 | 
			
		||||
    for (const QString& ext : unsupported_ext)
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
 | 
			
		||||
 | 
			
		||||
    return unsupported_ext;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GRenderWindow::OnEmulationStarting(EmuThread* emu_thread) {
 | 
			
		||||
    this->emu_thread = emu_thread;
 | 
			
		||||
    child->DisablePainting();
 | 
			
		||||
 | 
			
		||||
@ -7,17 +7,28 @@
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <condition_variable>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
 | 
			
		||||
#include <QImage>
 | 
			
		||||
#include <QThread>
 | 
			
		||||
#include <QWidget>
 | 
			
		||||
 | 
			
		||||
#include "common/thread.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/frontend/emu_window.h"
 | 
			
		||||
 | 
			
		||||
class QKeyEvent;
 | 
			
		||||
class QScreen;
 | 
			
		||||
class QTouchEvent;
 | 
			
		||||
class QStringList;
 | 
			
		||||
class QSurface;
 | 
			
		||||
class QOpenGLContext;
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
class QVulkanInstance;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
class GWidgetInternal;
 | 
			
		||||
class GGLWidgetInternal;
 | 
			
		||||
class GVKWidgetInternal;
 | 
			
		||||
class GMainWindow;
 | 
			
		||||
class GRenderWindow;
 | 
			
		||||
class QSurface;
 | 
			
		||||
@ -123,6 +134,9 @@ public:
 | 
			
		||||
    void MakeCurrent() override;
 | 
			
		||||
    void DoneCurrent() override;
 | 
			
		||||
    void PollEvents() override;
 | 
			
		||||
    bool IsShown() const override;
 | 
			
		||||
    void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
 | 
			
		||||
                                void* surface) const override;
 | 
			
		||||
    std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
 | 
			
		||||
 | 
			
		||||
    void ForwardKeyPressEvent(QKeyEvent* event);
 | 
			
		||||
@ -142,7 +156,7 @@ public:
 | 
			
		||||
 | 
			
		||||
    void OnClientAreaResized(u32 width, u32 height);
 | 
			
		||||
 | 
			
		||||
    void InitRenderTarget();
 | 
			
		||||
    bool InitRenderTarget();
 | 
			
		||||
 | 
			
		||||
    void CaptureScreenshot(u32 res_scale, const QString& screenshot_path);
 | 
			
		||||
 | 
			
		||||
@ -165,10 +179,13 @@ private:
 | 
			
		||||
 | 
			
		||||
    void OnMinimalClientAreaChangeRequest(std::pair<u32, u32> minimal_size) override;
 | 
			
		||||
 | 
			
		||||
    QWidget* container = nullptr;
 | 
			
		||||
    GGLWidgetInternal* child = nullptr;
 | 
			
		||||
    bool InitializeOpenGL();
 | 
			
		||||
    bool InitializeVulkan();
 | 
			
		||||
    bool LoadOpenGL();
 | 
			
		||||
    QStringList GetUnsupportedGLExtensions() const;
 | 
			
		||||
 | 
			
		||||
    QByteArray geometry;
 | 
			
		||||
    QWidget* container = nullptr;
 | 
			
		||||
    GWidgetInternal* child = nullptr;
 | 
			
		||||
 | 
			
		||||
    EmuThread* emu_thread;
 | 
			
		||||
    // Context that backs the GGLWidgetInternal (and will be used by core to render)
 | 
			
		||||
@ -177,9 +194,14 @@ private:
 | 
			
		||||
    // current
 | 
			
		||||
    std::unique_ptr<QOpenGLContext> shared_context;
 | 
			
		||||
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
    std::unique_ptr<QVulkanInstance> vk_instance;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    /// Temporary storage of the screenshot taken
 | 
			
		||||
    QImage screenshot_image;
 | 
			
		||||
 | 
			
		||||
    QByteArray geometry;
 | 
			
		||||
    bool first_frame = false;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,8 @@ void ConfigureDebug::SetConfiguration() {
 | 
			
		||||
    ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
 | 
			
		||||
    ui->reporting_services->setChecked(Settings::values.reporting_services);
 | 
			
		||||
    ui->quest_flag->setChecked(Settings::values.quest_flag);
 | 
			
		||||
    ui->enable_graphics_debugging->setEnabled(!Core::System::GetInstance().IsPoweredOn());
 | 
			
		||||
    ui->enable_graphics_debugging->setChecked(Settings::values.renderer_debug);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureDebug::ApplyConfiguration() {
 | 
			
		||||
@ -46,6 +48,7 @@ void ConfigureDebug::ApplyConfiguration() {
 | 
			
		||||
    Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
 | 
			
		||||
    Settings::values.reporting_services = ui->reporting_services->isChecked();
 | 
			
		||||
    Settings::values.quest_flag = ui->quest_flag->isChecked();
 | 
			
		||||
    Settings::values.renderer_debug = ui->enable_graphics_debugging->isChecked();
 | 
			
		||||
    Debugger::ToggleConsole();
 | 
			
		||||
    Log::Filter filter;
 | 
			
		||||
    filter.ParseFilterString(Settings::values.log_filter);
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@
 | 
			
		||||
    <x>0</x>
 | 
			
		||||
    <y>0</y>
 | 
			
		||||
    <width>400</width>
 | 
			
		||||
    <height>474</height>
 | 
			
		||||
    <height>467</height>
 | 
			
		||||
   </rect>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="windowTitle">
 | 
			
		||||
@ -103,44 +103,6 @@
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="reporting_services">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Enable Verbose Reporting Services</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="label">
 | 
			
		||||
        <property name="font">
 | 
			
		||||
         <font>
 | 
			
		||||
          <italic>true</italic>
 | 
			
		||||
         </font>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>This will be reset automatically when yuzu closes.</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="indent">
 | 
			
		||||
         <number>20</number>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <widget class="QGroupBox" name="groupBox_5">
 | 
			
		||||
     <property name="title">
 | 
			
		||||
      <string>Advanced</string>
 | 
			
		||||
     </property>
 | 
			
		||||
     <layout class="QVBoxLayout" name="verticalLayout">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="quest_flag">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Kiosk (Quest) Mode</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
@ -167,6 +129,95 @@
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <widget class="QGroupBox" name="groupBox_4">
 | 
			
		||||
     <property name="title">
 | 
			
		||||
      <string>Graphics</string>
 | 
			
		||||
     </property>
 | 
			
		||||
     <layout class="QVBoxLayout" name="verticalLayout_6">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="enable_graphics_debugging">
 | 
			
		||||
        <property name="enabled">
 | 
			
		||||
         <bool>true</bool>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="whatsThis">
 | 
			
		||||
         <string>When checked, the graphics API enters in a slower debugging mode</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Enable Graphics Debugging</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <widget class="QGroupBox" name="groupBox_5">
 | 
			
		||||
     <property name="title">
 | 
			
		||||
      <string>Dump</string>
 | 
			
		||||
     </property>
 | 
			
		||||
     <layout class="QVBoxLayout" name="verticalLayout_6">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="dump_decompressed_nso">
 | 
			
		||||
        <property name="whatsThis">
 | 
			
		||||
         <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Dump Decompressed NSOs</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="dump_exefs">
 | 
			
		||||
        <property name="whatsThis">
 | 
			
		||||
         <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Dump ExeFS</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="reporting_services">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Enable Verbose Reporting Services</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QLabel" name="label">
 | 
			
		||||
        <property name="font">
 | 
			
		||||
         <font>
 | 
			
		||||
          <italic>true</italic>
 | 
			
		||||
         </font>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>This will be reset automatically when yuzu closes.</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <property name="indent">
 | 
			
		||||
         <number>20</number>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <widget class="QGroupBox" name="groupBox_6">
 | 
			
		||||
     <property name="title">
 | 
			
		||||
      <string>Advanced</string>
 | 
			
		||||
     </property>
 | 
			
		||||
     <layout class="QVBoxLayout" name="verticalLayout_7">
 | 
			
		||||
      <item>
 | 
			
		||||
       <widget class="QCheckBox" name="quest_flag">
 | 
			
		||||
        <property name="text">
 | 
			
		||||
         <string>Kiosk (Quest) Mode</string>
 | 
			
		||||
        </property>
 | 
			
		||||
       </widget>
 | 
			
		||||
      </item>
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <spacer name="verticalSpacer">
 | 
			
		||||
     <property name="orientation">
 | 
			
		||||
@ -185,6 +236,19 @@
 | 
			
		||||
   </item>
 | 
			
		||||
  </layout>
 | 
			
		||||
 </widget>
 | 
			
		||||
 <tabstops>
 | 
			
		||||
  <tabstop>toggle_gdbstub</tabstop>
 | 
			
		||||
  <tabstop>gdbport_spinbox</tabstop>
 | 
			
		||||
  <tabstop>log_filter_edit</tabstop>
 | 
			
		||||
  <tabstop>toggle_console</tabstop>
 | 
			
		||||
  <tabstop>open_log_button</tabstop>
 | 
			
		||||
  <tabstop>homebrew_args_edit</tabstop>
 | 
			
		||||
  <tabstop>enable_graphics_debugging</tabstop>
 | 
			
		||||
  <tabstop>dump_decompressed_nso</tabstop>
 | 
			
		||||
  <tabstop>dump_exefs</tabstop>
 | 
			
		||||
  <tabstop>reporting_services</tabstop>
 | 
			
		||||
  <tabstop>quest_flag</tabstop>
 | 
			
		||||
 </tabstops>
 | 
			
		||||
 <resources/>
 | 
			
		||||
 <connections>
 | 
			
		||||
  <connection>
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,13 @@
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <QColorDialog>
 | 
			
		||||
#include <QComboBox>
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
#include <QVulkanInstance>
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "ui_configure_graphics.h"
 | 
			
		||||
@ -51,10 +58,18 @@ Resolution FromResolutionFactor(float factor) {
 | 
			
		||||
 | 
			
		||||
ConfigureGraphics::ConfigureGraphics(QWidget* parent)
 | 
			
		||||
    : QWidget(parent), ui(new Ui::ConfigureGraphics) {
 | 
			
		||||
    vulkan_device = Settings::values.vulkan_device;
 | 
			
		||||
    RetrieveVulkanDevices();
 | 
			
		||||
 | 
			
		||||
    ui->setupUi(this);
 | 
			
		||||
 | 
			
		||||
    SetConfiguration();
 | 
			
		||||
 | 
			
		||||
    connect(ui->api, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
 | 
			
		||||
            [this] { UpdateDeviceComboBox(); });
 | 
			
		||||
    connect(ui->device, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this,
 | 
			
		||||
            [this](int device) { UpdateDeviceSelection(device); });
 | 
			
		||||
 | 
			
		||||
    connect(ui->bg_button, &QPushButton::clicked, this, [this] {
 | 
			
		||||
        const QColor new_bg_color = QColorDialog::getColor(bg_color);
 | 
			
		||||
        if (!new_bg_color.isValid()) {
 | 
			
		||||
@ -64,11 +79,22 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureGraphics::UpdateDeviceSelection(int device) {
 | 
			
		||||
    if (device == -1) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    if (GetCurrentGraphicsBackend() == Settings::RendererBackend::Vulkan) {
 | 
			
		||||
        vulkan_device = device;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ConfigureGraphics::~ConfigureGraphics() = default;
 | 
			
		||||
 | 
			
		||||
void ConfigureGraphics::SetConfiguration() {
 | 
			
		||||
    const bool runtime_lock = !Core::System::GetInstance().IsPoweredOn();
 | 
			
		||||
 | 
			
		||||
    ui->api->setEnabled(runtime_lock);
 | 
			
		||||
    ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend));
 | 
			
		||||
    ui->resolution_factor_combobox->setCurrentIndex(
 | 
			
		||||
        static_cast<int>(FromResolutionFactor(Settings::values.resolution_factor)));
 | 
			
		||||
    ui->use_disk_shader_cache->setEnabled(runtime_lock);
 | 
			
		||||
@ -80,9 +106,12 @@ void ConfigureGraphics::SetConfiguration() {
 | 
			
		||||
    ui->force_30fps_mode->setChecked(Settings::values.force_30fps_mode);
 | 
			
		||||
    UpdateBackgroundColorButton(QColor::fromRgbF(Settings::values.bg_red, Settings::values.bg_green,
 | 
			
		||||
                                                 Settings::values.bg_blue));
 | 
			
		||||
    UpdateDeviceComboBox();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureGraphics::ApplyConfiguration() {
 | 
			
		||||
    Settings::values.renderer_backend = GetCurrentGraphicsBackend();
 | 
			
		||||
    Settings::values.vulkan_device = vulkan_device;
 | 
			
		||||
    Settings::values.resolution_factor =
 | 
			
		||||
        ToResolutionFactor(static_cast<Resolution>(ui->resolution_factor_combobox->currentIndex()));
 | 
			
		||||
    Settings::values.use_disk_shader_cache = ui->use_disk_shader_cache->isChecked();
 | 
			
		||||
@ -116,3 +145,68 @@ void ConfigureGraphics::UpdateBackgroundColorButton(QColor color) {
 | 
			
		||||
    const QIcon color_icon(pixmap);
 | 
			
		||||
    ui->bg_button->setIcon(color_icon);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureGraphics::UpdateDeviceComboBox() {
 | 
			
		||||
    ui->device->clear();
 | 
			
		||||
 | 
			
		||||
    bool enabled = false;
 | 
			
		||||
    switch (GetCurrentGraphicsBackend()) {
 | 
			
		||||
    case Settings::RendererBackend::OpenGL:
 | 
			
		||||
        ui->device->addItem(tr("OpenGL Graphics Device"));
 | 
			
		||||
        enabled = false;
 | 
			
		||||
        break;
 | 
			
		||||
    case Settings::RendererBackend::Vulkan:
 | 
			
		||||
        for (const auto device : vulkan_devices) {
 | 
			
		||||
            ui->device->addItem(device);
 | 
			
		||||
        }
 | 
			
		||||
        ui->device->setCurrentIndex(vulkan_device);
 | 
			
		||||
        enabled = !vulkan_devices.empty();
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ConfigureGraphics::RetrieveVulkanDevices() {
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
    QVulkanInstance instance;
 | 
			
		||||
    instance.setApiVersion(QVersionNumber(1, 1, 0));
 | 
			
		||||
    if (!instance.create()) {
 | 
			
		||||
        LOG_INFO(Frontend, "Vulkan 1.1 not available");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    const auto vkEnumeratePhysicalDevices{reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(
 | 
			
		||||
        instance.getInstanceProcAddr("vkEnumeratePhysicalDevices"))};
 | 
			
		||||
    if (vkEnumeratePhysicalDevices == nullptr) {
 | 
			
		||||
        LOG_INFO(Frontend, "Failed to get pointer to vkEnumeratePhysicalDevices");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    u32 physical_device_count;
 | 
			
		||||
    if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count, nullptr) !=
 | 
			
		||||
        VK_SUCCESS) {
 | 
			
		||||
        LOG_INFO(Frontend, "Failed to get physical devices count");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<VkPhysicalDevice> physical_devices(physical_device_count);
 | 
			
		||||
    if (vkEnumeratePhysicalDevices(instance.vkInstance(), &physical_device_count,
 | 
			
		||||
                                   physical_devices.data()) != VK_SUCCESS) {
 | 
			
		||||
        LOG_INFO(Frontend, "Failed to get physical devices");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto vkGetPhysicalDeviceProperties{reinterpret_cast<PFN_vkGetPhysicalDeviceProperties>(
 | 
			
		||||
        instance.getInstanceProcAddr("vkGetPhysicalDeviceProperties"))};
 | 
			
		||||
    if (vkGetPhysicalDeviceProperties == nullptr) {
 | 
			
		||||
        LOG_INFO(Frontend, "Failed to get pointer to vkGetPhysicalDeviceProperties");
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    for (const auto physical_device : physical_devices) {
 | 
			
		||||
        VkPhysicalDeviceProperties properties;
 | 
			
		||||
        vkGetPhysicalDeviceProperties(physical_device, &properties);
 | 
			
		||||
        vulkan_devices.push_back(QString::fromUtf8(properties.deviceName));
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
 | 
			
		||||
    return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,10 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <QString>
 | 
			
		||||
#include <QWidget>
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
 | 
			
		||||
namespace Ui {
 | 
			
		||||
class ConfigureGraphics;
 | 
			
		||||
@ -27,7 +30,16 @@ private:
 | 
			
		||||
    void SetConfiguration();
 | 
			
		||||
 | 
			
		||||
    void UpdateBackgroundColorButton(QColor color);
 | 
			
		||||
    void UpdateDeviceComboBox();
 | 
			
		||||
    void UpdateDeviceSelection(int device);
 | 
			
		||||
 | 
			
		||||
    void RetrieveVulkanDevices();
 | 
			
		||||
 | 
			
		||||
    Settings::RendererBackend GetCurrentGraphicsBackend() const;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Ui::ConfigureGraphics> ui;
 | 
			
		||||
    QColor bg_color;
 | 
			
		||||
 | 
			
		||||
    std::vector<QString> vulkan_devices;
 | 
			
		||||
    u32 vulkan_device{};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -7,21 +7,69 @@
 | 
			
		||||
    <x>0</x>
 | 
			
		||||
    <y>0</y>
 | 
			
		||||
    <width>400</width>
 | 
			
		||||
    <height>300</height>
 | 
			
		||||
    <height>321</height>
 | 
			
		||||
   </rect>
 | 
			
		||||
  </property>
 | 
			
		||||
  <property name="windowTitle">
 | 
			
		||||
   <string>Form</string>
 | 
			
		||||
  </property>
 | 
			
		||||
  <layout class="QVBoxLayout" name="verticalLayout">
 | 
			
		||||
  <layout class="QVBoxLayout" name="verticalLayout_1">
 | 
			
		||||
   <item>
 | 
			
		||||
    <layout class="QVBoxLayout" name="verticalLayout_3">
 | 
			
		||||
    <layout class="QVBoxLayout" name="verticalLayout_2">
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QGroupBox" name="groupBox_2">
 | 
			
		||||
       <property name="title">
 | 
			
		||||
        <string>API Settings</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <layout class="QVBoxLayout" name="verticalLayout_3">
 | 
			
		||||
        <item>
 | 
			
		||||
         <layout class="QHBoxLayout" name="horizontalLayout_4">
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="label_2">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string>API:</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QComboBox" name="api">
 | 
			
		||||
            <item>
 | 
			
		||||
             <property name="text">
 | 
			
		||||
              <string notr="true">OpenGL</string>
 | 
			
		||||
             </property>
 | 
			
		||||
            </item>
 | 
			
		||||
            <item>
 | 
			
		||||
             <property name="text">
 | 
			
		||||
              <string notr="true">Vulkan</string>
 | 
			
		||||
             </property>
 | 
			
		||||
            </item>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
         </layout>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <layout class="QHBoxLayout" name="horizontalLayout_5">
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="label_3">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string>Device:</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QComboBox" name="device"/>
 | 
			
		||||
          </item>
 | 
			
		||||
         </layout>
 | 
			
		||||
        </item>
 | 
			
		||||
       </layout>
 | 
			
		||||
      </widget>
 | 
			
		||||
     </item>
 | 
			
		||||
     <item>
 | 
			
		||||
      <widget class="QGroupBox" name="groupBox">
 | 
			
		||||
       <property name="title">
 | 
			
		||||
        <string>Graphics</string>
 | 
			
		||||
        <string>Graphics Settings</string>
 | 
			
		||||
       </property>
 | 
			
		||||
       <layout class="QVBoxLayout" name="verticalLayout_2">
 | 
			
		||||
       <layout class="QVBoxLayout" name="verticalLayout_4">
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QCheckBox" name="use_disk_shader_cache">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
@ -29,13 +77,6 @@
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QCheckBox" name="use_accurate_gpu_emulation">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
           <string>Use accurate GPU emulation (slow)</string>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QCheckBox" name="use_asynchronous_gpu_emulation">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
@ -43,6 +84,13 @@
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QCheckBox" name="use_accurate_gpu_emulation">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
           <string>Use accurate GPU emulation (slow)</string>
 | 
			
		||||
          </property>
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <widget class="QCheckBox" name="force_30fps_mode">
 | 
			
		||||
          <property name="text">
 | 
			
		||||
@ -51,11 +99,11 @@
 | 
			
		||||
         </widget>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <layout class="QHBoxLayout" name="horizontalLayout">
 | 
			
		||||
         <layout class="QHBoxLayout" name="horizontalLayout_2">
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="label">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
             <string>Internal Resolution</string>
 | 
			
		||||
             <string>Internal Resolution:</string>
 | 
			
		||||
            </property>
 | 
			
		||||
           </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
@ -91,7 +139,7 @@
 | 
			
		||||
         </layout>
 | 
			
		||||
        </item>
 | 
			
		||||
        <item>
 | 
			
		||||
         <layout class="QHBoxLayout" name="horizontalLayout_6">
 | 
			
		||||
         <layout class="QHBoxLayout" name="horizontalLayout_3">
 | 
			
		||||
          <item>
 | 
			
		||||
           <widget class="QLabel" name="bg_label">
 | 
			
		||||
            <property name="text">
 | 
			
		||||
 | 
			
		||||
@ -806,70 +806,12 @@ void GMainWindow::AllowOSSleep() {
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
QStringList GMainWindow::GetUnsupportedGLExtensions() {
 | 
			
		||||
    QStringList unsupported_ext;
 | 
			
		||||
 | 
			
		||||
    if (!GLAD_GL_ARB_buffer_storage) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_buffer_storage"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!GLAD_GL_ARB_direct_state_access) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_direct_state_access"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!GLAD_GL_ARB_vertex_type_10f_11f_11f_rev) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_vertex_type_10f_11f_11f_rev"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!GLAD_GL_ARB_texture_mirror_clamp_to_edge) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_texture_mirror_clamp_to_edge"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!GLAD_GL_ARB_multi_bind) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_multi_bind"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!GLAD_GL_ARB_clip_control) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_clip_control"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Extensions required to support some texture formats.
 | 
			
		||||
    if (!GLAD_GL_EXT_texture_compression_s3tc) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("EXT_texture_compression_s3tc"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!GLAD_GL_ARB_texture_compression_rgtc) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_texture_compression_rgtc"));
 | 
			
		||||
    }
 | 
			
		||||
    if (!GLAD_GL_ARB_depth_buffer_float) {
 | 
			
		||||
        unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (const QString& ext : unsupported_ext) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return unsupported_ext;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GMainWindow::LoadROM(const QString& filename) {
 | 
			
		||||
    // Shutdown previous session if the emu thread is still active...
 | 
			
		||||
    if (emu_thread != nullptr)
 | 
			
		||||
        ShutdownGame();
 | 
			
		||||
 | 
			
		||||
    render_window->InitRenderTarget();
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        Core::Frontend::ScopeAcquireWindowContext acquire_context{*render_window};
 | 
			
		||||
        if (!gladLoadGL()) {
 | 
			
		||||
            QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3 Core!"),
 | 
			
		||||
                                  tr("Your GPU may not support OpenGL 4.3, or you do not "
 | 
			
		||||
                                     "have the latest graphics driver."));
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
 | 
			
		||||
    if (!unsupported_gl_extensions.empty()) {
 | 
			
		||||
        QMessageBox::critical(this, tr("Error while initializing OpenGL Core!"),
 | 
			
		||||
                              tr("Your GPU may not support one or more required OpenGL"
 | 
			
		||||
                                 "extensions. Please ensure you have the latest graphics "
 | 
			
		||||
                                 "driver.<br><br>Unsupported extensions:<br>") +
 | 
			
		||||
                                  unsupported_gl_extensions.join(QStringLiteral("<br>")));
 | 
			
		||||
    if (!render_window->InitRenderTarget()) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -980,7 +922,9 @@ void GMainWindow::BootGame(const QString& filename) {
 | 
			
		||||
    // Create and start the emulation thread
 | 
			
		||||
    emu_thread = std::make_unique<EmuThread>(render_window);
 | 
			
		||||
    emit EmulationStarting(emu_thread.get());
 | 
			
		||||
    render_window->moveContext();
 | 
			
		||||
    if (Settings::values.renderer_backend == Settings::RendererBackend::OpenGL) {
 | 
			
		||||
        render_window->moveContext();
 | 
			
		||||
    }
 | 
			
		||||
    emu_thread->start();
 | 
			
		||||
 | 
			
		||||
    connect(render_window, &GRenderWindow::Closed, this, &GMainWindow::OnStopGame);
 | 
			
		||||
@ -2195,6 +2139,18 @@ void GMainWindow::closeEvent(QCloseEvent* event) {
 | 
			
		||||
    QWidget::closeEvent(event);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GMainWindow::keyPressEvent(QKeyEvent* event) {
 | 
			
		||||
    if (render_window) {
 | 
			
		||||
        render_window->ForwardKeyPressEvent(event);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
 | 
			
		||||
    if (render_window) {
 | 
			
		||||
        render_window->ForwardKeyReleaseEvent(event);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool IsSingleFileDropEvent(QDropEvent* event) {
 | 
			
		||||
    const QMimeData* mimeData = event->mimeData();
 | 
			
		||||
    return mimeData->hasUrls() && mimeData->urls().length() == 1;
 | 
			
		||||
@ -2227,18 +2183,6 @@ void GMainWindow::dragMoveEvent(QDragMoveEvent* event) {
 | 
			
		||||
    event->acceptProposedAction();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GMainWindow::keyPressEvent(QKeyEvent* event) {
 | 
			
		||||
    if (render_window) {
 | 
			
		||||
        render_window->ForwardKeyPressEvent(event);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void GMainWindow::keyReleaseEvent(QKeyEvent* event) {
 | 
			
		||||
    if (render_window) {
 | 
			
		||||
        render_window->ForwardKeyReleaseEvent(event);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool GMainWindow::ConfirmChangeGame() {
 | 
			
		||||
    if (emu_thread == nullptr)
 | 
			
		||||
        return true;
 | 
			
		||||
 | 
			
		||||
@ -130,7 +130,6 @@ private:
 | 
			
		||||
    void PreventOSSleep();
 | 
			
		||||
    void AllowOSSleep();
 | 
			
		||||
 | 
			
		||||
    QStringList GetUnsupportedGLExtensions();
 | 
			
		||||
    bool LoadROM(const QString& filename);
 | 
			
		||||
    void BootGame(const QString& filename);
 | 
			
		||||
    void ShutdownGame();
 | 
			
		||||
 | 
			
		||||
@ -8,11 +8,22 @@ add_executable(yuzu-cmd
 | 
			
		||||
    emu_window/emu_window_sdl2_gl.h
 | 
			
		||||
    emu_window/emu_window_sdl2.cpp
 | 
			
		||||
    emu_window/emu_window_sdl2.h
 | 
			
		||||
    emu_window/emu_window_sdl2_gl.cpp
 | 
			
		||||
    emu_window/emu_window_sdl2_gl.h
 | 
			
		||||
    resource.h
 | 
			
		||||
    yuzu.cpp
 | 
			
		||||
    yuzu.rc
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
if (ENABLE_VULKAN)
 | 
			
		||||
    target_sources(yuzu-cmd PRIVATE
 | 
			
		||||
                   emu_window/emu_window_sdl2_vk.cpp
 | 
			
		||||
                   emu_window/emu_window_sdl2_vk.h)
 | 
			
		||||
 | 
			
		||||
    target_include_directories(yuzu-cmd PRIVATE ../../externals/Vulkan-Headers/include)
 | 
			
		||||
    target_compile_definitions(yuzu-cmd PRIVATE HAS_VULKAN)
 | 
			
		||||
endif()
 | 
			
		||||
 | 
			
		||||
create_target_directory_groups(yuzu-cmd)
 | 
			
		||||
 | 
			
		||||
target_link_libraries(yuzu-cmd PRIVATE common core input_common)
 | 
			
		||||
 | 
			
		||||
@ -89,6 +89,10 @@ bool EmuWindow_SDL2::IsOpen() const {
 | 
			
		||||
    return is_open;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EmuWindow_SDL2::IsShown() const {
 | 
			
		||||
    return is_shown;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_SDL2::OnResize() {
 | 
			
		||||
    int width, height;
 | 
			
		||||
    SDL_GetWindowSize(render_window, &width, &height);
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,9 @@ public:
 | 
			
		||||
    /// Whether the window is still open, and a close request hasn't yet been sent
 | 
			
		||||
    bool IsOpen() const;
 | 
			
		||||
 | 
			
		||||
    /// Returns if window is shown (not minimized)
 | 
			
		||||
    bool IsShown() const override;
 | 
			
		||||
 | 
			
		||||
protected:
 | 
			
		||||
    /// Called by PollEvents when a key is pressed or released.
 | 
			
		||||
    void OnKeyEvent(int key, u8 state);
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,7 @@
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <glad/glad.h>
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/scm_rev.h"
 | 
			
		||||
#include "common/string_util.h"
 | 
			
		||||
@ -151,6 +152,12 @@ void EmuWindow_SDL2_GL::DoneCurrent() {
 | 
			
		||||
    SDL_GL_MakeCurrent(render_window, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_SDL2_GL::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
 | 
			
		||||
                                               void* surface) const {
 | 
			
		||||
    // Should not have been called from OpenGL
 | 
			
		||||
    UNREACHABLE();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
 | 
			
		||||
    return std::make_unique<SDLGLContext>();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,6 +22,10 @@ public:
 | 
			
		||||
    /// Releases the GL context from the caller thread
 | 
			
		||||
    void DoneCurrent() override;
 | 
			
		||||
 | 
			
		||||
    /// Ignored in OpenGL
 | 
			
		||||
    void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
 | 
			
		||||
                                void* surface) const override;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										161
									
								
								src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,161 @@
 | 
			
		||||
// Copyright 2018 yuzu Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
#include <SDL_vulkan.h>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
#include <vulkan/vulkan.h>
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/scm_rev.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
 | 
			
		||||
 | 
			
		||||
EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(bool fullscreen) : EmuWindow_SDL2(fullscreen) {
 | 
			
		||||
    if (SDL_Vulkan_LoadLibrary(nullptr) != 0) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "SDL failed to load the Vulkan library: {}", SDL_GetError());
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vkGetInstanceProcAddr =
 | 
			
		||||
        reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr());
 | 
			
		||||
    if (vkGetInstanceProcAddr == nullptr) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
 | 
			
		||||
                                                 Common::g_scm_branch, Common::g_scm_desc);
 | 
			
		||||
    render_window =
 | 
			
		||||
        SDL_CreateWindow(window_title.c_str(),
 | 
			
		||||
                         SDL_WINDOWPOS_UNDEFINED, // x position
 | 
			
		||||
                         SDL_WINDOWPOS_UNDEFINED, // y position
 | 
			
		||||
                         Layout::ScreenUndocked::Width, Layout::ScreenUndocked::Height,
 | 
			
		||||
                         SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_VULKAN);
 | 
			
		||||
 | 
			
		||||
    const bool use_standard_layers = UseStandardLayers(vkGetInstanceProcAddr);
 | 
			
		||||
 | 
			
		||||
    u32 extra_ext_count{};
 | 
			
		||||
    if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, NULL)) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions count from SDL! {}",
 | 
			
		||||
                     SDL_GetError());
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto extra_ext_names = std::make_unique<const char* []>(extra_ext_count);
 | 
			
		||||
    if (!SDL_Vulkan_GetInstanceExtensions(render_window, &extra_ext_count, extra_ext_names.get())) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to query Vulkan extensions from SDL! {}", SDL_GetError());
 | 
			
		||||
        exit(1);
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<const char*> enabled_extensions;
 | 
			
		||||
    enabled_extensions.insert(enabled_extensions.begin(), extra_ext_names.get(),
 | 
			
		||||
                              extra_ext_names.get() + extra_ext_count);
 | 
			
		||||
 | 
			
		||||
    std::vector<const char*> enabled_layers;
 | 
			
		||||
    if (use_standard_layers) {
 | 
			
		||||
        enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
 | 
			
		||||
        enabled_layers.push_back("VK_LAYER_LUNARG_standard_validation");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    VkApplicationInfo app_info{};
 | 
			
		||||
    app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
 | 
			
		||||
    app_info.apiVersion = VK_API_VERSION_1_1;
 | 
			
		||||
    app_info.applicationVersion = VK_MAKE_VERSION(0, 1, 0);
 | 
			
		||||
    app_info.pApplicationName = "yuzu-emu";
 | 
			
		||||
    app_info.engineVersion = VK_MAKE_VERSION(0, 1, 0);
 | 
			
		||||
    app_info.pEngineName = "yuzu-emu";
 | 
			
		||||
 | 
			
		||||
    VkInstanceCreateInfo instance_ci{};
 | 
			
		||||
    instance_ci.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
 | 
			
		||||
    instance_ci.pApplicationInfo = &app_info;
 | 
			
		||||
    instance_ci.enabledExtensionCount = static_cast<u32>(enabled_extensions.size());
 | 
			
		||||
    instance_ci.ppEnabledExtensionNames = enabled_extensions.data();
 | 
			
		||||
    if (Settings::values.renderer_debug) {
 | 
			
		||||
        instance_ci.enabledLayerCount = static_cast<u32>(enabled_layers.size());
 | 
			
		||||
        instance_ci.ppEnabledLayerNames = enabled_layers.data();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto vkCreateInstance =
 | 
			
		||||
        reinterpret_cast<PFN_vkCreateInstance>(vkGetInstanceProcAddr(nullptr, "vkCreateInstance"));
 | 
			
		||||
    if (vkCreateInstance == nullptr ||
 | 
			
		||||
        vkCreateInstance(&instance_ci, nullptr, &instance) != VK_SUCCESS) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to create Vulkan instance!");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
 | 
			
		||||
        vkGetInstanceProcAddr(instance, "vkDestroyInstance"));
 | 
			
		||||
    if (vkDestroyInstance == nullptr) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!SDL_Vulkan_CreateSurface(render_window, instance, &surface)) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to create Vulkan surface! {}", SDL_GetError());
 | 
			
		||||
        exit(EXIT_FAILURE);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    OnResize();
 | 
			
		||||
    OnMinimalClientAreaChangeRequest(GetActiveConfig().min_client_area_size);
 | 
			
		||||
    SDL_PumpEvents();
 | 
			
		||||
    LOG_INFO(Frontend, "yuzu Version: {} | {}-{} (Vulkan)", Common::g_build_name,
 | 
			
		||||
             Common::g_scm_branch, Common::g_scm_desc);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() {
 | 
			
		||||
    vkDestroyInstance(instance, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_SDL2_VK::SwapBuffers() {}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_SDL2_VK::MakeCurrent() {
 | 
			
		||||
    // Unused on Vulkan
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_SDL2_VK::DoneCurrent() {
 | 
			
		||||
    // Unused on Vulkan
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_SDL2_VK::RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
 | 
			
		||||
                                               void* surface) const {
 | 
			
		||||
    std::memcpy(get_instance_proc_addr, vkGetInstanceProcAddr, sizeof(vkGetInstanceProcAddr));
 | 
			
		||||
    std::memcpy(instance, &this->instance, sizeof(this->instance));
 | 
			
		||||
    std::memcpy(surface, &this->surface, sizeof(this->surface));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
 | 
			
		||||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EmuWindow_SDL2_VK::UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const {
 | 
			
		||||
    if (!Settings::values.renderer_debug) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto vkEnumerateInstanceLayerProperties =
 | 
			
		||||
        reinterpret_cast<PFN_vkEnumerateInstanceLayerProperties>(
 | 
			
		||||
            vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceLayerProperties"));
 | 
			
		||||
    if (vkEnumerateInstanceLayerProperties == nullptr) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to retrieve Vulkan function pointer!");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u32 available_layers_count{};
 | 
			
		||||
    if (vkEnumerateInstanceLayerProperties(&available_layers_count, nullptr) != VK_SUCCESS) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
    std::vector<VkLayerProperties> layers(available_layers_count);
 | 
			
		||||
    if (vkEnumerateInstanceLayerProperties(&available_layers_count, layers.data()) != VK_SUCCESS) {
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Failed to enumerate Vulkan validation layers!");
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return std::find_if(layers.begin(), layers.end(), [&](const auto& layer) {
 | 
			
		||||
               return layer.layerName == std::string("VK_LAYER_LUNARG_standard_validation");
 | 
			
		||||
           }) != layers.end();
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,39 @@
 | 
			
		||||
// Copyright 2018 yuzu Emulator Project
 | 
			
		||||
// Licensed under GPLv2 or any later version
 | 
			
		||||
// Refer to the license.txt file included.
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <vulkan/vulkan.h>
 | 
			
		||||
#include "core/frontend/emu_window.h"
 | 
			
		||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
 | 
			
		||||
 | 
			
		||||
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
 | 
			
		||||
public:
 | 
			
		||||
    explicit EmuWindow_SDL2_VK(bool fullscreen);
 | 
			
		||||
    ~EmuWindow_SDL2_VK();
 | 
			
		||||
 | 
			
		||||
    /// Swap buffers to display the next frame
 | 
			
		||||
    void SwapBuffers() override;
 | 
			
		||||
 | 
			
		||||
    /// Makes the graphics context current for the caller thread
 | 
			
		||||
    void MakeCurrent() override;
 | 
			
		||||
 | 
			
		||||
    /// Releases the GL context from the caller thread
 | 
			
		||||
    void DoneCurrent() override;
 | 
			
		||||
 | 
			
		||||
    /// Retrieves Vulkan specific handlers from the window
 | 
			
		||||
    void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
 | 
			
		||||
                                void* surface) const override;
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    bool UseStandardLayers(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr) const;
 | 
			
		||||
 | 
			
		||||
    VkInstance instance{};
 | 
			
		||||
    VkSurfaceKHR surface{};
 | 
			
		||||
 | 
			
		||||
    PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr{};
 | 
			
		||||
    PFN_vkDestroyInstance vkDestroyInstance{};
 | 
			
		||||
};
 | 
			
		||||
@ -32,6 +32,9 @@
 | 
			
		||||
#include "yuzu_cmd/config.h"
 | 
			
		||||
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
 | 
			
		||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_gl.h"
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "core/file_sys/registered_cache.h"
 | 
			
		||||
 | 
			
		||||
@ -174,7 +177,20 @@ int main(int argc, char** argv) {
 | 
			
		||||
    Settings::values.use_gdbstub = use_gdbstub;
 | 
			
		||||
    Settings::Apply();
 | 
			
		||||
 | 
			
		||||
    std::unique_ptr<EmuWindow_SDL2> emu_window{std::make_unique<EmuWindow_SDL2_GL>(fullscreen)};
 | 
			
		||||
    std::unique_ptr<EmuWindow_SDL2> emu_window;
 | 
			
		||||
    switch (Settings::values.renderer_backend) {
 | 
			
		||||
    case Settings::RendererBackend::OpenGL:
 | 
			
		||||
        emu_window = std::make_unique<EmuWindow_SDL2_GL>(fullscreen);
 | 
			
		||||
        break;
 | 
			
		||||
    case Settings::RendererBackend::Vulkan:
 | 
			
		||||
#ifdef HAS_VULKAN
 | 
			
		||||
        emu_window = std::make_unique<EmuWindow_SDL2_VK>(fullscreen);
 | 
			
		||||
        break;
 | 
			
		||||
#else
 | 
			
		||||
        LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
 | 
			
		||||
        return 1;
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!Settings::values.use_multi_core) {
 | 
			
		||||
        // Single core mode must acquire OpenGL context for entire emulation session
 | 
			
		||||
 | 
			
		||||
@ -5,10 +5,15 @@
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#define SDL_MAIN_HANDLED
 | 
			
		||||
#include <SDL.h>
 | 
			
		||||
#include <fmt/format.h>
 | 
			
		||||
 | 
			
		||||
#include <glad/glad.h>
 | 
			
		||||
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/scm_rev.h"
 | 
			
		||||
#include "core/settings.h"
 | 
			
		||||
@ -120,3 +125,11 @@ void EmuWindow_SDL2_Hide::MakeCurrent() {
 | 
			
		||||
void EmuWindow_SDL2_Hide::DoneCurrent() {
 | 
			
		||||
    SDL_GL_MakeCurrent(render_window, nullptr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool EmuWindow_SDL2_Hide::IsShown() const {
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void EmuWindow_SDL2_Hide::RetrieveVulkanHandlers(void*, void*, void*) const {
 | 
			
		||||
    UNREACHABLE();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,13 @@ public:
 | 
			
		||||
    /// Releases the GL context from the caller thread
 | 
			
		||||
    void DoneCurrent() override;
 | 
			
		||||
 | 
			
		||||
    /// Whether the screen is being shown or not.
 | 
			
		||||
    bool IsShown() const override;
 | 
			
		||||
 | 
			
		||||
    /// Retrieves Vulkan specific handlers from the window
 | 
			
		||||
    void RetrieveVulkanHandlers(void* get_instance_proc_addr, void* instance,
 | 
			
		||||
                                void* surface) const override;
 | 
			
		||||
 | 
			
		||||
    /// Whether the window is still open, and a close request hasn't yet been sent
 | 
			
		||||
    bool IsOpen() const;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user