1158 lines
39 KiB
C++
1158 lines
39 KiB
C++
#include "vulkanwidget.h"
|
||
#include "vulkanrenderer.h"
|
||
#include <QDebug>
|
||
#include <QShowEvent>
|
||
#include <QHideEvent>
|
||
#include <QResizeEvent>
|
||
#include <QPaintEvent>
|
||
#include <QCloseEvent>
|
||
#include <QWindow>
|
||
#include <QApplication>
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
|
||
// Include volk for Vulkan function loading
|
||
#define VK_NO_PROTOTYPES
|
||
#include "../../third_party/volk/volk.h"
|
||
|
||
#ifndef M_PI
|
||
#define M_PI 3.14159265358979323846
|
||
#endif
|
||
|
||
// Platform-specific headers
|
||
#ifdef _WIN32
|
||
#include <windows.h>
|
||
#elif defined(__linux__)
|
||
#include <X11/Xlib.h>
|
||
#endif
|
||
|
||
VulkanWidget::VulkanWidget(QWidget *parent)
|
||
: RenderWidgetBase(parent)
|
||
, m_instance(VK_NULL_HANDLE)
|
||
, m_physicalDevice(VK_NULL_HANDLE)
|
||
, m_device(VK_NULL_HANDLE)
|
||
, m_queue(VK_NULL_HANDLE)
|
||
, m_surface(VK_NULL_HANDLE)
|
||
, m_swapchain(VK_NULL_HANDLE)
|
||
, m_commandPool(VK_NULL_HANDLE)
|
||
, m_initialized(false)
|
||
, m_renderingEnabled(false)
|
||
, m_needsResize(false)
|
||
, m_needsLockedFrameUpdate(false)
|
||
, m_frameCount(0)
|
||
, m_queueFamilyIndex(0)
|
||
, m_currentFrame(0)
|
||
, m_surfaceWidth(0)
|
||
, m_surfaceHeight(0)
|
||
, m_renderTimer(nullptr)
|
||
, m_renderer(nullptr)
|
||
, m_rotationAngle(0.0)
|
||
, m_wavePhase(0.0)
|
||
, m_startTime(QDateTime::currentDateTime())
|
||
, m_lastLockDuration(0)
|
||
, m_lastLockFrameCount(0)
|
||
, m_lockPaintFrameCount(0)
|
||
, m_lockCount(0)
|
||
, m_isClosing(false)
|
||
, m_lastFrameTime(QDateTime::currentDateTime())
|
||
, m_currentFps(0.0)
|
||
{
|
||
// Set widget attributes for native window
|
||
setAttribute(Qt::WA_NativeWindow);
|
||
setAttribute(Qt::WA_PaintOnScreen);
|
||
setAttribute(Qt::WA_OpaquePaintEvent);
|
||
|
||
// Create render timer
|
||
m_renderTimer = new QTimer(this);
|
||
connect(m_renderTimer, &QTimer::timeout, this, &VulkanWidget::onRenderTimer);
|
||
|
||
// Initialize FPS calculation
|
||
m_frameTimes.reserve(FPS_SAMPLE_COUNT);
|
||
|
||
qDebug() << "VulkanWidget created";
|
||
}
|
||
|
||
VulkanWidget::~VulkanWidget()
|
||
{
|
||
qDebug() << "VulkanWidget destroying...";
|
||
|
||
if (m_renderTimer) {
|
||
m_renderTimer->stop();
|
||
}
|
||
|
||
// Clean up renderer first
|
||
if (m_renderer) {
|
||
m_renderer->cleanup();
|
||
delete m_renderer;
|
||
m_renderer = nullptr;
|
||
}
|
||
|
||
cleanupVulkan();
|
||
|
||
qDebug() << "VulkanWidget destroyed";
|
||
}
|
||
|
||
// 实现 RenderWidgetBase 接口
|
||
bool VulkanWidget::initializeRenderer()
|
||
{
|
||
return initializeVulkan();
|
||
}
|
||
|
||
void VulkanWidget::setRenderingEnabled(bool enabled)
|
||
{
|
||
if (m_renderingEnabled == enabled) {
|
||
return;
|
||
}
|
||
|
||
m_renderingEnabled = enabled;
|
||
|
||
if (m_renderingEnabled) {
|
||
qDebug() << "Vulkan rendering ENABLED - Resuming animations";
|
||
// 恢复渲染时,重新启动定时器(锁屏时已停止)
|
||
if (!m_renderTimer->isActive()) {
|
||
m_renderTimer->start(16); // ~60 FPS
|
||
qDebug() << "Render timer restarted";
|
||
}
|
||
|
||
// Unlocked: calculate lock duration
|
||
if (m_lastLockTime.isValid()) {
|
||
QDateTime unlockTime = QDateTime::currentDateTime();
|
||
m_lastLockDuration = m_lastLockTime.secsTo(unlockTime);
|
||
m_lockPaintFrameCount = m_frameCount - m_lastLockFrameCount;
|
||
qDebug() << "Screen was locked for" << m_lastLockDuration << "seconds";
|
||
qDebug() << "Frames at lock:" << m_lastLockFrameCount
|
||
<< "- Frames painted during lock:" << m_lockPaintFrameCount;
|
||
}
|
||
|
||
m_startTime = QDateTime::currentDateTime();
|
||
} else {
|
||
qDebug() << "Vulkan rendering DISABLED - Showing locked state";
|
||
// 关键修复:渲染一帧锁屏界面后停止定时器
|
||
m_needsLockedFrameUpdate = true; // 标记需要渲染锁屏帧
|
||
|
||
// Locked: record lock time
|
||
m_pauseTime = QDateTime::currentDateTime();
|
||
m_lastLockTime = m_pauseTime;
|
||
m_lastLockFrameCount = m_frameCount;
|
||
m_lockCount++;
|
||
qDebug() << "Screen locked at" << m_lastLockTime.toString("yyyy-MM-dd hh:mm:ss")
|
||
<< "- Lock count:" << m_lockCount
|
||
<< "- Frame count at lock:" << m_lastLockFrameCount;
|
||
}
|
||
}
|
||
|
||
bool VulkanWidget::isRenderingEnabled() const
|
||
{
|
||
return m_renderingEnabled;
|
||
}
|
||
|
||
int VulkanWidget::getRenderFrameCount() const
|
||
{
|
||
return m_frameCount;
|
||
}
|
||
|
||
void VulkanWidget::resetFrameCount()
|
||
{
|
||
m_frameCount = 0;
|
||
qDebug() << "Vulkan frame count reset";
|
||
}
|
||
|
||
QString VulkanWidget::getRendererType() const
|
||
{
|
||
return QString("Vulkan");
|
||
}
|
||
|
||
bool VulkanWidget::initializeVulkan()
|
||
{
|
||
qDebug() << "Initializing Vulkan...";
|
||
|
||
if (m_initialized) {
|
||
qDebug() << "Vulkan already initialized";
|
||
return true;
|
||
}
|
||
|
||
// Step 1: Initialize volk
|
||
if (!initializeVolk()) {
|
||
setError("Failed to initialize volk");
|
||
return false;
|
||
}
|
||
|
||
// Step 2: Create Vulkan instance
|
||
if (!createInstance()) {
|
||
setError("Failed to create Vulkan instance");
|
||
return false;
|
||
}
|
||
|
||
// Step 3: Create surface from native window
|
||
if (!createSurface()) {
|
||
setError("Failed to create Vulkan surface");
|
||
return false;
|
||
}
|
||
|
||
// Step 4: Select physical device
|
||
if (!selectPhysicalDevice()) {
|
||
setError("Failed to select physical device");
|
||
return false;
|
||
}
|
||
|
||
// Step 5: Create logical device
|
||
if (!createDevice()) {
|
||
setError("Failed to create logical device");
|
||
return false;
|
||
}
|
||
|
||
// Step 6: Create swapchain
|
||
if (!createSwapchain()) {
|
||
setError("Failed to create swapchain");
|
||
return false;
|
||
}
|
||
|
||
// Step 7: Create command objects
|
||
if (!createCommandObjects()) {
|
||
setError("Failed to create command objects");
|
||
return false;
|
||
}
|
||
|
||
// Step 8: Create synchronization objects
|
||
if (!createSyncObjects()) {
|
||
setError("Failed to create sync objects");
|
||
return false;
|
||
}
|
||
|
||
// Step 9: Create VulkanRenderer - but only if we have reasonable dimensions
|
||
if (m_surfaceWidth >= 100 && m_surfaceHeight >= 100) {
|
||
m_renderer = new VulkanRenderer();
|
||
|
||
// Get swapchain format
|
||
VkSurfaceFormatKHR surfaceFormat;
|
||
uint32_t formatCount = 0;
|
||
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr);
|
||
if (formatCount > 0) {
|
||
std::vector<VkSurfaceFormatKHR> formats(formatCount);
|
||
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, formats.data());
|
||
surfaceFormat = formats[0];
|
||
}
|
||
|
||
// Initialize renderer
|
||
if (!m_renderer->initialize(m_device, m_physicalDevice,
|
||
m_queue, m_queueFamilyIndex,
|
||
static_cast<uint32_t>(surfaceFormat.format),
|
||
m_surfaceWidth, m_surfaceHeight)) {
|
||
setError("Failed to initialize VulkanRenderer");
|
||
delete m_renderer;
|
||
m_renderer = nullptr;
|
||
return false;
|
||
}
|
||
|
||
qDebug() << "VulkanRenderer initialized successfully!";
|
||
qDebug() << "Widget size:" << width() << "x" << height()
|
||
<< "| Surface size:" << m_surfaceWidth << "x" << m_surfaceHeight;
|
||
} else {
|
||
qDebug() << "VulkanRenderer initialization deferred - window too small:" << m_surfaceWidth << "x" << m_surfaceHeight;
|
||
}
|
||
|
||
m_initialized = true;
|
||
qDebug() << "Vulkan initialization successful!";
|
||
return true;
|
||
}
|
||
|
||
bool VulkanWidget::initializeVolk()
|
||
{
|
||
qDebug() << "Initializing volk...";
|
||
|
||
VkResult result = volkInitialize();
|
||
if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to initialize volk, error code:" << result;
|
||
return false;
|
||
}
|
||
|
||
qDebug() << "Volk initialized successfully";
|
||
return true;
|
||
}
|
||
|
||
bool VulkanWidget::createInstance()
|
||
{
|
||
qDebug() << "Creating Vulkan instance...";
|
||
|
||
VkApplicationInfo appInfo = {};
|
||
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||
appInfo.pApplicationName = "VulkanWidget";
|
||
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
||
appInfo.pEngineName = "No Engine";
|
||
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
||
appInfo.apiVersion = VK_API_VERSION_1_0;
|
||
|
||
std::vector<const char*> extensions = getRequiredInstanceExtensions();
|
||
|
||
VkInstanceCreateInfo createInfo = {};
|
||
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||
createInfo.pApplicationInfo = &appInfo;
|
||
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
|
||
createInfo.ppEnabledExtensionNames = extensions.data();
|
||
createInfo.enabledLayerCount = 0;
|
||
|
||
VkResult result = vkCreateInstance(&createInfo, nullptr, &m_instance);
|
||
if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to create Vulkan instance, error code:" << result;
|
||
return false;
|
||
}
|
||
|
||
// Load instance-level functions
|
||
volkLoadInstance(m_instance);
|
||
|
||
qDebug() << "Vulkan instance created successfully";
|
||
return true;
|
||
}
|
||
|
||
bool VulkanWidget::createSurface()
|
||
{
|
||
qDebug() << "Creating Vulkan surface from native window...";
|
||
|
||
if (!windowHandle()) {
|
||
qDebug() << "Window handle not available, creating window...";
|
||
create();
|
||
if (!windowHandle()) {
|
||
qDebug() << "Failed to create window handle";
|
||
return false;
|
||
}
|
||
}
|
||
|
||
QWindow *window = windowHandle();
|
||
|
||
#ifdef _WIN32
|
||
VkWin32SurfaceCreateInfoKHR createInfo = {};
|
||
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
|
||
createInfo.hwnd = reinterpret_cast<HWND>(window->winId());
|
||
createInfo.hinstance = GetModuleHandle(nullptr);
|
||
|
||
VkResult result = vkCreateWin32SurfaceKHR(m_instance, &createInfo, nullptr, &m_surface);
|
||
|
||
#elif defined(__linux__)
|
||
VkXlibSurfaceCreateInfoKHR createInfo = {};
|
||
createInfo.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR;
|
||
// Get X11 display - use default display
|
||
createInfo.dpy = XOpenDisplay(nullptr);
|
||
if (!createInfo.dpy) {
|
||
qDebug() << "Failed to open X11 display";
|
||
return false;
|
||
}
|
||
createInfo.window = static_cast<Window>(window->winId());
|
||
|
||
VkResult result = vkCreateXlibSurfaceKHR(m_instance, &createInfo, nullptr, &m_surface);
|
||
|
||
#elif defined(__APPLE__)
|
||
// macOS requires MoltenVK and Metal
|
||
VkMetalSurfaceCreateInfoEXT createInfo = {};
|
||
createInfo.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT;
|
||
createInfo.pLayer = nullptr; // This needs proper Metal layer setup
|
||
|
||
VkResult result = vkCreateMetalSurfaceEXT(m_instance, &createInfo, nullptr, &m_surface);
|
||
#else
|
||
qDebug() << "Unsupported platform for Vulkan surface creation";
|
||
return false;
|
||
#endif
|
||
|
||
if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to create Vulkan surface, error code:" << result;
|
||
return false;
|
||
}
|
||
|
||
qDebug() << "Vulkan surface created successfully";
|
||
return true;
|
||
}
|
||
|
||
bool VulkanWidget::selectPhysicalDevice()
|
||
{
|
||
qDebug() << "Selecting physical device...";
|
||
|
||
uint32_t deviceCount = 0;
|
||
vkEnumeratePhysicalDevices(m_instance, &deviceCount, nullptr);
|
||
|
||
if (deviceCount == 0) {
|
||
qDebug() << "Failed to find GPUs with Vulkan support";
|
||
return false;
|
||
}
|
||
|
||
std::vector<VkPhysicalDevice> devices(deviceCount);
|
||
vkEnumeratePhysicalDevices(m_instance, &deviceCount, devices.data());
|
||
|
||
// Select the first suitable device
|
||
for (const auto& device : devices) {
|
||
VkPhysicalDeviceProperties properties;
|
||
vkGetPhysicalDeviceProperties(device, &properties);
|
||
|
||
qDebug() << "Found device:" << properties.deviceName;
|
||
|
||
// Temporarily set physical device to check queue families
|
||
m_physicalDevice = device;
|
||
|
||
// Check if device supports our queue family
|
||
uint32_t queueFamilyIndex;
|
||
if (findQueueFamily(queueFamilyIndex)) {
|
||
m_queueFamilyIndex = queueFamilyIndex;
|
||
qDebug() << "Selected device:" << properties.deviceName;
|
||
return true;
|
||
}
|
||
}
|
||
|
||
// Reset if no suitable device found
|
||
m_physicalDevice = VK_NULL_HANDLE;
|
||
qDebug() << "Failed to find suitable physical device";
|
||
return false;
|
||
}
|
||
|
||
bool VulkanWidget::findQueueFamily(uint32_t &queueFamilyIndex)
|
||
{
|
||
uint32_t queueFamilyCount = 0;
|
||
vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, nullptr);
|
||
|
||
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
|
||
vkGetPhysicalDeviceQueueFamilyProperties(m_physicalDevice, &queueFamilyCount, queueFamilies.data());
|
||
|
||
for (uint32_t i = 0; i < queueFamilyCount; i++) {
|
||
// Check if queue family supports graphics
|
||
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
||
// Check if queue family supports presentation
|
||
VkBool32 presentSupport = false;
|
||
vkGetPhysicalDeviceSurfaceSupportKHR(m_physicalDevice, i, m_surface, &presentSupport);
|
||
|
||
if (presentSupport) {
|
||
queueFamilyIndex = i;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
bool VulkanWidget::createDevice()
|
||
{
|
||
qDebug() << "Creating logical device...";
|
||
|
||
float queuePriority = 1.0f;
|
||
VkDeviceQueueCreateInfo queueCreateInfo = {};
|
||
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||
queueCreateInfo.queueFamilyIndex = m_queueFamilyIndex;
|
||
queueCreateInfo.queueCount = 1;
|
||
queueCreateInfo.pQueuePriorities = &queuePriority;
|
||
|
||
// Query supported features first
|
||
VkPhysicalDeviceFeatures supportedFeatures;
|
||
vkGetPhysicalDeviceFeatures(m_physicalDevice, &supportedFeatures);
|
||
|
||
VkPhysicalDeviceFeatures deviceFeatures = {};
|
||
|
||
// Enable sample rate shading for better MSAA quality
|
||
if (supportedFeatures.sampleRateShading) {
|
||
deviceFeatures.sampleRateShading = VK_TRUE;
|
||
qDebug() << "Sample rate shading feature enabled";
|
||
} else {
|
||
qDebug() << "Sample rate shading not supported by device";
|
||
}
|
||
|
||
// Enable wide lines if supported (for smoother line rendering)
|
||
if (supportedFeatures.wideLines) {
|
||
deviceFeatures.wideLines = VK_TRUE;
|
||
qDebug() << "Wide lines feature enabled";
|
||
}
|
||
|
||
std::vector<const char*> deviceExtensions = getRequiredDeviceExtensions();
|
||
|
||
VkDeviceCreateInfo createInfo = {};
|
||
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||
createInfo.pQueueCreateInfos = &queueCreateInfo;
|
||
createInfo.queueCreateInfoCount = 1;
|
||
createInfo.pEnabledFeatures = &deviceFeatures;
|
||
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
|
||
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
|
||
createInfo.enabledLayerCount = 0;
|
||
|
||
VkResult result = vkCreateDevice(m_physicalDevice, &createInfo, nullptr, &m_device);
|
||
if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to create logical device, error code:" << result;
|
||
return false;
|
||
}
|
||
|
||
// Load device-level functions
|
||
volkLoadDevice(m_device);
|
||
|
||
// Get queue handle
|
||
vkGetDeviceQueue(m_device, m_queueFamilyIndex, 0, &m_queue);
|
||
|
||
qDebug() << "Logical device created successfully";
|
||
return true;
|
||
}
|
||
|
||
bool VulkanWidget::createSwapchain()
|
||
{
|
||
qDebug() << "Creating swapchain...";
|
||
qDebug() << "Widget size:" << width() << "x" << height();
|
||
qDebug() << "Device pixel ratio:" << devicePixelRatioF();
|
||
|
||
// Don't create swapchain if closing
|
||
if (m_isClosing) {
|
||
qDebug() << "Aborting swapchain creation - widget is closing";
|
||
return false;
|
||
}
|
||
|
||
// Query surface capabilities
|
||
VkSurfaceCapabilitiesKHR capabilities;
|
||
VkResult result = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(m_physicalDevice, m_surface, &capabilities);
|
||
|
||
if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to get surface capabilities, error code:" << result;
|
||
return false;
|
||
}
|
||
|
||
qDebug() << "Surface capabilities - current extent:"
|
||
<< capabilities.currentExtent.width << "x" << capabilities.currentExtent.height;
|
||
qDebug() << "Surface capabilities - min extent:"
|
||
<< capabilities.minImageExtent.width << "x" << capabilities.minImageExtent.height;
|
||
qDebug() << "Surface capabilities - max extent:"
|
||
<< capabilities.maxImageExtent.width << "x" << capabilities.maxImageExtent.height;
|
||
|
||
// Validate surface capabilities - detect invalid/corrupted values
|
||
// This can happen when the window is being destroyed
|
||
if (capabilities.currentExtent.width > 16384 || capabilities.currentExtent.height > 16384 ||
|
||
capabilities.minImageExtent.width > 16384 || capabilities.minImageExtent.height > 16384 ||
|
||
capabilities.maxImageExtent.width > 1000000 || capabilities.maxImageExtent.height > 1000000) {
|
||
qDebug() << "ERROR: Invalid surface capabilities detected - surface may be destroyed";
|
||
qDebug() << "This usually happens when the window is being closed";
|
||
return false;
|
||
}
|
||
|
||
// Query surface formats
|
||
uint32_t formatCount;
|
||
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr);
|
||
std::vector<VkSurfaceFormatKHR> formats(formatCount);
|
||
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, formats.data());
|
||
|
||
// Query present modes
|
||
uint32_t presentModeCount;
|
||
vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, nullptr);
|
||
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
|
||
vkGetPhysicalDeviceSurfacePresentModesKHR(m_physicalDevice, m_surface, &presentModeCount, presentModes.data());
|
||
|
||
// Select format
|
||
VkSurfaceFormatKHR surfaceFormat = formats[0];
|
||
for (const auto& format : formats) {
|
||
if (format.format == VK_FORMAT_B8G8R8A8_SRGB &&
|
||
format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
|
||
surfaceFormat = format;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Select present mode (prefer mailbox, fallback to FIFO)
|
||
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
|
||
for (const auto& mode : presentModes) {
|
||
if (mode == VK_PRESENT_MODE_MAILBOX_KHR) {
|
||
presentMode = mode;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Determine extent
|
||
VkExtent2D extent;
|
||
if (capabilities.currentExtent.width != UINT32_MAX) {
|
||
extent = capabilities.currentExtent;
|
||
qDebug() << "Using surface-provided extent:" << extent.width << "x" << extent.height;
|
||
} else {
|
||
// Calculate extent based on widget size with DPI scaling
|
||
uint32_t pixelWidth = static_cast<uint32_t>(width() * devicePixelRatioF());
|
||
uint32_t pixelHeight = static_cast<uint32_t>(height() * devicePixelRatioF());
|
||
|
||
extent.width = std::max(capabilities.minImageExtent.width,
|
||
std::min(capabilities.maxImageExtent.width, pixelWidth));
|
||
extent.height = std::max(capabilities.minImageExtent.height,
|
||
std::min(capabilities.maxImageExtent.height, pixelHeight));
|
||
qDebug() << "Calculated extent from widget:" << extent.width << "x" << extent.height
|
||
<< "(widget:" << width() << "x" << height()
|
||
<< "* DPR:" << devicePixelRatioF() << ")";
|
||
}
|
||
|
||
m_surfaceWidth = extent.width;
|
||
m_surfaceHeight = extent.height;
|
||
|
||
qDebug() << "Final swapchain extent:" << m_surfaceWidth << "x" << m_surfaceHeight;
|
||
|
||
// Determine image count - limit to MAX_FRAMES_IN_FLIGHT for proper synchronization
|
||
uint32_t imageCount = capabilities.minImageCount + 1;
|
||
if (capabilities.maxImageCount > 0 && imageCount > capabilities.maxImageCount) {
|
||
imageCount = capabilities.maxImageCount;
|
||
}
|
||
// 确保不超过 MAX_FRAMES_IN_FLIGHT,避免三缓冲同步问题
|
||
if (imageCount > MAX_FRAMES_IN_FLIGHT) {
|
||
qDebug() << "Limiting swapchain images from" << imageCount << "to" << MAX_FRAMES_IN_FLIGHT;
|
||
imageCount = MAX_FRAMES_IN_FLIGHT;
|
||
}
|
||
|
||
VkSwapchainCreateInfoKHR createInfo = {};
|
||
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||
createInfo.surface = m_surface;
|
||
createInfo.minImageCount = imageCount;
|
||
createInfo.imageFormat = surfaceFormat.format;
|
||
createInfo.imageColorSpace = surfaceFormat.colorSpace;
|
||
createInfo.imageExtent = extent;
|
||
createInfo.imageArrayLayers = 1;
|
||
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||
createInfo.preTransform = capabilities.currentTransform;
|
||
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||
createInfo.presentMode = presentMode;
|
||
createInfo.clipped = VK_TRUE;
|
||
createInfo.oldSwapchain = VK_NULL_HANDLE;
|
||
|
||
VkResult swapchainResult = vkCreateSwapchainKHR(m_device, &createInfo, nullptr, &m_swapchain);
|
||
if (swapchainResult != VK_SUCCESS) {
|
||
qDebug() << "Failed to create swapchain, error code:" << swapchainResult;
|
||
return false;
|
||
}
|
||
|
||
// Retrieve swapchain images
|
||
vkGetSwapchainImagesKHR(m_device, m_swapchain, &imageCount, nullptr);
|
||
m_swapchainImages.resize(imageCount);
|
||
vkGetSwapchainImagesKHR(m_device, m_swapchain, &imageCount,
|
||
reinterpret_cast<VkImage*>(m_swapchainImages.data()));
|
||
|
||
// Create image views for swapchain images
|
||
m_swapchainImageViews.resize(imageCount);
|
||
for (uint32_t i = 0; i < imageCount; i++) {
|
||
VkImageViewCreateInfo viewInfo = {};
|
||
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||
viewInfo.image = reinterpret_cast<VkImage>(m_swapchainImages[i]);
|
||
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||
viewInfo.format = surfaceFormat.format;
|
||
viewInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||
viewInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||
viewInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||
viewInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
viewInfo.subresourceRange.baseMipLevel = 0;
|
||
viewInfo.subresourceRange.levelCount = 1;
|
||
viewInfo.subresourceRange.baseArrayLayer = 0;
|
||
viewInfo.subresourceRange.layerCount = 1;
|
||
|
||
VkImageView imageView;
|
||
VkResult viewResult = vkCreateImageView(m_device, &viewInfo, nullptr, &imageView);
|
||
if (viewResult != VK_SUCCESS) {
|
||
qDebug() << "Failed to create image view" << i << ", error code:" << viewResult;
|
||
return false;
|
||
}
|
||
m_swapchainImageViews[i] = reinterpret_cast<void*>(imageView);
|
||
}
|
||
|
||
qDebug() << "Swapchain created successfully with" << imageCount << "images (MAX_FRAMES_IN_FLIGHT="
|
||
<< MAX_FRAMES_IN_FLIGHT << "), size:" << m_surfaceWidth << "x" << m_surfaceHeight;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool VulkanWidget::createCommandObjects()
|
||
{
|
||
qDebug() << "Creating command objects...";
|
||
|
||
VkCommandPoolCreateInfo poolInfo = {};
|
||
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||
poolInfo.queueFamilyIndex = m_queueFamilyIndex;
|
||
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
|
||
|
||
VkResult result = vkCreateCommandPool(m_device, &poolInfo, nullptr, &m_commandPool);
|
||
if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to create command pool, error code:" << result;
|
||
return false;
|
||
}
|
||
|
||
m_commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
|
||
|
||
VkCommandBufferAllocateInfo allocInfo = {};
|
||
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||
allocInfo.commandPool = m_commandPool;
|
||
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||
allocInfo.commandBufferCount = static_cast<uint32_t>(m_commandBuffers.size());
|
||
|
||
result = vkAllocateCommandBuffers(m_device, &allocInfo, m_commandBuffers.data());
|
||
if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to allocate command buffers, error code:" << result;
|
||
return false;
|
||
}
|
||
|
||
qDebug() << "Command objects created successfully";
|
||
return true;
|
||
}
|
||
|
||
bool VulkanWidget::createSyncObjects()
|
||
{
|
||
qDebug() << "Creating synchronization objects...";
|
||
|
||
m_imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
||
m_renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
|
||
m_inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
|
||
|
||
VkSemaphoreCreateInfo semaphoreInfo = {};
|
||
semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||
|
||
VkFenceCreateInfo fenceInfo = {};
|
||
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
|
||
|
||
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||
if (vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &m_imageAvailableSemaphores[i]) != VK_SUCCESS ||
|
||
vkCreateSemaphore(m_device, &semaphoreInfo, nullptr, &m_renderFinishedSemaphores[i]) != VK_SUCCESS ||
|
||
vkCreateFence(m_device, &fenceInfo, nullptr, &m_inFlightFences[i]) != VK_SUCCESS) {
|
||
qDebug() << "Failed to create synchronization objects";
|
||
return false;
|
||
}
|
||
}
|
||
|
||
qDebug() << "Synchronization objects created successfully";
|
||
return true;
|
||
}
|
||
|
||
bool VulkanWidget::recreateSwapchain()
|
||
{
|
||
qDebug() << "Recreating swapchain...";
|
||
|
||
// Don't recreate if closing
|
||
if (m_isClosing) {
|
||
qDebug() << "Aborting swapchain recreation - widget is closing";
|
||
return false;
|
||
}
|
||
|
||
// Wait for device to be idle
|
||
vkDeviceWaitIdle(m_device);
|
||
|
||
// Cleanup old swapchain
|
||
cleanupSwapchain();
|
||
|
||
// Create new swapchain
|
||
if (!createSwapchain()) {
|
||
qDebug() << "Failed to recreate swapchain";
|
||
return false;
|
||
}
|
||
|
||
// Update renderer with new surface dimensions
|
||
if (m_renderer) {
|
||
qDebug() << "Updating VulkanRenderer to surface size:" << m_surfaceWidth << "x" << m_surfaceHeight;
|
||
m_renderer->resize(m_surfaceWidth, m_surfaceHeight);
|
||
}
|
||
|
||
qDebug() << "Swapchain recreated successfully";
|
||
return true;
|
||
}
|
||
|
||
void VulkanWidget::renderFrame()
|
||
{
|
||
// Don't render if closing or not initialized
|
||
if (!m_initialized || m_isClosing) {
|
||
return;
|
||
}
|
||
|
||
// 关键修复:即使 renderingEnabled=false 也继续渲染,以显示锁屏状态
|
||
// 只是传递不同的 paintingEnabled 参数给 renderer
|
||
|
||
// Calculate FPS
|
||
QDateTime currentTime = QDateTime::currentDateTime();
|
||
qint64 frameTimeUs = m_lastFrameTime.msecsTo(currentTime) * 1000; // Convert to microseconds
|
||
m_lastFrameTime = currentTime;
|
||
|
||
if (frameTimeUs > 0) {
|
||
// Add frame time to circular buffer
|
||
if (m_frameTimes.size() >= FPS_SAMPLE_COUNT) {
|
||
m_frameTimes.erase(m_frameTimes.begin());
|
||
}
|
||
m_frameTimes.push_back(frameTimeUs);
|
||
|
||
// Calculate average FPS from recent frames
|
||
if (!m_frameTimes.empty()) {
|
||
qint64 totalTimeUs = 0;
|
||
for (qint64 time : m_frameTimes) {
|
||
totalTimeUs += time;
|
||
}
|
||
double avgFrameTimeUs = static_cast<double>(totalTimeUs) / m_frameTimes.size();
|
||
m_currentFps = 1000000.0 / avgFrameTimeUs; // Convert microseconds to FPS
|
||
}
|
||
}
|
||
|
||
// Wait for previous frame
|
||
vkWaitForFences(m_device, 1, &m_inFlightFences[m_currentFrame], VK_TRUE, UINT64_MAX);
|
||
|
||
// Acquire next image
|
||
uint32_t imageIndex;
|
||
VkResult result = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX,
|
||
m_imageAvailableSemaphores[m_currentFrame],
|
||
VK_NULL_HANDLE, &imageIndex);
|
||
|
||
if (result == VK_ERROR_OUT_OF_DATE_KHR) {
|
||
recreateSwapchain();
|
||
return;
|
||
} else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) {
|
||
//qDebug() << "Failed to acquire swapchain image";
|
||
return;
|
||
}
|
||
|
||
// Reset fence
|
||
vkResetFences(m_device, 1, &m_inFlightFences[m_currentFrame]);
|
||
|
||
// Record command buffer
|
||
vkResetCommandBuffer(m_commandBuffers[m_currentFrame], 0);
|
||
recordCommandBuffer(m_commandBuffers[m_currentFrame], imageIndex);
|
||
|
||
// Submit command buffer
|
||
VkSubmitInfo submitInfo = {};
|
||
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||
|
||
VkSemaphore waitSemaphores[] = {m_imageAvailableSemaphores[m_currentFrame]};
|
||
VkPipelineStageFlags waitStages[] = {VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT};
|
||
submitInfo.waitSemaphoreCount = 1;
|
||
submitInfo.pWaitSemaphores = waitSemaphores;
|
||
submitInfo.pWaitDstStageMask = waitStages;
|
||
submitInfo.commandBufferCount = 1;
|
||
submitInfo.pCommandBuffers = &m_commandBuffers[m_currentFrame];
|
||
|
||
VkSemaphore signalSemaphores[] = {m_renderFinishedSemaphores[m_currentFrame]};
|
||
submitInfo.signalSemaphoreCount = 1;
|
||
submitInfo.pSignalSemaphores = signalSemaphores;
|
||
|
||
result = vkQueueSubmit(m_queue, 1, &submitInfo, m_inFlightFences[m_currentFrame]);
|
||
if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to submit draw command buffer! Error code = " << result;
|
||
return;
|
||
}
|
||
|
||
// Present
|
||
VkPresentInfoKHR presentInfo = {};
|
||
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
|
||
presentInfo.waitSemaphoreCount = 1;
|
||
presentInfo.pWaitSemaphores = signalSemaphores;
|
||
|
||
VkSwapchainKHR swapchains[] = {m_swapchain};
|
||
presentInfo.swapchainCount = 1;
|
||
presentInfo.pSwapchains = swapchains;
|
||
presentInfo.pImageIndices = &imageIndex;
|
||
|
||
result = vkQueuePresentKHR(m_queue, &presentInfo);
|
||
|
||
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR || m_needsResize) {
|
||
m_needsResize = false;
|
||
recreateSwapchain();
|
||
} else if (result != VK_SUCCESS) {
|
||
qDebug() << "Failed to present swapchain image";
|
||
}
|
||
|
||
m_currentFrame = (m_currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
|
||
m_frameCount++;
|
||
}
|
||
|
||
void VulkanWidget::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex)
|
||
{
|
||
// Initialize renderer now if we didn't do it earlier (window was too small)
|
||
if (!m_renderer && m_surfaceWidth >= 100 && m_surfaceHeight >= 100) {
|
||
qDebug() << "Creating deferred VulkanRenderer with actual surface size:" << m_surfaceWidth << "x" << m_surfaceHeight;
|
||
m_renderer = new VulkanRenderer();
|
||
|
||
VkSurfaceFormatKHR surfaceFormat;
|
||
uint32_t formatCount = 0;
|
||
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, nullptr);
|
||
if (formatCount > 0) {
|
||
std::vector<VkSurfaceFormatKHR> formats(formatCount);
|
||
vkGetPhysicalDeviceSurfaceFormatsKHR(m_physicalDevice, m_surface, &formatCount, formats.data());
|
||
surfaceFormat = formats[0];
|
||
}
|
||
|
||
if (!m_renderer->initialize(m_device, m_physicalDevice,
|
||
m_queue, m_queueFamilyIndex,
|
||
static_cast<uint32_t>(surfaceFormat.format),
|
||
m_surfaceWidth, m_surfaceHeight)) {
|
||
qDebug() << "Failed to initialize deferred VulkanRenderer";
|
||
delete m_renderer;
|
||
m_renderer = nullptr;
|
||
} else {
|
||
qDebug() << "Deferred VulkanRenderer initialized successfully with surface size:"
|
||
<< m_surfaceWidth << "x" << m_surfaceHeight;
|
||
}
|
||
}
|
||
|
||
// Use VulkanRenderer if available
|
||
if (m_renderer) {
|
||
if (imageIndex >= m_swapchainImageViews.size()) {
|
||
qDebug() << "ERROR: imageIndex out of bounds!";
|
||
return;
|
||
}
|
||
|
||
VkImageView imageView = reinterpret_cast<VkImageView>(m_swapchainImageViews[imageIndex]);
|
||
|
||
// Build lock info string
|
||
QString lockInfo;
|
||
if (m_lastLockTime.isValid()) {
|
||
lockInfo = QString("Last Lock: %1\nDuration: %2s\nFrame Count at Lock: %3\nFrames During Lock: %4\nLock Count: %5")
|
||
.arg(m_lastLockTime.toString("hh:mm:ss"))
|
||
.arg(m_lastLockDuration)
|
||
.arg(m_lastLockFrameCount)
|
||
.arg(m_lockPaintFrameCount)
|
||
.arg(m_lockCount);
|
||
}
|
||
|
||
// Calculate elapsed time in seconds
|
||
QDateTime now = QDateTime::currentDateTime();
|
||
qint64 elapsedTime = m_startTime.secsTo(now);
|
||
|
||
m_renderer->recordCommandBuffer(commandBuffer, imageIndex, imageView,
|
||
m_frameCount, static_cast<double>(elapsedTime),
|
||
m_currentFps,
|
||
m_rotationAngle, m_wavePhase,
|
||
m_renderingEnabled, lockInfo.toStdString());
|
||
return;
|
||
}
|
||
|
||
// Fallback: Simple clear color pass
|
||
VkCommandBufferBeginInfo beginInfo = {};
|
||
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||
|
||
vkBeginCommandBuffer(commandBuffer, &beginInfo);
|
||
|
||
VkClearColorValue clearColor;
|
||
float hue = (m_frameCount % 360) / 360.0f;
|
||
float r = std::abs(std::sin(hue * 6.28318f));
|
||
float g = std::abs(std::sin((hue + 0.33f) * 6.28318f));
|
||
float b = std::abs(std::sin((hue + 0.66f) * 6.28318f));
|
||
clearColor.float32[0] = r * 0.5f + 0.1f;
|
||
clearColor.float32[1] = g * 0.5f + 0.1f;
|
||
clearColor.float32[2] = b * 0.5f + 0.1f;
|
||
clearColor.float32[3] = 1.0f;
|
||
|
||
VkImageSubresourceRange range = {};
|
||
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||
range.baseMipLevel = 0;
|
||
range.levelCount = 1;
|
||
range.baseArrayLayer = 0;
|
||
range.layerCount = 1;
|
||
|
||
VkImage image = reinterpret_cast<VkImage>(m_swapchainImages[imageIndex]);
|
||
|
||
VkImageMemoryBarrier barrier1 = {};
|
||
barrier1.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||
barrier1.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
||
barrier1.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
barrier1.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||
barrier1.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||
barrier1.image = image;
|
||
barrier1.subresourceRange = range;
|
||
barrier1.srcAccessMask = 0;
|
||
barrier1.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||
|
||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
|
||
VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier1);
|
||
|
||
vkCmdClearColorImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||
&clearColor, 1, &range);
|
||
|
||
VkImageMemoryBarrier barrier2 = {};
|
||
barrier2.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||
barrier2.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
|
||
barrier2.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||
barrier2.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||
barrier2.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||
barrier2.image = image;
|
||
barrier2.subresourceRange = range;
|
||
barrier2.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||
barrier2.dstAccessMask = 0;
|
||
|
||
vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT,
|
||
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier2);
|
||
|
||
vkEndCommandBuffer(commandBuffer);
|
||
}
|
||
|
||
void VulkanWidget::cleanupSwapchain()
|
||
{
|
||
// Destroy image views first
|
||
for (void* imageView : m_swapchainImageViews) {
|
||
if (imageView != nullptr) {
|
||
vkDestroyImageView(m_device, reinterpret_cast<VkImageView>(imageView), nullptr);
|
||
}
|
||
}
|
||
m_swapchainImageViews.clear();
|
||
|
||
if (m_swapchain != VK_NULL_HANDLE) {
|
||
vkDestroySwapchainKHR(m_device, m_swapchain, nullptr);
|
||
m_swapchain = VK_NULL_HANDLE;
|
||
}
|
||
|
||
m_swapchainImages.clear();
|
||
}
|
||
|
||
void VulkanWidget::cleanupVulkan()
|
||
{
|
||
if (!m_initialized) {
|
||
return;
|
||
}
|
||
|
||
if (m_device != VK_NULL_HANDLE) {
|
||
vkDeviceWaitIdle(m_device);
|
||
}
|
||
|
||
// Cleanup sync objects
|
||
for (int i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
|
||
if (m_imageAvailableSemaphores[i] != VK_NULL_HANDLE) {
|
||
vkDestroySemaphore(m_device, m_imageAvailableSemaphores[i], nullptr);
|
||
}
|
||
if (m_renderFinishedSemaphores[i] != VK_NULL_HANDLE) {
|
||
vkDestroySemaphore(m_device, m_renderFinishedSemaphores[i], nullptr);
|
||
}
|
||
if (m_inFlightFences[i] != VK_NULL_HANDLE) {
|
||
vkDestroyFence(m_device, m_inFlightFences[i], nullptr);
|
||
}
|
||
}
|
||
|
||
// Cleanup command pool
|
||
if (m_commandPool != VK_NULL_HANDLE) {
|
||
vkDestroyCommandPool(m_device, m_commandPool, nullptr);
|
||
}
|
||
|
||
// Cleanup swapchain
|
||
cleanupSwapchain();
|
||
|
||
// Cleanup device
|
||
if (m_device != VK_NULL_HANDLE) {
|
||
vkDestroyDevice(m_device, nullptr);
|
||
}
|
||
|
||
// Cleanup surface
|
||
if (m_surface != VK_NULL_HANDLE) {
|
||
vkDestroySurfaceKHR(m_instance, m_surface, nullptr);
|
||
}
|
||
|
||
// Cleanup instance
|
||
if (m_instance != VK_NULL_HANDLE) {
|
||
vkDestroyInstance(m_instance, nullptr);
|
||
}
|
||
|
||
m_initialized = false;
|
||
qDebug() << "Vulkan cleanup complete";
|
||
}
|
||
|
||
|
||
|
||
void VulkanWidget::showEvent(QShowEvent *event)
|
||
{
|
||
QWidget::showEvent(event);
|
||
|
||
if (!m_initialized) {
|
||
initializeVulkan();
|
||
}
|
||
}
|
||
|
||
void VulkanWidget::hideEvent(QHideEvent *event)
|
||
{
|
||
QWidget::hideEvent(event);
|
||
setRenderingEnabled(false);
|
||
}
|
||
|
||
void VulkanWidget::closeEvent(QCloseEvent *event)
|
||
{
|
||
qDebug() << "VulkanWidget closeEvent - stopping rendering and cleaning up";
|
||
|
||
// Set closing flag to prevent any further rendering attempts
|
||
m_isClosing = true;
|
||
|
||
// Stop the render timer immediately
|
||
if (m_renderTimer) {
|
||
m_renderTimer->stop();
|
||
qDebug() << "Render timer stopped on close";
|
||
}
|
||
|
||
// Wait for device to be idle before accepting close event
|
||
if (m_device != VK_NULL_HANDLE) {
|
||
vkDeviceWaitIdle(m_device);
|
||
qDebug() << "Vulkan device idle on close";
|
||
}
|
||
|
||
// Accept the close event
|
||
QWidget::closeEvent(event);
|
||
}
|
||
|
||
void VulkanWidget::resizeEvent(QResizeEvent *event)
|
||
{
|
||
QWidget::resizeEvent(event);
|
||
|
||
if (m_initialized) {
|
||
m_needsResize = true;
|
||
|
||
// Note: Don't update renderer size here - it will be updated after swapchain recreation
|
||
// The renderer must use the actual surface dimensions (m_surfaceWidth/Height),
|
||
// not the widget dimensions, which may differ on high DPI displays
|
||
}
|
||
}
|
||
|
||
void VulkanWidget::paintEvent(QPaintEvent *event)
|
||
{
|
||
// Do nothing - rendering is handled by Vulkan
|
||
Q_UNUSED(event);
|
||
}
|
||
|
||
QPaintEngine* VulkanWidget::paintEngine() const
|
||
{
|
||
// Return nullptr to prevent Qt from using QPainter
|
||
return nullptr;
|
||
}
|
||
|
||
void VulkanWidget::onRenderTimer()
|
||
{
|
||
if (m_renderingEnabled) {
|
||
// Update animation parameters
|
||
m_rotationAngle += 2.0;
|
||
if (m_rotationAngle >= 360.0) {
|
||
m_rotationAngle -= 360.0;
|
||
}
|
||
|
||
m_wavePhase += 0.05;
|
||
if (m_wavePhase >= 2 * M_PI) {
|
||
m_wavePhase -= 2 * M_PI;
|
||
}
|
||
|
||
// 正常渲染
|
||
renderFrame();
|
||
} else if (m_needsLockedFrameUpdate) {
|
||
// 锁屏状态:只渲染一帧显示锁屏界面
|
||
m_needsLockedFrameUpdate = false;
|
||
renderFrame();
|
||
|
||
// 渲染完锁屏帧后停止定时器,避免浪费资源
|
||
m_renderTimer->stop();
|
||
qDebug() << "Locked frame rendered, timer stopped";
|
||
}
|
||
// 否则:锁屏状态且已渲染锁屏帧,不做任何事
|
||
}
|
||
|
||
void VulkanWidget::setError(const QString &error)
|
||
{
|
||
m_lastError = error;
|
||
qDebug() << "VulkanWidget Error:" << error;
|
||
}
|
||
|
||
std::vector<const char*> VulkanWidget::getRequiredInstanceExtensions()
|
||
{
|
||
std::vector<const char*> extensions;
|
||
|
||
extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||
|
||
#ifdef _WIN32
|
||
extensions.push_back(VK_KHR_WIN32_SURFACE_EXTENSION_NAME);
|
||
#elif defined(__linux__)
|
||
extensions.push_back(VK_KHR_XLIB_SURFACE_EXTENSION_NAME);
|
||
#elif defined(__APPLE__)
|
||
extensions.push_back(VK_EXT_METAL_SURFACE_EXTENSION_NAME);
|
||
#endif
|
||
|
||
return extensions;
|
||
}
|
||
|
||
std::vector<const char*> VulkanWidget::getRequiredDeviceExtensions()
|
||
{
|
||
std::vector<const char*> extensions;
|
||
extensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||
return extensions;
|
||
}
|