修复clash的问题

This commit is contained in:
xsl
2026-04-25 16:42:20 +08:00
parent 2066b3b124
commit 50680ee854
8 changed files with 893 additions and 64 deletions
+222 -26
View File
@@ -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 的提交),所以不会拖慢渲染主路径。
//
// 不持 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,
@@ -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, &region);
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, &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;
}