@@ -571,6 +571,8 @@ void GPUDisplayBackendVulkan::createDevice()
571571 GPUInfo (" Using physical Vulkan device %s" , &deviceProperties.deviceName [0 ]);
572572 mMaxMSAAsupported = getMaxUsableSampleCount (deviceProperties);
573573 mStrictLines = deviceProperties.limits .strictLines ;
574+ mMaxLineWidth = deviceProperties.limits .lineWidthRange [1 ];
575+ mMaxPointSize = deviceProperties.limits .pointSizeRange [1 ];
574576 mMaxRenderSize = std::min ({deviceProperties.limits .maxImageDimension2D , deviceProperties.limits .maxFramebufferWidth , deviceProperties.limits .maxFramebufferHeight });
575577 mZSupported = (bool )(depth32FormatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment);
576578 mStencilSupported = (bool )(depth64FormatProperties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eDepthStencilAttachment);
@@ -1196,7 +1198,9 @@ void GPUDisplayBackendVulkan::startFillCommandBuffer(vk::CommandBuffer& commandB
11961198 cmdBeginRendering (commandBuffer, mRenderWidth , mRenderHeight , colorAttachment, mZActive ? &depthAttachment : nullptr );
11971199
11981200 vk::DeviceSize offsets[] = {0 };
1199- commandBuffer.bindVertexBuffers (0 , 1 , &mVBO .buffer , offsets);
1201+ if (mVBO .size ) {
1202+ commandBuffer.bindVertexBuffers (0 , 1 , &mVBO .buffer , offsets);
1203+ }
12001204 cmdPushDescriptors (commandBuffer, mPipelineLayout , mUniformBuffersMat [0 ][imageIndex]);
12011205 const float viewport[2 ] = {(float )mRenderWidth , (float )mRenderHeight };
12021206 commandBuffer.pushConstants (mPipelineLayout , vk::ShaderStageFlagBits::eVertex, 6 * sizeof (float ), sizeof (viewport), viewport);
@@ -1399,6 +1403,10 @@ void GPUDisplayBackendVulkan::ExitBackendA()
13991403 }
14001404 clearTextureSampler ();
14011405 clearDevice ();
1406+ mCommandInfrastructureCreated = false ; // Recreated by the next InitBackendA
1407+ mImageCount = 0 ;
1408+ mLastPresentId = 0 ;
1409+ mSwapChainSuboptimal = mFrameSkipped = false ;
14021410}
14031411
14041412// ---------------------------- USER CODE ----------------------------
@@ -1419,10 +1427,14 @@ void GPUDisplayBackendVulkan::loadDataToGPU(size_t totalVertizes)
14191427{
14201428 mDevice .waitIdle ();
14211429 clearVertexBuffers ();
1422- mVBO = createBuffer (totalVertizes * sizeof (mDisplay ->vertexBuffer ()[0 ][0 ]), mDisplay ->vertexBuffer ()[0 ].data (), vk::BufferUsageFlagBits::eVertexBuffer, 1 );
1430+ if (totalVertizes) { // Zero-size buffers are invalid (empty event)
1431+ mVBO = createBuffer (totalVertizes * sizeof (mDisplay ->vertexBuffer ()[0 ][0 ]), mDisplay ->vertexBuffer ()[0 ].data (), vk::BufferUsageFlagBits::eVertexBuffer, 1 );
1432+ }
14231433 if (mDisplay ->cfgR ().useGLIndirectDraw ) {
14241434 fillIndirectCmdBuffer ();
1425- mIndirectCommandBuffer = createBuffer (mCmdBuffer .size () * sizeof (mCmdBuffer [0 ]), mCmdBuffer .data (), vk::BufferUsageFlagBits::eIndirectBuffer, 1 );
1435+ if (mCmdBuffer .size ()) {
1436+ mIndirectCommandBuffer = createBuffer (mCmdBuffer .size () * sizeof (mCmdBuffer [0 ]), mCmdBuffer .data (), vk::BufferUsageFlagBits::eIndirectBuffer, 1 );
1437+ }
14261438 mCmdBuffer .clear ();
14271439 }
14281440 needRecordCommandBuffers ();
@@ -1456,25 +1468,27 @@ uint32_t GPUDisplayBackendVulkan::drawVertices(const vboList& v, const drawType
14561468 return count;
14571469}
14581470
1459- void GPUDisplayBackendVulkan::prepareDraw (const hmm_mat4& proj, const hmm_mat4& view, bool requestScreenshot, bool toMixBuffer, float includeMixImage)
1471+ bool GPUDisplayBackendVulkan::prepareDraw (const hmm_mat4& proj, const hmm_mat4& view, bool requestScreenshot, bool toMixBuffer, float includeMixImage)
14601472{
14611473 if (mDisplay ->updateDrawCommands () || toMixBuffer || includeMixImage > 0 ) {
14621474 needRecordCommandBuffers ();
14631475 }
14641476
1477+ if (includeMixImage > 0 .f && mFrameSkipped ) {
1478+ return false ; // The render to the mix buffer of this frame did not get an image
1479+ }
14651480 if (includeMixImage == 0 .f ) {
14661481 mCurrentFrame = (mCurrentFrame + 1 ) % mFramesInFlight ;
14671482 waitTimeline (mFrameTimelineValue [mCurrentFrame ]);
14681483 if (mLastPresentId > 1 && fpsLimitMode () == FPS_LIMIT_VSYNC ) { // Refresh rate limit (FIFO): at most one frame queued (wait for the second-last present), keeps the rate if a frame misses the deadline, timeout in case the window is hidden
14691484 const vk::Result retVal = (vk::Result)VULKAN_HPP_DEFAULT_DISPATCHER .vkWaitForPresentKHR (static_cast <VkDevice>(mDevice ), static_cast <VkSwapchainKHR>(mSwapChain ), mLastPresentId - 1 , 100'000'000 );
1470- if (retVal == vk::Result::eErrorOutOfDateKHR || retVal == vk::Result::eSuboptimalKHR) {
1471- mMustUpdateSwapChain = true ;
1472- } else if (retVal != vk::Result::eTimeout) {
1485+ handleSwapChainResult (retVal);
1486+ if (retVal != vk::Result::eTimeout && retVal != vk::Result::eErrorOutOfDateKHR) {
14731487 CHKERR (retVal);
14741488 }
14751489 }
1476- auto getImage = [&]() {
1477- return mDevice .acquireNextImageKHR (mSwapChain , UINT64_MAX , mImageAvailableSemaphore [mCurrentFrame ], VkFence (VK_NULL_HANDLE ), &mCurrentImageIndex );
1490+ auto getImage = [&]() { // Timeout so that a hidden window cannot block the thread (which may also dispatch the window system events) forever
1491+ return mDevice .acquireNextImageKHR (mSwapChain , 100'000'000 , mImageAvailableSemaphore [mCurrentFrame ], VkFence (VK_NULL_HANDLE ), &mCurrentImageIndex );
14781492 };
14791493
14801494 vk::Result retVal = vk::Result::eSuccess;
@@ -1492,7 +1506,12 @@ void GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4&
14921506 recreateRendering (requestScreenshot, toMixBuffer);
14931507 retVal = getImage ();
14941508 }
1495- mMustUpdateSwapChain = retVal == vk::Result::eSuboptimalKHR;
1509+ mMustUpdateSwapChain = false ;
1510+ handleSwapChainResult (retVal);
1511+ mFrameSkipped = retVal == vk::Result::eTimeout || retVal == vk::Result::eNotReady;
1512+ if (mFrameSkipped ) {
1513+ return false ;
1514+ }
14961515 CHKERR (retVal);
14971516 waitTimeline (mImageTimelineValue [mCurrentImageIndex ]); // Previous frame using this image's resources (command buffers, uniform buffer, ...) finished
14981517 if (mPresentFences .size ()) { // Previous present of this image done, its present semaphore can be signaled again
@@ -1519,6 +1538,17 @@ void GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4&
15191538 if (!mCommandBufferUpToDate [mCurrentImageIndex ]) {
15201539 startFillCommandBuffer (mCurrentCommandBuffer , mCurrentImageIndex , toMixBuffer);
15211540 }
1541+ return true ;
1542+ }
1543+
1544+ void GPUDisplayBackendVulkan::handleSwapChainResult (vk::Result retVal)
1545+ {
1546+ if (retVal == vk::Result::eErrorOutOfDateKHR || (retVal == vk::Result::eSuboptimalKHR && !mSwapChainSuboptimal )) {
1547+ mMustUpdateSwapChain = true ;
1548+ }
1549+ if (retVal == vk::Result::eSuboptimalKHR || retVal == vk::Result::eSuccess) {
1550+ mSwapChainSuboptimal = retVal == vk::Result::eSuboptimalKHR;
1551+ }
15221552}
15231553
15241554void GPUDisplayBackendVulkan::finishDraw (bool doScreenshot, bool toMixBuffer, float includeMixImage)
@@ -1588,10 +1618,13 @@ void GPUDisplayBackendVulkan::finishFrame(bool doScreenshot, bool toMixBuffer, f
15881618 presentFenceInfo = vk::SwapchainPresentFenceInfoKHR{1 , &mPresentFences [mCurrentImageIndex ], presentInfo.pNext };
15891619 presentInfo.pNext = &presentFenceInfo;
15901620 }
1591- vk::Result retVal = mGraphicsQueue .presentKHR (&presentInfo);
1592- if (retVal == vk::Result::eErrorOutOfDateKHR || retVal == vk::Result::eSuboptimalKHR) {
1593- mMustUpdateSwapChain = true ;
1594- } else {
1621+ vk::Result retVal;
1622+ {
1623+ std::lock_guard<std::mutex> guard (mQueueMutex );
1624+ retVal = mGraphicsQueue .presentKHR (&presentInfo);
1625+ }
1626+ handleSwapChainResult (retVal);
1627+ if (retVal != vk::Result::eErrorOutOfDateKHR) {
15951628 CHKERR (retVal);
15961629 }
15971630 }
@@ -1740,7 +1773,7 @@ void GPUDisplayBackendVulkan::pointSizeFactor(float factor)
17401773 if (mCommandBufferUpToDate [mCurrentImageIndex ]) {
17411774 return ;
17421775 }
1743- float size = mDisplay ->cfgL ().pointSize * mDownsampleFactor * factor + (mDisplay ->cfgL ().smoothPoints ? SMOOTH_POINT_EXTRA_SIZE : 0 .f );
1776+ float size = std::min ( mDisplay ->cfgL ().pointSize * mDownsampleFactor * factor + (mDisplay ->cfgL ().smoothPoints ? SMOOTH_POINT_EXTRA_SIZE : 0 .f ), mMaxPointSize );
17441777 mCurrentCommandBuffer .pushConstants (mPipelineLayout , vk::ShaderStageFlagBits::eVertex, sizeof (std::array<float , 4 >), sizeof (size), &size);
17451778}
17461779
@@ -1761,11 +1794,12 @@ void GPUDisplayBackendVulkan::lineWidthFactor(float factor)
17611794 }
17621795 float width = mDisplay ->cfgL ().lineWidth * mDownsampleFactor * factor;
17631796 if (useSmoothLineShader ()) {
1764- width += SMOOTH_LINE_EXTRA_WIDTH ;
1797+ const float expand = mStrictLines ? 1 .f : 1 .4143f ; // Non-strict wide lines may be parallelograms, widen so that the perpendicular width is covered
1798+ width = std::min (width + SMOOTH_LINE_EXTRA_WIDTH , mMaxLineWidth / expand); // The shader must see the width that is actually rasterized
17651799 mCurrentCommandBuffer .pushConstants (mPipelineLayout , vk::ShaderStageFlagBits::eVertex, 5 * sizeof (float ), sizeof (width), &width);
1766- width *= mStrictLines ? 1 . f : 1 . 4143f ; // Non-strict wide lines may be parallelograms, widen so that the perpendicular width is covered
1800+ width *= expand;
17671801 }
1768- mCurrentCommandBuffer .setLineWidth (width);
1802+ mCurrentCommandBuffer .setLineWidth (std::min ( width, mMaxLineWidth ) );
17691803}
17701804
17711805void GPUDisplayBackendVulkan::needRecordCommandBuffers ()
0 commit comments