修复clash的问题
This commit is contained in:
+222
-26
@@ -110,6 +110,11 @@ void Application::initVulkan()
|
||||
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
|
||||
@@ -790,7 +795,14 @@ void Application::drawFrame(long long frameTime)
|
||||
submitInfo.signalSemaphoreCount = 1;
|
||||
submitInfo.pSignalSemaphores = signalSemaphores;
|
||||
|
||||
VkResult submitRes = vkQueueSubmit(graphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]);
|
||||
// 串行化 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",
|
||||
@@ -810,7 +822,10 @@ void Application::drawFrame(long long frameTime)
|
||||
presentInfo.swapchainCount = 1;
|
||||
presentInfo.pSwapchains = swapChains;
|
||||
presentInfo.pImageIndices = &imageIndex;
|
||||
result = vkQueuePresentKHR(presentQueue, &presentInfo);
|
||||
{
|
||||
std::lock_guard<std::mutex> poolLock(poolQueueMtx);
|
||||
result = vkQueuePresentKHR(presentQueue, &presentInfo);
|
||||
}
|
||||
|
||||
if (result != VK_SUCCESS) {
|
||||
#ifndef _WIN32
|
||||
@@ -1121,6 +1136,184 @@ void Application::endSingleTimeCommands(VkDevice device, VkCommandPool commandPo
|
||||
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 的提交),所以不会拖慢渲染主路径。
|
||||
//
|
||||
// 不持 poolQueueMtx:fence 是独立同步对象,等它不需要外部互斥。
|
||||
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,
|
||||
@@ -1153,33 +1346,36 @@ void Application::updateTexture(VkDevice device, VkPhysicalDevice physicalDevice
|
||||
|
||||
vkUnmapMemory(device, texture.stagingBufferMemory);
|
||||
|
||||
VkCommandBuffer commandBuffer = beginSingleTimeCommands(device, commandPool);
|
||||
// 走 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);
|
||||
|
||||
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 };
|
||||
|
||||
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, ®ion);
|
||||
|
||||
transitionImageLayout(commandBuffer, texture.image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
|
||||
endSingleTimeCommands(device, commandPool, queue, commandBuffer);
|
||||
vkCmdCopyBufferToImage(commandBuffer, texture.stagingBuffer, texture.image,
|
||||
VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "AppBase.h"
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <functional>
|
||||
|
||||
struct Texture
|
||||
{
|
||||
@@ -48,8 +49,68 @@ public:
|
||||
virtual void render(VkCommandBuffer commandBuffer, long long frameTime);
|
||||
virtual bool isInited() { return _applicationInited; }
|
||||
std::mutex createTextureMtx;
|
||||
// 全局 GPU 提交互斥锁:任何对 graphicsQueue / presentQueue 的提交
|
||||
// (vkQueueSubmit / vkQueuePresentKHR / vkQueueWaitIdle)以及共享
|
||||
// commandPool 的 vkAllocateCommandBuffers / vkFreeCommandBuffers
|
||||
// 都必须在持有此锁的期间执行,否则驱动内部维护命令池与队列的
|
||||
// pthread_mutex 在长时间多线程并发下会被破坏(FORTIFY: pthread_mutex_lock
|
||||
// called on a destroyed mutex)。
|
||||
// 需要覆盖的 3 条并发线路:
|
||||
// 1) 渲染线程 drawFrame
|
||||
// 2) processImageNative → updateTexture (single-time commands)
|
||||
// 3) passDataToNative → update_face_vertex_buffer → copyBuffer
|
||||
std::mutex poolQueueMtx;
|
||||
void processWithVulkan(uint8_t* data, int width, int height, int rowStride, size_t dataSize, Texture& texture, bool srgb, VkCommandPool pool, std::string tex_path);
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// 复用式 single-time-command 接口(替代每帧 allocate+submit+waitIdle+free)
|
||||
//
|
||||
// ★ 同步语义(重要):
|
||||
// 本接口 **同步**,返回时 GPU 已经读完 record 里访问的源数据。
|
||||
// 行为上等价于旧的 vkQueueSubmit + vkQueueWaitIdle,但只等自己这次
|
||||
// 提交的 fence,不阻塞渲染线程在 graphicsQueue 上的其它提交。
|
||||
//
|
||||
// 为什么必须同步:调用方 (FaceApp::uploadVertexData /
|
||||
// Application::updateTexture) 紧接着会复用同一个 staging buffer:
|
||||
//
|
||||
// vkMapMemory(staging); memcpy(staging, A); vkUnmapMemory;
|
||||
// runTransferCommand([&](cmd){ vkCmdCopyBuffer(cmd, staging, dstA); });
|
||||
// vkMapMemory(staging); memcpy(staging, B); ← 必须等 dstA 拷完!
|
||||
// vkUnmapMemory;
|
||||
// runTransferCommand([&](cmd){ vkCmdCopyBuffer(cmd, staging, dstB); });
|
||||
//
|
||||
// 如果 runTransferCommand 异步返回,第二次 memcpy 会在 GPU 还没读完
|
||||
// staging 里的 A 时就把它覆盖成 B —— 顶点 / 纹理立刻畸形。
|
||||
//
|
||||
// 行为:
|
||||
// - 共 kTransferSlotCount 个 VkCommandBuffer + 同数 VkFence,
|
||||
// 从 commandPool_ex 一次性分配,在 cleanup 时一次性释放。
|
||||
// - 每次 runTransferCommand:
|
||||
// 1) m_xferMtx.lock() (串行所有 transfer 提交)
|
||||
// 2) 当前 slot 上 vkWaitForFences (等上一次该 slot 的提交完成)
|
||||
// 3) vkResetFences + vkResetCommandBuffer + vkBegin
|
||||
// 4) 调用 record(cmd) 录制命令 (vkCmdCopy* 等)
|
||||
// 5) vkEndCommandBuffer
|
||||
// 6) poolQueueMtx 内 vkQueueSubmit(graphicsQueue, fence)
|
||||
// 7) vkWaitForFences(fence) (★ 等本次 GPU 完成才返回)
|
||||
// 8) m_xferIdx 推进到下一个 slot
|
||||
//
|
||||
// 为什么这样设计:
|
||||
// - 完全消除每帧 vkAllocateCommandBuffers / vkFreeCommandBuffers,
|
||||
// 根除驱动 per-pool mutex 在长时间高频压力下被破坏导致的
|
||||
// FORTIFY: pthread_mutex_lock called on a destroyed mutex 崩溃。
|
||||
// - 用 fence 替代 vkQueueWaitIdle,等待粒度只到自己这一次提交,
|
||||
// 不会 stall 整条 graphicsQueue(包括渲染主路径的提交)。
|
||||
// - 固定使用 commandPool_ex(与渲染主用的 commandPool 物理隔离),
|
||||
// 即使驱动还有内部 contention,也不会波及渲染主管线。
|
||||
//
|
||||
// 调用约束:
|
||||
// - 调用方 **绝不能** 提前持有 poolQueueMtx;本接口内部按需短暂获取。
|
||||
// - record 函数体内只允许 vkCmd*(命令录制)这类操作,不要在里面调
|
||||
// queue / pool 级别的 API(submit / present / pool reset 等)。
|
||||
static constexpr uint32_t kTransferSlotCount = 3;
|
||||
void runTransferCommand(const std::function<void(VkCommandBuffer)>& record);
|
||||
|
||||
protected:
|
||||
|
||||
VkPhysicalDevice physicalDevice = VK_NULL_HANDLE; // 物理设备
|
||||
@@ -104,6 +165,21 @@ public:
|
||||
VkExtent2D _prevSwapChainExtent = {0, 0};
|
||||
VkFormat _prevSwapChainImageFormat = VK_FORMAT_UNDEFINED;
|
||||
|
||||
protected:
|
||||
// Transfer command resources(详见 runTransferCommand 上方注释)。
|
||||
// 命令缓冲来自 commandPool_ex,与渲染主用的 commandPool 物理隔离。
|
||||
VkCommandBuffer m_xferCmd[kTransferSlotCount] = { VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE };
|
||||
VkFence m_xferFence[kTransferSlotCount] = { VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE };
|
||||
uint32_t m_xferIdx = 0;
|
||||
bool m_xferInited = false;
|
||||
std::mutex m_xferMtx;
|
||||
|
||||
// 创建/销毁 transfer 资源。createTransferResources 必须在 createCommandPool
|
||||
// 之后调用;destroyTransferResources 必须在销毁 commandPool_ex 之前调用,
|
||||
// 且 GPU 已 idle(vkDeviceWaitIdle 或确认所有 fence 已 signaled)。
|
||||
void createTransferResources();
|
||||
void destroyTransferResources();
|
||||
|
||||
protected:
|
||||
void loadTexture(std::string path, Texture& tex, bool srgb, VkCommandPool pool);
|
||||
void loadTexture(std::vector<unsigned char>& image_data, size_t image_size, int w, int h, Texture& tex, bool srgb, VkCommandPool pool, std::string path);
|
||||
|
||||
+23
-35
@@ -889,6 +889,12 @@ void FaceApp::onWindowLost()
|
||||
std::unique_lock<std::mutex> lk_point(mtx_point);
|
||||
std::unique_lock<std::mutex> lk_motion(changeMotionMtx);
|
||||
std::unique_lock<std::mutex> lk_tex(createTextureMtx);
|
||||
// 同时阻塞所有并发的 GPU 提交(drawFrame / copyBuffer / updateTexture)。
|
||||
// cleanupForWindowLost() 会执行 vkDeviceWaitIdle 并销毁 swapchain / surface /
|
||||
// semaphores / fences 等窗口相关资源,如果此时有其它线程正在 vkQueueSubmit
|
||||
// 或 vkQueuePresentKHR,会触发 FORTIFY: pthread_mutex_lock called on a
|
||||
// destroyed mutex。这里持锁确保销毁与提交是互斥的。
|
||||
std::unique_lock<std::mutex> lk_pool(poolQueueMtx);
|
||||
|
||||
Application::cleanupForWindowLost();
|
||||
_secondfaceAppInited = false;
|
||||
@@ -1053,41 +1059,20 @@ void FaceApp::update_face_vertex_buffer(float* pos, int pointCount)
|
||||
|
||||
void FaceApp::copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size)
|
||||
{
|
||||
VkCommandBuffer commandBuffer = beginSingleTimeCommands();
|
||||
|
||||
VkBufferCopy copyRegion = {};
|
||||
copyRegion.size = size;
|
||||
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);
|
||||
|
||||
endSingleTimeCommands(commandBuffer);
|
||||
}
|
||||
|
||||
VkCommandBuffer FaceApp::beginSingleTimeCommands() {
|
||||
VkCommandBufferAllocateInfo allocInfo = { 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 = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
||||
beginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
|
||||
|
||||
vkBeginCommandBuffer(commandBuffer, &beginInfo);
|
||||
return commandBuffer;
|
||||
}
|
||||
|
||||
void FaceApp::endSingleTimeCommands(VkCommandBuffer commandBuffer) {
|
||||
vkEndCommandBuffer(commandBuffer);
|
||||
|
||||
VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
||||
submitInfo.commandBufferCount = 1;
|
||||
submitInfo.pCommandBuffers = &commandBuffer;
|
||||
|
||||
vkQueueSubmit(graphicsQueue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||
vkQueueWaitIdle(graphicsQueue);
|
||||
vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer);
|
||||
// 改造说明:
|
||||
// 旧实现是 vkAllocate(commandPool) + vkBegin + vkCmdCopyBuffer + vkEnd +
|
||||
// vkQueueSubmit(graphicsQueue) + vkQueueWaitIdle + vkFree(commandPool)
|
||||
// 每帧 update_face_vertex_buffer 会调两次 copyBuffer(顶点 + 索引),
|
||||
// 长期高频 allocate/free 会把驱动 per-pool mutex 玩坏,触发
|
||||
// FORTIFY: pthread_mutex_lock called on a destroyed mutex。
|
||||
//
|
||||
// 现在改走基类的 runTransferCommand,复用 commandPool_ex 上预分配的
|
||||
// 3 个 cmdbuf + fence,并发安全由 m_xferMtx + poolQueueMtx 联合保证。
|
||||
runTransferCommand([&](VkCommandBuffer commandBuffer) {
|
||||
VkBufferCopy copyRegion = {};
|
||||
copyRegion.size = size;
|
||||
vkCmdCopyBuffer(commandBuffer, srcBuffer, dstBuffer, 1, ©Region);
|
||||
});
|
||||
}
|
||||
|
||||
void FaceApp::createUniformBuffer()
|
||||
@@ -1458,6 +1443,9 @@ void FaceApp::cleanup()
|
||||
//}
|
||||
|
||||
destroyTexture(device, tex_bg);
|
||||
// 必须在销毁 commandPool_ex 之前释放从它分配的 transfer cmdbuf + fence。
|
||||
// vkDeviceWaitIdle 已在本函数开头调过,提交不会再有 in-flight。
|
||||
destroyTransferResources();
|
||||
vkDestroyCommandPool(device, commandPool, nullptr);
|
||||
vkDestroyCommandPool(device, commandPool_ex, nullptr);
|
||||
Application::cleanup();
|
||||
|
||||
@@ -112,8 +112,6 @@ private:
|
||||
const bool kThick = false;
|
||||
|
||||
void copyBuffer(VkBuffer srcBuffer, VkBuffer dstBuffer, VkDeviceSize size);
|
||||
VkCommandBuffer beginSingleTimeCommands();
|
||||
void endSingleTimeCommands(VkCommandBuffer commandBuffer);
|
||||
|
||||
// 顶点缓冲区相关
|
||||
VkBuffer m_vertexBuffer = VK_NULL_HANDLE;
|
||||
|
||||
Reference in New Issue
Block a user