Files
face_sdk/vulkan/Application.cpp
2026-04-25 16:42:20 +08:00

1436 lines
53 KiB
C++
Raw Permalink 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 "Application.h"
#include "lodepng.h"
#include <string>
#ifdef _WIN32
#include <codecvt>
#else
#include <game-activity/native_app_glue/android_native_app_glue.h>
#include <game-activity/GameActivity.h>
#include <vulkan/vulkan_android.h>
#include "../app/src/main/cpp/AndroidOut.h"
#include "../app/src/main/cpp/DebugLog.h"
#endif
#ifdef _WIN32
#define FACE_DBG_LOG(...) ((void)0)
#else
#define FACE_DBG_LOG(...) DebugLog::log(__VA_ARGS__)
#endif
static const char* VkResultStr(VkResult r) {
switch (r) {
case VK_SUCCESS: return "VK_SUCCESS";
case VK_NOT_READY: return "VK_NOT_READY";
case VK_TIMEOUT: return "VK_TIMEOUT";
case VK_EVENT_SET: return "VK_EVENT_SET";
case VK_EVENT_RESET: return "VK_EVENT_RESET";
case VK_INCOMPLETE: return "VK_INCOMPLETE";
case VK_SUBOPTIMAL_KHR: return "VK_SUBOPTIMAL_KHR";
case VK_ERROR_OUT_OF_HOST_MEMORY: return "VK_ERROR_OUT_OF_HOST_MEMORY";
case VK_ERROR_OUT_OF_DEVICE_MEMORY: return "VK_ERROR_OUT_OF_DEVICE_MEMORY";
case VK_ERROR_INITIALIZATION_FAILED: return "VK_ERROR_INITIALIZATION_FAILED";
case VK_ERROR_DEVICE_LOST: return "VK_ERROR_DEVICE_LOST";
case VK_ERROR_MEMORY_MAP_FAILED: return "VK_ERROR_MEMORY_MAP_FAILED";
case VK_ERROR_LAYER_NOT_PRESENT: return "VK_ERROR_LAYER_NOT_PRESENT";
case VK_ERROR_EXTENSION_NOT_PRESENT: return "VK_ERROR_EXTENSION_NOT_PRESENT";
case VK_ERROR_FEATURE_NOT_PRESENT: return "VK_ERROR_FEATURE_NOT_PRESENT";
case VK_ERROR_INCOMPATIBLE_DRIVER: return "VK_ERROR_INCOMPATIBLE_DRIVER";
case VK_ERROR_TOO_MANY_OBJECTS: return "VK_ERROR_TOO_MANY_OBJECTS";
case VK_ERROR_FORMAT_NOT_SUPPORTED: return "VK_ERROR_FORMAT_NOT_SUPPORTED";
case VK_ERROR_FRAGMENTED_POOL: return "VK_ERROR_FRAGMENTED_POOL";
case VK_ERROR_OUT_OF_DATE_KHR: return "VK_ERROR_OUT_OF_DATE_KHR";
case VK_ERROR_SURFACE_LOST_KHR: return "VK_ERROR_SURFACE_LOST_KHR";
case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: return "VK_ERROR_NATIVE_WINDOW_IN_USE_KHR";
case VK_ERROR_OUT_OF_POOL_MEMORY: return "VK_ERROR_OUT_OF_POOL_MEMORY";
case VK_ERROR_INVALID_EXTERNAL_HANDLE: return "VK_ERROR_INVALID_EXTERNAL_HANDLE";
case VK_ERROR_FRAGMENTATION: return "VK_ERROR_FRAGMENTATION";
case VK_ERROR_UNKNOWN: return "VK_ERROR_UNKNOWN";
default: return "VK_UNMAPPED_VALUE";
}
}
void Application::initWindow()
{
#ifdef _WIN32
glfwInit();
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
window = glfwCreateWindow(480, 480, "Vulkan", nullptr, nullptr);
#else
#endif
}
#ifdef _WIN32
#else
extern android_app* g_android_app;
#endif
void Application::createSurface() {
#ifdef _WIN32
if (glfwCreateWindowSurface(instance, window, nullptr, &surface) != VK_SUCCESS) {
throw std::runtime_error("failed to create window surface!");
}
#else
//VkSurfaceKHR surface{};
VkAndroidSurfaceCreateInfoKHR info{VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR};
info.window = g_android_app->window;
VK_CHECK(vkCreateAndroidSurfaceKHR(instance, &info, nullptr, &surface));
#endif
}
void Application::initVulkan()
{
if (!_applicationInited)
{
initWindow();
createInstance(); // 创建 Vulkan 实例
setupDebugMessenger(); // 在这里调用
createSurface(); // 创建窗口表面
pickPhysicalDevice(physicalDevice, surface); // 选择物理设备
createLogicalDevice(); // 创建逻辑设备
createSwapChain(); // 创建交换链
createImageViews(); // 创建交换链图像视图
createRenderPass(); // 创建渲染流程
createPipelineLayout(); // 在这里调用
createGraphicsPipeline(); // 创建图形管线
createFramebuffers(); // 创建帧缓冲区
createCommandPool(); // 创建命令池
createCommandBuffer(); // 创建命令缓冲区
createSyncObjects(); // 创建同步对象
// 复用式 single-time-command 资源:从 commandPool_ex 预分配
// kTransferSlotCount 个 cmdbuf + 同数 signaled fence
// 之后所有 copyBuffer / updateTexture 走 runTransferCommand
// 不再每次 vkAllocate/vkFree。
createTransferResources();
_lastDrawFrameTime = getCurrentTimeMillis();
_applicationInited = true;
// The first branch above already built all of the window-dependent
// objects. Mark _sceondInited so we don't fall into the recovery
// branch below and leak a second copy of surface/swapChain/etc.
_sceondInited = true;
}
else if (!_sceondInited) {
createSurface();
createSwapChain();
createImageViews();
createRenderPass();
createPipelineLayout();
createGraphicsPipeline();
createFramebuffers();
_sceondInited = true;
}
}
void Application::cleanupSecondInit()
{
vkDestroySwapchainKHR(device, swapChain, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
for (auto imageView : swapChainImageViews) {
vkDestroyImageView(device, imageView, nullptr);
}
for (auto framebuffer : swapChainFramebuffers)
{
if (framebuffer != VK_NULL_HANDLE)
{
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
}
swapChainFramebuffers.clear();
_sceondInited = false;
}
void Application::cleanupForWindowLost()
{
if (!_applicationInited || !_sceondInited) {
FACE_DBG_LOG("cleanupForWindowLost: skip (_applicationInited=%d _sceondInited=%d)",
(int)_applicationInited, (int)_sceondInited);
return;
}
FACE_DBG_LOG("cleanupForWindowLost: begin (imageCount=%zu MAX_FRAMES_IN_FLIGHT=%d extent=%ux%u format=%d)",
swapChainImages.size(), MAX_FRAMES_IN_FLIGHT,
swapChainExtent.width, swapChainExtent.height, (int)swapChainImageFormat);
// Snapshot before we destroy the swapchain so reinitForNewWindow() can
// decide whether the new swapchain needs renderPass / pipelines rebuilt.
_prevSwapChainExtent = swapChainExtent;
_prevSwapChainImageFormat = swapChainImageFormat;
// Block until GPU is no longer using any of the resources we are about
// to destroy. Anything weaker than this risks hitting VK_ERROR_DEVICE_LOST
// on drivers that don't tolerate destroying in-flight resources.
vkDeviceWaitIdle(device);
for (auto framebuffer : swapChainFramebuffers) {
if (framebuffer != VK_NULL_HANDLE) {
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
}
swapChainFramebuffers.clear();
for (auto imageView : swapChainImageViews) {
if (imageView != VK_NULL_HANDLE) {
vkDestroyImageView(device, imageView, nullptr);
}
}
swapChainImageViews.clear();
// Free the command buffers allocated from commandPool. The pool itself is
// kept alive so textures/staging uploads that run concurrently off the
// render thread (via commandPool_ex) aren't disrupted.
if (!commandBuffers.empty()) {
vkFreeCommandBuffers(device, commandPool,
(uint32_t)commandBuffers.size(), commandBuffers.data());
commandBuffers.clear();
}
for (size_t i = 0; i < imageAvailableSemaphores.size(); ++i) {
if (imageAvailableSemaphores[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
}
}
imageAvailableSemaphores.clear();
for (size_t i = 0; i < renderFinishedSemaphores.size(); ++i) {
if (renderFinishedSemaphores[i] != VK_NULL_HANDLE) {
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
}
}
renderFinishedSemaphores.clear();
for (size_t i = 0; i < inFlightFences.size(); ++i) {
if (inFlightFences[i] != VK_NULL_HANDLE) {
vkDestroyFence(device, inFlightFences[i], nullptr);
}
}
inFlightFences.clear();
imagesInFlight.clear();
if (swapChain != VK_NULL_HANDLE) {
vkDestroySwapchainKHR(device, swapChain, nullptr);
swapChain = VK_NULL_HANDLE;
}
swapChainImages.clear();
if (surface != VK_NULL_HANDLE) {
vkDestroySurfaceKHR(instance, surface, nullptr);
surface = VK_NULL_HANDLE;
}
currentFrame = 0;
_sceondInited = false;
FACE_DBG_LOG("cleanupForWindowLost: done");
}
bool Application::reinitForNewWindow()
{
if (!_applicationInited) {
FACE_DBG_LOG("reinitForNewWindow: skip, _applicationInited=0 (must go through initVulkan first)");
return false;
}
if (_sceondInited) {
FACE_DBG_LOG("reinitForNewWindow: skip, already _sceondInited=1");
return false;
}
FACE_DBG_LOG("reinitForNewWindow: begin (prev extent=%ux%u format=%d)",
_prevSwapChainExtent.width, _prevSwapChainExtent.height,
(int)_prevSwapChainImageFormat);
createSurface();
createSwapChain();
const bool extentChanged = (swapChainExtent.width != _prevSwapChainExtent.width) ||
(swapChainExtent.height != _prevSwapChainExtent.height);
const bool formatChanged = (swapChainImageFormat != _prevSwapChainImageFormat);
const bool swapchainIncompatible = extentChanged || formatChanged;
if (formatChanged) {
// renderPass bakes in swapChainImageFormat, so it must be rebuilt
// when the surface chose a different format. The old Application
// graphicsPipeline is tied to the old renderPass, so destroy it
// first to make the handle invalidation explicit.
FACE_DBG_LOG("reinitForNewWindow: format changed (%d -> %d), rebuilding renderPass",
(int)_prevSwapChainImageFormat, (int)swapChainImageFormat);
if (graphicsPipeline != VK_NULL_HANDLE) {
vkDestroyPipeline(device, graphicsPipeline, nullptr);
graphicsPipeline = VK_NULL_HANDLE;
}
if (pipelineLayout != VK_NULL_HANDLE) {
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
pipelineLayout = VK_NULL_HANDLE;
}
if (renderPass != VK_NULL_HANDLE) {
vkDestroyRenderPass(device, renderPass, nullptr);
renderPass = VK_NULL_HANDLE;
}
createRenderPass();
createPipelineLayout();
createGraphicsPipeline();
} else if (extentChanged) {
// renderPass is still fine, but the Application pipeline has a static
// viewport baked to the old extent and must be rebuilt.
FACE_DBG_LOG("reinitForNewWindow: extent changed (%ux%u -> %ux%u), rebuilding graphicsPipeline",
_prevSwapChainExtent.width, _prevSwapChainExtent.height,
swapChainExtent.width, swapChainExtent.height);
if (graphicsPipeline != VK_NULL_HANDLE) {
vkDestroyPipeline(device, graphicsPipeline, nullptr);
graphicsPipeline = VK_NULL_HANDLE;
}
createGraphicsPipeline();
}
createImageViews();
createFramebuffers();
createCommandBuffer();
createSyncObjects();
_sceondInited = true;
FACE_DBG_LOG("reinitForNewWindow: done (imageCount=%zu extent=%ux%u format=%d MAX_FRAMES_IN_FLIGHT=%d swapchainIncompatible=%d)",
swapChainImages.size(), swapChainExtent.width, swapChainExtent.height,
(int)swapChainImageFormat, MAX_FRAMES_IN_FLIGHT, (int)swapchainIncompatible);
return swapchainIncompatible;
}
void Application::createImageViews() {
swapChainImageViews.resize(swapChainImages.size());
for (size_t i = 0; i < swapChainImages.size(); i++) {
VkImageViewCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
createInfo.image = swapChainImages[i];
createInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
createInfo.format = swapChainImageFormat;
createInfo.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
createInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
createInfo.subresourceRange.baseMipLevel = 0;
createInfo.subresourceRange.levelCount = 1;
createInfo.subresourceRange.baseArrayLayer = 0;
createInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &createInfo, nullptr, &swapChainImageViews[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create image views!");
}
}
}
void Application::createFramebuffers() {
swapChainFramebuffers.resize(swapChainImageViews.size());
for (size_t i = 0; i < swapChainImageViews.size(); i++) {
VkImageView attachments[] = {
swapChainImageViews[i]
};
VkFramebufferCreateInfo framebufferInfo{};
framebufferInfo.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
framebufferInfo.renderPass = renderPass;
framebufferInfo.attachmentCount = 1;
framebufferInfo.pAttachments = attachments;
framebufferInfo.width = swapChainExtent.width;
framebufferInfo.height = swapChainExtent.height;
framebufferInfo.layers = 1;
if (vkCreateFramebuffer(device, &framebufferInfo, nullptr, &swapChainFramebuffers[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create framebuffer!");
}
}
}
void Application::mainLoop()
{
#ifdef _WIN32
static int testFrame = 0;
while (!glfwWindowShouldClose(window))
{
auto cur_time = getCurrentTimeMillis();
glfwPollEvents();
auto functionTime = (cur_time - _lastDrawFrameTime);
_lastDrawFrameTime = getCurrentTimeMillis();
drawFrame(functionTime); // 在这里调用
if (testFrame++ == 300) {
break;
}
}
#else
#endif
vkDeviceWaitIdle(device); // 等待设备空闲
}
void Application::createLogicalDevice() {
QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface);
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
std::set<uint32_t> uniqueQueueFamilies = { indices.graphicsFamily.value(), indices.presentFamily.value() };
float queuePriority = 1.0f;
for (uint32_t queueFamily : uniqueQueueFamilies) {
VkDeviceQueueCreateInfo queueCreateInfo{};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfo.pQueuePriorities = &queuePriority;
queueCreateInfos.push_back(queueCreateInfo);
}
VkPhysicalDeviceFeatures deviceFeatures{};
VkDeviceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();
createInfo.pEnabledFeatures = &deviceFeatures;
// 启用交换链扩展
const std::vector<const char*> deviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
createInfo.enabledExtensionCount = static_cast<uint32_t>(deviceExtensions.size());
createInfo.ppEnabledExtensionNames = deviceExtensions.data();
if (vkCreateDevice(physicalDevice, &createInfo, nullptr, &device) != VK_SUCCESS) {
throw std::runtime_error("failed to create logical device!");
}
vkGetDeviceQueue(device, indices.graphicsFamily.value(), 0, &graphicsQueue);
vkGetDeviceQueue(device, indices.presentFamily.value(), 0, &presentQueue);
}
void Application::createSwapChain() {
// 选择交换链格式、颜色空间和分辨率
VkSurfaceFormatKHR surfaceFormat = { VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR };
VkPresentModeKHR presentMode = VK_PRESENT_MODE_FIFO_KHR;
VkSurfaceCapabilitiesKHR surface_capabilities{};
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(this->physicalDevice, this->surface, &surface_capabilities);
VkExtent2D extent = surface_capabilities.currentExtent;
uint32_t imageCount = 2;
// 创建交换链
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = 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;
QueueFamilyIndices indices = findQueueFamilies(physicalDevice, surface);
uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };
if (indices.graphicsFamily != indices.presentFamily) {
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIndices;
}
else {
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
createInfo.queueFamilyIndexCount = 0; // Optional
createInfo.pQueueFamilyIndices = nullptr; // Optional
}
createInfo.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
createInfo.presentMode = presentMode;
createInfo.clipped = VK_TRUE;
createInfo.oldSwapchain = VK_NULL_HANDLE;
if (vkCreateSwapchainKHR(device, &createInfo, nullptr, &swapChain) != VK_SUCCESS) {
throw std::runtime_error("failed to create swap chain!");
}
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, nullptr);
swapChainImages.resize(imageCount);
vkGetSwapchainImagesKHR(device, swapChain, &imageCount, swapChainImages.data());
swapChainImageFormat = surfaceFormat.format;
swapChainExtent = extent;
MAX_FRAMES_IN_FLIGHT = imageCount;
}
void Application::createPipelineLayout() {
VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 0; // 可选:描述符集布局
pipelineLayoutInfo.pSetLayouts = nullptr; // 可选:描述符集布局
pipelineLayoutInfo.pushConstantRangeCount = 0; // 可选:推送常量范围
pipelineLayoutInfo.pPushConstantRanges = nullptr; // 可选:推送常量范围
if (vkCreatePipelineLayout(device, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("failed to create pipeline layout!");
}
}
void Application::createGraphicsPipeline() {
// 顶点着色器和片段着色器
auto vertShaderCode = readFile("shaders/simple_shader.vert.spv");
auto fragShaderCode = readFile("shaders/simple_shader.frag.spv");
// 创建着色器模块
VkShaderModule vertShaderModule = createShaderModule(this->device, vertShaderCode);
VkShaderModule fragShaderModule = createShaderModule(this->device, fragShaderCode);
// 定义着色器阶段
VkPipelineShaderStageCreateInfo vertShaderStageInfo{};
vertShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
vertShaderStageInfo.stage = VK_SHADER_STAGE_VERTEX_BIT;
vertShaderStageInfo.module = vertShaderModule;
vertShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo fragShaderStageInfo{};
fragShaderStageInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
fragShaderStageInfo.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
fragShaderStageInfo.module = fragShaderModule;
fragShaderStageInfo.pName = "main";
VkPipelineShaderStageCreateInfo shaderStages[] = { vertShaderStageInfo, fragShaderStageInfo };
// 顶点输入状态
VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexInputInfo.vertexBindingDescriptionCount = 0;
vertexInputInfo.pVertexBindingDescriptions = nullptr;
vertexInputInfo.vertexAttributeDescriptionCount = 0;
vertexInputInfo.pVertexAttributeDescriptions = nullptr;
// 输入组装状态
VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputAssembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
inputAssembly.primitiveRestartEnable = VK_FALSE;
// 视口状态
VkViewport viewport{};
viewport.x = 0.0f;
viewport.y = 0.0f;
viewport.width = (float)swapChainExtent.width;
viewport.height = (float)swapChainExtent.height;
viewport.minDepth = 0.0f;
viewport.maxDepth = 1.0f;
VkRect2D scissor{};
scissor.offset = { 0, 0 };
scissor.extent = swapChainExtent;
VkPipelineViewportStateCreateInfo viewportState{};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.pViewports = &viewport;
viewportState.scissorCount = 1;
viewportState.pScissors = &scissor;
// 光栅化状态
VkPipelineRasterizationStateCreateInfo rasterizer{};
rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizer.depthClampEnable = VK_FALSE;
rasterizer.rasterizerDiscardEnable = VK_FALSE;
rasterizer.polygonMode = VK_POLYGON_MODE_FILL;
rasterizer.lineWidth = 1.0f;
rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
rasterizer.depthBiasEnable = VK_FALSE;
// 多重采样状态
VkPipelineMultisampleStateCreateInfo multisampling{};
multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampling.sampleShadingEnable = VK_FALSE;
multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
// 颜色混合状态
VkPipelineColorBlendAttachmentState colorBlendAttachment{};
colorBlendAttachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
colorBlendAttachment.blendEnable = VK_FALSE;
VkPipelineColorBlendStateCreateInfo colorBlending{};
colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlending.logicOpEnable = VK_FALSE;
colorBlending.attachmentCount = 1;
colorBlending.pAttachments = &colorBlendAttachment;
// 创建图形管线
VkGraphicsPipelineCreateInfo pipelineInfo{};
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages;
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;
if (vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS) {
throw std::runtime_error("failed to create graphics pipeline!");
}
// 销毁着色器模块
vkDestroyShaderModule(device, fragShaderModule, nullptr);
vkDestroyShaderModule(device, vertShaderModule, nullptr);
}
void Application::createRenderPass() {
VkAttachmentDescription colorAttachment{};
colorAttachment.format = swapChainImageFormat;
colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
throw std::runtime_error("failed to create render pass!");
}
}
void Application::createCommandPool() {
QueueFamilyIndices queueFamilyIndices = findQueueFamilies(physicalDevice, surface);
VkCommandPoolCreateInfo poolInfo{};
poolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
poolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; // 添加标志
if (vkCreateCommandPool(device, &poolInfo, nullptr, &commandPool) != VK_SUCCESS) {
throw std::runtime_error("failed to create command pool!");
}
queueFamilyIndices = findQueueFamilies(physicalDevice, surface);
VkCommandPoolCreateInfo poolInfo_ex{};
poolInfo_ex.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
poolInfo_ex.queueFamilyIndex = queueFamilyIndices.graphicsFamily.value();
poolInfo_ex.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; // 添加标志
if (vkCreateCommandPool(device, &poolInfo_ex, nullptr, &commandPool_ex) != VK_SUCCESS) {
throw std::runtime_error("failed to create command pool!");
}
}
void Application::createCommandBuffer()
{
commandBuffers.resize(MAX_FRAMES_IN_FLIGHT);
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.commandPool = commandPool;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandBufferCount = (uint32_t)MAX_FRAMES_IN_FLIGHT;
if (vkAllocateCommandBuffers(device, &allocInfo, commandBuffers.data()) != VK_SUCCESS) {
throw std::runtime_error("failed to allocate command buffers!");
}
}
void Application::createSyncObjects()
{
imageAvailableSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
renderFinishedSemaphores.resize(MAX_FRAMES_IN_FLIGHT);
inFlightFences.resize(MAX_FRAMES_IN_FLIGHT);
imagesInFlight.resize(MAX_FRAMES_IN_FLIGHT, VK_NULL_HANDLE);
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 (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
if (vkCreateSemaphore(device, &semaphoreInfo, nullptr, &imageAvailableSemaphores[i]) != VK_SUCCESS ||
vkCreateSemaphore(device, &semaphoreInfo, nullptr, &renderFinishedSemaphores[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create synchronization objects!");
}
if (vkCreateFence(device, &fenceInfo, nullptr, &inFlightFences[i]) != VK_SUCCESS) {
throw std::runtime_error("failed to create synchronization objects!");
}
}
}
void Application::recordCommandBuffer(VkCommandBuffer commandBuffer, uint32_t imageIndex, long long frameTime)
{
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0; // 可选标志
beginInfo.pInheritanceInfo = nullptr; // 仅用于次级命令缓冲区
if (vkBeginCommandBuffer(commandBuffer, &beginInfo) != VK_SUCCESS) {
throw std::runtime_error("failed to begin recording command buffer!");
}
// 开始渲染流程
VkRenderPassBeginInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
renderPassInfo.renderPass = renderPass;
renderPassInfo.framebuffer = swapChainFramebuffers[imageIndex]; // 使用正确的imageIndex
renderPassInfo.renderArea.offset = { 0, 0 };
renderPassInfo.renderArea.extent = swapChainExtent;
VkClearValue clearColor = { {{0.0f, 0.0f, 0.0f, 1.0f}} };
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(commandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
render(commandBuffer, frameTime);
// 结束渲染流程
vkCmdEndRenderPass(commandBuffer);
if (vkEndCommandBuffer(commandBuffer) != VK_SUCCESS) {
throw std::runtime_error("failed to record command buffer!");
}
}
void Application::render(VkCommandBuffer commandBuffer, long long frameTime)
{
// 绑定图形管线
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, graphicsPipeline);
// 绘制三角形
vkCmdDraw(commandBuffer, 3, 1, 0, 0);
}
void Application::drawFrame(long long frameTime)
{
static uint64_t s_drawFrameCount = 0;
++s_drawFrameCount;
// 1. 等待前一帧完成
VkResult waitRes = vkWaitForFences(device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
if (waitRes != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("drawFrame.waitFences",
"drawFrame[%llu] vkWaitForFences -> %d (%s) currentFrame=%u",
(unsigned long long)s_drawFrameCount,
(int)waitRes, VkResultStr(waitRes), currentFrame);
#endif
}
// 2. 获取交换链图像
uint32_t imageIndex;
VkResult result = vkAcquireNextImageKHR(device, swapChain, UINT64_MAX, imageAvailableSemaphores[currentFrame], VK_NULL_HANDLE, &imageIndex);
if (result != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("drawFrame.acquire",
"drawFrame[%llu] vkAcquireNextImageKHR -> %d (%s) currentFrame=%u",
(unsigned long long)s_drawFrameCount,
(int)result, VkResultStr(result), currentFrame);
#endif
throw std::runtime_error("failed to acquire swap chain image!");
}
// 3. 检查该图像是否正在被之前的帧使用
if (imagesInFlight[imageIndex] != VK_NULL_HANDLE) {
vkWaitForFences(device, 1, &imagesInFlight[imageIndex], VK_TRUE, UINT64_MAX);
}
imagesInFlight[imageIndex] = inFlightFences[currentFrame];
// 4. 重置栅栏
vkResetFences(device, 1, &inFlightFences[currentFrame]);
// 5. 记录命令缓冲区
vkResetCommandBuffer(commandBuffers[currentFrame], 0);
recordCommandBuffer(commandBuffers[currentFrame], imageIndex, frameTime);
// 6. 提交命令缓冲区
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
VkSemaphore waitSemaphores[] = { imageAvailableSemaphores[currentFrame] };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffers[currentFrame];
VkSemaphore signalSemaphores[] = { renderFinishedSemaphores[imageIndex] };
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
// 串行化 graphicsQueue/presentQueue 的 submit 与 present
// 确保与 copyBuffer / updateTexture 等其它线程的队列提交互斥,
// 避免驱动内部 pthread_mutex 在长时间并发下被破坏。
VkResult submitRes;
{
std::lock_guard<std::mutex> poolLock(poolQueueMtx);
submitRes = vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]);
}
if (submitRes != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("drawFrame.submit",
"drawFrame[%llu] vkQueueSubmit -> %d (%s) currentFrame=%u imageIndex=%u",
(unsigned long long)s_drawFrameCount,
(int)submitRes, VkResultStr(submitRes),
currentFrame, imageIndex);
#endif
throw std::runtime_error("failed to submit draw command buffer!");
}
// 7. 呈现图像
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { swapChain };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
{
std::lock_guard<std::mutex> poolLock(poolQueueMtx);
result = vkQueuePresentKHR(presentQueue, &presentInfo);
}
if (result != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("drawFrame.present",
"drawFrame[%llu] vkQueuePresentKHR -> %d (%s) currentFrame=%u imageIndex=%u",
(unsigned long long)s_drawFrameCount,
(int)result, VkResultStr(result),
currentFrame, imageIndex);
#endif
throw std::runtime_error("failed to present swap chain image!");
}
// 8. 前进到下一帧
currentFrame = (currentFrame + 1) % MAX_FRAMES_IN_FLIGHT;
}
void DestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT debugMessenger, const VkAllocationCallbacks* pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, debugMessenger, pAllocator);
}
}
void Application::cleanup() {
if (enableValidationLayers) {
DestroyDebugUtilsMessengerEXT(instance, debugMessenger, nullptr);
}
vkDeviceWaitIdle(device);
for (size_t i = 0; i < MAX_FRAMES_IN_FLIGHT; i++) {
vkDestroySemaphore(device, imageAvailableSemaphores[i], nullptr);
vkDestroySemaphore(device, renderFinishedSemaphores[i], nullptr);
vkDestroyFence(device, inFlightFences[i], nullptr);
}
vkDestroyPipeline(device, graphicsPipeline, nullptr);
vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
vkDestroyRenderPass(device, renderPass, nullptr);
for (auto imageView : swapChainImageViews) {
vkDestroyImageView(device, imageView, nullptr);
}
for (auto framebuffer : swapChainFramebuffers)
{
if (framebuffer != VK_NULL_HANDLE)
{
vkDestroyFramebuffer(device, framebuffer, nullptr);
}
}
swapChainFramebuffers.clear();
vkDestroySwapchainKHR(device, swapChain, nullptr);
vkDestroyDevice(device, nullptr);
vkDestroySurfaceKHR(instance, surface, nullptr);
vkDestroyInstance(instance, nullptr);
#ifdef _WIN32
glfwDestroyWindow(window);
glfwTerminate();
#endif
}
uint32_t Application::findMemoryType(VkPhysicalDevice physicalDevice, uint32_t typeFilter,
VkMemoryPropertyFlags properties)
{
VkPhysicalDeviceMemoryProperties memProperties;
vkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProperties);
for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++)
{
if ((typeFilter & (1 << i)) &&
(memProperties.memoryTypes[i].propertyFlags & properties) == properties)
{
return i;
}
}
throw std::runtime_error("Failed to find suitable memory type!");
}
void Application::loadTexture(std::string path, Texture& tex, bool srgb, VkCommandPool pool)
{
std::vector<unsigned char> data = readFileUnsignedChar(path, false);
std::vector<unsigned char> image;
unsigned w, h;
unsigned error = lodepng::decode(image, w, h, data, LCT_RGBA, 8);
processWithVulkan(image.data(), w, h, w * 4, image.size(), tex, srgb, pool, path);
//return tex;
}
void Application::loadTexture(std::vector<unsigned char>& image_data, size_t image_size, int w, int h, Texture& tex, bool srgb, VkCommandPool pool, std::string path)
{
processWithVulkan(image_data.data(), w, h, w * 4, image_size, tex, srgb, pool, path);
//return tex;
}
#ifdef _WIN32
void Application::loadTextureExample(std::wstring path, Texture& tex, bool srgb, VkCommandPool pool)
{
std::vector<unsigned char> data = readFileUnsignedCharWin32(path, true);
std::vector<unsigned char> image;
unsigned w, h;
unsigned error = lodepng::decode(image, w, h, data, LCT_RGBA, 8);
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
std::string str = converter.to_bytes(path);
processWithVulkan(image.data(), w, h, w * 4, image.size(), tex, srgb, pool, str);
//return tex;
}
#endif // _WIN32
void Application::createTexture(VkDevice device, VkPhysicalDevice physicalDevice, int width, int height, Texture& texture, bool srgb, std::string tex_path, size_t dataSize)
{
texture.width = width;
texture.height = height;
texture.mip_levels = 1;
VkImageCreateInfo imageInfo = {};
imageInfo.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
if (srgb)
{
imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
}
else
{
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;// VK_FORMAT_R8G8B8A8_SRGB; //VK_FORMAT_R8G8B8A8_UNORM;
}
imageInfo.extent.width = width;
imageInfo.extent.height = height;
imageInfo.extent.depth = 1;
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = 1;
imageInfo.samples = VK_SAMPLE_COUNT_1_BIT;
imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL;
imageInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;
imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
if (vkCreateImage(device, &imageInfo, nullptr, &texture.image) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create image!");
}
VkMemoryRequirements memRequirements;
vkGetImageMemoryRequirements(device, texture.image, &memRequirements);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(physicalDevice,
memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
if (vkAllocateMemory(device, &allocInfo, nullptr, &texture.device_memory) != VK_SUCCESS)
{
throw std::runtime_error("Failed to allocate image memory!");
}
std::cout << "vkAllocateMemory device_memory " << tex_path << ": " << texture.device_memory << std::endl;
vkBindImageMemory(device, texture.image, texture.device_memory, 0);
VkImageViewCreateInfo viewInfo = {};
viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
viewInfo.image = texture.image;
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
if (srgb)
{
viewInfo.format = VK_FORMAT_R8G8B8A8_SRGB;
}
else
{
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;// VK_FORMAT_R8G8B8A8_SRGB; // VK_FORMAT_R8G8B8A8_UNORM;
}
viewInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
viewInfo.subresourceRange.baseMipLevel = 0;
viewInfo.subresourceRange.levelCount = 1;
viewInfo.subresourceRange.baseArrayLayer = 0;
viewInfo.subresourceRange.layerCount = 1;
if (vkCreateImageView(device, &viewInfo, nullptr, &texture.view) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create texture image view!");
}
VkSamplerCreateInfo samplerInfo = {};
samplerInfo.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
samplerInfo.magFilter = VK_FILTER_LINEAR;
samplerInfo.minFilter = VK_FILTER_LINEAR;
samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
samplerInfo.anisotropyEnable = VK_FALSE;
samplerInfo.maxAnisotropy = 1.0f;
samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK;
samplerInfo.unnormalizedCoordinates = VK_FALSE;
samplerInfo.compareEnable = VK_FALSE;
samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS;
samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;
samplerInfo.mipLodBias = 0.0f;
samplerInfo.minLod = 0.0f;
samplerInfo.maxLod = 0.0f;
if (vkCreateSampler(device, &samplerInfo, nullptr, &texture.sampler) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create texture sampler!");
}
if (texture.stagingBuffer == nullptr)
{
VkBufferCreateInfo bufferInfo = {};
bufferInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
bufferInfo.size = dataSize;
bufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
bufferInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
if (vkCreateBuffer(device, &bufferInfo, nullptr, &texture.stagingBuffer) != VK_SUCCESS)
{
throw std::runtime_error("Failed to create staging buffer!");
}
}
if (texture.stagingBufferMemory == nullptr)
{
VkMemoryRequirements memRequirements;
vkGetBufferMemoryRequirements(device, texture.stagingBuffer, &memRequirements);
VkMemoryAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
allocInfo.allocationSize = memRequirements.size;
allocInfo.memoryTypeIndex = findMemoryType(physicalDevice,
memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);
if (vkAllocateMemory(device, &allocInfo, nullptr, &texture.stagingBufferMemory) != VK_SUCCESS)
{
throw std::runtime_error("Failed to allocate staging buffer memory!");
}
std::cout << "vkAllocateMemory stagingBufferMemory " << tex_path << ": " << texture.stagingBufferMemory << std::endl;
vkBindBufferMemory(device, texture.stagingBuffer, texture.stagingBufferMemory, 0);
}
texture.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
texture.device = device;
texture.texture_path = tex_path;
}
void Application::processWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& texture, bool srgb, VkCommandPool pool, std::string tex_path)
{
std::unique_lock<std::mutex> lock(createTextureMtx);
if (texture.image == VK_NULL_HANDLE)
{
createTexture(device, physicalDevice, width, height, texture, srgb, tex_path, dataSize);
}
updateTexture(device, physicalDevice, pool, graphicsQueue, data, width, height,
rowStride, dataSize, texture);
}
VkCommandBuffer Application::beginSingleTimeCommands(VkDevice device, VkCommandPool commandPool)
{
VkCommandBufferAllocateInfo allocInfo = {};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = commandPool;
allocInfo.commandBufferCount = 1;
VkCommandBuffer commandBuffer;
vkAllocateCommandBuffers(device, &allocInfo, &commandBuffer);
VkCommandBufferBeginInfo beginInfo = {};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
vkBeginCommandBuffer(commandBuffer, &beginInfo);
return commandBuffer;
}
void Application::endSingleTimeCommands(VkDevice device, VkCommandPool commandPool,
VkQueue queue, VkCommandBuffer commandBuffer)
{
vkEndCommandBuffer(commandBuffer);
VkSubmitInfo submitInfo = {};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &commandBuffer;
vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
vkQueueWaitIdle(queue);
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
}
void Application::createTransferResources()
{
if (m_xferInited) {
#ifndef _WIN32
DebugLog::log("createTransferResources: already inited, skip");
#endif
return;
}
if (commandPool_ex == VK_NULL_HANDLE) {
#ifndef _WIN32
DebugLog::log("createTransferResources: commandPool_ex is null, abort");
#endif
throw std::runtime_error("createTransferResources: commandPool_ex not created yet");
}
VkCommandBufferAllocateInfo allocInfo{};
allocInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
allocInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
allocInfo.commandPool = commandPool_ex;
allocInfo.commandBufferCount = kTransferSlotCount;
if (vkAllocateCommandBuffers(device, &allocInfo, m_xferCmd) != VK_SUCCESS) {
throw std::runtime_error("createTransferResources: vkAllocateCommandBuffers failed");
}
// fence 创建为 SIGNALED:第一次 runTransferCommand 的 vkWaitForFences
// 会立刻返回,避免冷启动卡顿。
VkFenceCreateInfo fenceInfo{};
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
for (uint32_t i = 0; i < kTransferSlotCount; ++i) {
if (vkCreateFence(device, &fenceInfo, nullptr, &m_xferFence[i]) != VK_SUCCESS) {
for (uint32_t j = 0; j < i; ++j) {
vkDestroyFence(device, m_xferFence[j], nullptr);
m_xferFence[j] = VK_NULL_HANDLE;
}
vkFreeCommandBuffers(device, commandPool_ex, kTransferSlotCount, m_xferCmd);
for (uint32_t j = 0; j < kTransferSlotCount; ++j) m_xferCmd[j] = VK_NULL_HANDLE;
throw std::runtime_error("createTransferResources: vkCreateFence failed");
}
}
m_xferIdx = 0;
m_xferInited = true;
#ifndef _WIN32
DebugLog::log("createTransferResources: done, slots=%u pool=commandPool_ex",
(unsigned)kTransferSlotCount);
#endif
}
void Application::destroyTransferResources()
{
if (!m_xferInited) {
return;
}
// 调用方必须保证 GPU 已 idle 且没有线程正在 runTransferCommand。
// 这里再加一道 m_xferMtx,串行化潜在的最后一次 transfer。
std::lock_guard<std::mutex> xferLock(m_xferMtx);
for (uint32_t i = 0; i < kTransferSlotCount; ++i) {
if (m_xferFence[i] != VK_NULL_HANDLE) {
vkDestroyFence(device, m_xferFence[i], nullptr);
m_xferFence[i] = VK_NULL_HANDLE;
}
}
if (m_xferCmd[0] != VK_NULL_HANDLE && commandPool_ex != VK_NULL_HANDLE) {
vkFreeCommandBuffers(device, commandPool_ex, kTransferSlotCount, m_xferCmd);
}
for (uint32_t i = 0; i < kTransferSlotCount; ++i) {
m_xferCmd[i] = VK_NULL_HANDLE;
}
m_xferIdx = 0;
m_xferInited = false;
#ifndef _WIN32
DebugLog::log("destroyTransferResources: done");
#endif
}
void Application::runTransferCommand(const std::function<void(VkCommandBuffer)>& record)
{
if (!m_xferInited) {
#ifndef _WIN32
DebugLog::log_throttled("runTransferCommand.notInited",
"runTransferCommand: not inited, skip");
#endif
return;
}
// 串行化所有 transfer 调用:
// - 保证 m_xferIdx 推进 / m_xferCmd[idx] 录制 / m_xferFence[idx] 等待
// 形成一组原子动作;
// - 同时也保证 commandPool_ex 的「外部同步」语义(同一时刻只允许一个
// 线程对它做 record/reset)。
std::lock_guard<std::mutex> xferLock(m_xferMtx);
const uint32_t idx = m_xferIdx;
VkFence fence = m_xferFence[idx];
VkCommandBuffer cmd = m_xferCmd[idx];
// 等上一次该 slot 的提交真正完成。fence 是独立同步对象,不需要持
// poolQueueMtx 就可以等待,drawFrame 的 submit 不会被阻塞。
VkResult wr = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
if (wr != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("runTransferCommand.wait",
"runTransferCommand: vkWaitForFences slot=%u -> %d",
idx, (int)wr);
#endif
}
vkResetFences(device, 1, &fence);
vkResetCommandBuffer(cmd, 0);
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
if (vkBeginCommandBuffer(cmd, &beginInfo) != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("runTransferCommand.begin",
"runTransferCommand: vkBeginCommandBuffer slot=%u failed", idx);
#endif
return;
}
// 调用方在这里 record 命令:vkCmdCopyBuffer / vkCmdCopyBufferToImage /
// transitionImageLayout 等。这些是纯 record 操作,在 m_xferMtx 持有
// 且 cmd 独占的前提下不需要再额外加锁。
record(cmd);
if (vkEndCommandBuffer(cmd) != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("runTransferCommand.end",
"runTransferCommand: vkEndCommandBuffer slot=%u failed", idx);
#endif
return;
}
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &cmd;
// vkQueueSubmit 必须和 drawFrame 的 vkQueueSubmit / vkQueuePresentKHR
// 互斥(队列要求外部同步)。其它步骤只占 m_xferMtx 即可。
{
std::lock_guard<std::mutex> poolLock(poolQueueMtx);
VkResult sr = vkQueueSubmit(graphicsQueue, 1, &submitInfo, fence);
if (sr != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("runTransferCommand.submit",
"runTransferCommand: vkQueueSubmit slot=%u -> %d",
idx, (int)sr);
#endif
}
}
// ★ 关键:调用方代码(FaceApp::uploadVertexData / Application::updateTexture
// 在 runTransferCommand 之后会立刻 vkMapMemory + memcpy 覆写共享的
// staging buffer,去做下一次拷贝。所以本接口必须像旧的 vkQueueWaitIdle
// 一样保证 GPU **已读完** staging 才能返回,否则覆写会 race 上 GPU
// 还在执行的 vkCmdCopyBuffer / vkCmdCopyBufferToImage,导致顶点 / 纹理
// 数据被错位拼接(外观就是模型畸形 / 纹理花屏)。
//
// 这里只等自己这一次的 fence,不像旧实现 vkQueueWaitIdle 那样等整个
// graphicsQueue(包括 drawFrame 的提交),所以不会拖慢渲染主路径。
//
// 不持 poolQueueMtxfence 是独立同步对象,等它不需要外部互斥。
VkResult er = vkWaitForFences(device, 1, &fence, VK_TRUE, UINT64_MAX);
if (er != VK_SUCCESS) {
#ifndef _WIN32
DebugLog::log_throttled("runTransferCommand.endWait",
"runTransferCommand: end vkWaitForFences slot=%u -> %d",
idx, (int)er);
#endif
}
m_xferIdx = (idx + 1) % kTransferSlotCount;
}
void Application::updateTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VkCommandPool commandPool, VkQueue queue,
uint8_t* data, int width, int height,
int rowStride, size_t dataSize, Texture& texture)
{
//VkBuffer stagingBuffer;
//VkDeviceMemory stagingBufferMemory;
void* mappedData;
vkMapMemory(device, texture.stagingBufferMemory, 0, dataSize, 0, &mappedData);
if (rowStride == width * 4)
{
memcpy(mappedData, data, dataSize);
}
else
{
uint8_t* dst = static_cast<uint8_t*>(mappedData);
const uint8_t* src = data;
size_t dstRowStride = width * 4;
for (int y = 0; y < height; y++)
{
memcpy(dst, src, dstRowStride);
dst += dstRowStride;
src += rowStride;
}
}
vkUnmapMemory(device, texture.stagingBufferMemory);
// 走 runTransferCommand:复用 commandPool_ex 上预分配的 cmdbuf + fence
// 不再每帧 vkAllocate/vkFree,也不再 vkQueueWaitIdle。
// 注意:传入的 commandPool / queue 参数保留只是为了兼容旧接口,
// 实际命令池一律换成 commandPool_ex(与渲染主管线物理隔离)。
(void)commandPool;
(void)queue;
runTransferCommand([&](VkCommandBuffer commandBuffer) {
transitionImageLayout(commandBuffer, texture.image,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL);
VkBufferImageCopy region = {};
region.bufferOffset = 0;
region.bufferRowLength = 0;
region.bufferImageHeight = 0;
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
region.imageSubresource.mipLevel = 0;
region.imageSubresource.baseArrayLayer = 0;
region.imageSubresource.layerCount = 1;
region.imageOffset = { 0, 0, 0 };
region.imageExtent = { static_cast<uint32_t>(width),
static_cast<uint32_t>(height), 1 };
vkCmdCopyBufferToImage(commandBuffer, texture.stagingBuffer, texture.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);
transitionImageLayout(commandBuffer, texture.image,
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
});
texture.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
}
void Application::transitionImageLayout(VkCommandBuffer commandBuffer, VkImage image,
VkImageLayout oldLayout, VkImageLayout newLayout)
{
VkImageMemoryBarrier barrier = {};
barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
barrier.oldLayout = oldLayout;
barrier.newLayout = newLayout;
barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
barrier.image = image;
barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
barrier.subresourceRange.baseMipLevel = 0;
barrier.subresourceRange.levelCount = 1;
barrier.subresourceRange.baseArrayLayer = 0;
barrier.subresourceRange.layerCount = 1;
VkPipelineStageFlags sourceStage;
VkPipelineStageFlags destinationStage;
if (oldLayout == VK_IMAGE_LAYOUT_UNDEFINED &&
newLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL)
{
barrier.srcAccessMask = 0;
barrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
sourceStage = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
destinationStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
}
else if (oldLayout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL &&
newLayout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL)
{
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
sourceStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
destinationStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
}
else
{
throw std::invalid_argument("Unsupported layout transition!");
}
vkCmdPipelineBarrier(commandBuffer, sourceStage, destinationStage, 0,
0, nullptr, 0, nullptr, 1, &barrier);
}
void Application::destroy_texture(Texture texture)
{
vkDestroyImageView(device, texture.view, nullptr);
vkDestroyImage(device, texture.image, nullptr);
vkDestroySampler(device, texture.sampler, nullptr);
vkFreeMemory(device, texture.device_memory, nullptr);
}