ScreenLockDetector/src/vulkanwidget.cpp

1158 lines
39 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}