提问人:Whosdatdev 提问时间:11/12/2023 更新时间:11/15/2023 访问量:86
通过 OBS 查看时,使用 vulkan 闪烁/损坏渲染的纹理四边形
Textured quads rendered using vulkan flicker/corrupt, when viewed through OBS
问:
我正在使用 vulkan 实现一个基本的 2D 游戏引擎。我想从 XNA/MonoGame 重新实现 SpriteBatch,所以基本上我想绘制大量带有单独转换(旋转、平移、缩放)的纹理四边形。
我的应用程序可以正常工作,但有两个问题:
- 通过OBS查看时,某些地方会出现一些闪烁或图像“损坏”
- 在低端硬件上,应用程序会立即崩溃,因为当有超过几百次绘制时,会返回
vkQueueSubmit
DEVICE_LOST
这看起来符合预期。通过OBS查看测试应用,你会看到这样:
在高端硬件上,应用程序每隔几个小时就会随机崩溃一次()。就在崩溃之前,窗口上显示的图像短暂地看起来像始终可以通过 OBS 观察到的图像(闪烁、损坏)。DEVICE_LOST
验证层未报告任何错误或警告。我最初的怀疑是我搞砸了同步,导致较慢的硬件崩溃的可能性更高。
我的代码基于 Sascha Willems vulkan 教程 (https://vulkan-tutorial.com/) (尽管经过大量改编)。这是帧外观的(大幅缩小)版本(我保留了所有 vulkan 命令和同步代码):
vkWaitForFences(instance.Device, 1, &inFlightFences[currentFrame], VK_TRUE, UINT64_MAX);
auto aquireImageResult = vkAcquireNextImageKHR(instance.Device, swapChain.SwapChain, UINT64_MAX,
imageAvailableSemaphore[currentFrame], VK_NULL_HANDLE,
&imageIndex);
if (aquireImageResult == VK_ERROR_OUT_OF_DATE_KHR || aquireImageResult == VK_SUBOPTIMAL_KHR)
{
return ...
}
if (aquireImageResult != VK_SUCCESS)
{
return GenericFailure;
}
if (inFlightImages[imageIndex] != VK_NULL_HANDLE)
vkWaitForFences(instance.Device, 1, &inFlightImages[imageIndex], VK_TRUE, UINT64_MAX);
inFlightImages[imageIndex] = inFlightFences[currentFrame];
vkResetFences(instance.Device, 1, &inFlightFences[currentFrame]);
// We re-record command buffers every frame
auto& rootCommandBuffer = instance.CommandBuffers[imageIndex];
auto& frameBuffer = instance.FrameBuffers[imageIndex];
VkCommandBufferBeginInfo beginInfo{};
beginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
beginInfo.flags = 0;
beginInfo.pInheritanceInfo = nullptr;
vkResetCommandBuffer(rootCommandBuffer, 0); // This is done implicitly on vkBeginCommandBuffer because of the flags on the command pool, but better do it explicitly
if (vkBeginCommandBuffer(rootCommandBuffer, &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 = instance.RenderPass;
renderPassInfo.framebuffer = frameBuffer;
renderPassInfo.renderArea.offset = { 0, 0 };
renderPassInfo.renderArea.extent = swapChain.Extent;
renderPassInfo.clearValueCount = 1;
renderPassInfo.pClearValues = &clearColor;
vkCmdBeginRenderPass(rootCommandBuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
// "Draws" are submitted in C# land - a struct of (image, trans_matrix, color) is written into a host_coherent buffer for each draw.
// Draw is being done with instanceCount = _numberOfDraws, geometrie is created in vertex shader (just a quad)
// Basically, these two commands are executed exaclty once per frame:
// Start C#
var setsToBeBound = stackalloc VkDescriptorSet[2] { _bufferDescriptorSet, _imageDescriptorSet };
VulkanNative.vkCmdBindDescriptorSets(_vkCommandBuffer, VkPipelineBindPoint.VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineHandle.Pipeline->VkPipelineLayout, 0, 2, setsToBeBound, 0, null);
VulkanNative.vkCmdDraw(_vkCommandBuffer, 4, (uint)_numberOfDraws, 0, 0);
// End C#
vkCmdEndRenderPass(rootCommandBuffer);
if (vkEndCommandBuffer(rootCommandBuffer) != VK_SUCCESS)
throw std::runtime_error("Failed to end command buffer recording.");
VkSemaphore waitSemaphores[] = { imageAvailableSemaphore[currentFrame] };
VkPipelineStageFlags waitStages[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
VkSemaphore signalSemaphores[] = { renderFinishedSemaphore[currentFrame] };
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = waitSemaphores;
submitInfo.pWaitDstStageMask = waitStages;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = signalSemaphores;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &rootCommandBuffer;
auto queueSubmitResult = vkQueueSubmit(instance.GraphicsQueue, 1, &submitInfo, inFlightFences[currentFrame]);
if (queueSubmitResult != VK_SUCCESS)
throw std::runtime_error(std::string("Failed to submit draw command buffer. Code: ") + std::to_string(queueSubmitResult)); // Here a crash can happen with code DEVICE_LOST
VkPresentInfoKHR presentInfo{};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.waitSemaphoreCount = 1;
presentInfo.pWaitSemaphores = signalSemaphores;
VkSwapchainKHR swapChains[] = { swapChain.SwapChain };
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = swapChains;
presentInfo.pImageIndices = &imageIndex;
presentInfo.pResults = nullptr; // Optional
auto presentResult = vkQueuePresentKHR(instance.PresentQueue, &presentInfo);
if (presentResult == VK_ERROR_OUT_OF_DATE_KHR || presentResult == VK_SUBOPTIMAL_KHR)
{
instance.PendingResize = true;
}
else if (presentResult != VK_SUCCESS)
{
throw ...
}
currentFrame = (currentFrame + 1) % maxFramesInFlight;
让我质疑我的理智的是,即使我在帧的末尾/开头插入 a,错误仍然会发生(因此,由于整个应用程序支持多帧飞行设计,这似乎不是一个错误)。vkQueueWaitIdle
答: 暂无答案
评论