@@ -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,11 @@ void GPUDisplayBackendVulkan::ExitBackendA()
13991403 }
14001404 clearTextureSampler ();
14011405 clearDevice ();
1406+ mCommandInfrastructureCreated = false ;
1407+ mImageCount = 0 ;
1408+ mLastPresentId = 0 ;
1409+ mSwapChainSuboptimal = 0 ;
1410+ mFrameSkipped = false ;
14021411}
14031412
14041413// ---------------------------- USER CODE ----------------------------
@@ -1419,10 +1428,14 @@ void GPUDisplayBackendVulkan::loadDataToGPU(size_t totalVertizes)
14191428{
14201429 mDevice .waitIdle ();
14211430 clearVertexBuffers ();
1422- mVBO = createBuffer (totalVertizes * sizeof (mDisplay ->vertexBuffer ()[0 ][0 ]), mDisplay ->vertexBuffer ()[0 ].data (), vk::BufferUsageFlagBits::eVertexBuffer, 1 );
1431+ if (totalVertizes) { // Zero-size buffers are invalid (empty event)
1432+ mVBO = createBuffer (totalVertizes * sizeof (mDisplay ->vertexBuffer ()[0 ][0 ]), mDisplay ->vertexBuffer ()[0 ].data (), vk::BufferUsageFlagBits::eVertexBuffer, 1 );
1433+ }
14231434 if (mDisplay ->cfgR ().useGLIndirectDraw ) {
14241435 fillIndirectCmdBuffer ();
1425- mIndirectCommandBuffer = createBuffer (mCmdBuffer .size () * sizeof (mCmdBuffer [0 ]), mCmdBuffer .data (), vk::BufferUsageFlagBits::eIndirectBuffer, 1 );
1436+ if (mCmdBuffer .size ()) {
1437+ mIndirectCommandBuffer = createBuffer (mCmdBuffer .size () * sizeof (mCmdBuffer [0 ]), mCmdBuffer .data (), vk::BufferUsageFlagBits::eIndirectBuffer, 1 );
1438+ }
14261439 mCmdBuffer .clear ();
14271440 }
14281441 needRecordCommandBuffers ();
@@ -1456,25 +1469,27 @@ uint32_t GPUDisplayBackendVulkan::drawVertices(const vboList& v, const drawType
14561469 return count;
14571470}
14581471
1459- void GPUDisplayBackendVulkan::prepareDraw (const hmm_mat4& proj, const hmm_mat4& view, bool requestScreenshot, bool toMixBuffer, float includeMixImage)
1472+ bool GPUDisplayBackendVulkan::prepareDraw (const hmm_mat4& proj, const hmm_mat4& view, bool requestScreenshot, bool toMixBuffer, float includeMixImage)
14601473{
14611474 if (mDisplay ->updateDrawCommands () || toMixBuffer || includeMixImage > 0 ) {
14621475 needRecordCommandBuffers ();
14631476 }
14641477
1478+ if (includeMixImage > 0 .f && mFrameSkipped ) {
1479+ return false ; // The render to the mix buffer of this frame did not get an image
1480+ }
14651481 if (includeMixImage == 0 .f ) {
14661482 mCurrentFrame = (mCurrentFrame + 1 ) % mFramesInFlight ;
14671483 waitTimeline (mFrameTimelineValue [mCurrentFrame ]);
14681484 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
14691485 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) {
1486+ handleSwapChainResult (retVal, 1 );
1487+ if (retVal != vk::Result::eTimeout && retVal != vk::Result::eErrorOutOfDateKHR) {
14731488 CHKERR (retVal);
14741489 }
14751490 }
1476- auto getImage = [&]() {
1477- return mDevice .acquireNextImageKHR (mSwapChain , UINT64_MAX , mImageAvailableSemaphore [mCurrentFrame ], VkFence (VK_NULL_HANDLE ), &mCurrentImageIndex );
1491+ auto getImage = [&]() { // Timeout so that a hidden window cannot block the thread (which may also dispatch the window system events) forever
1492+ return mDevice .acquireNextImageKHR (mSwapChain , 100'000'000 , mImageAvailableSemaphore [mCurrentFrame ], VkFence (VK_NULL_HANDLE ), &mCurrentImageIndex );
14781493 };
14791494
14801495 vk::Result retVal = vk::Result::eSuccess;
@@ -1491,8 +1506,13 @@ void GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4&
14911506 if (mustUpdateSwapChain) {
14921507 recreateRendering (requestScreenshot, toMixBuffer);
14931508 retVal = getImage ();
1509+ mMustUpdateSwapChain = false ;
1510+ }
1511+ handleSwapChainResult (retVal, 2 );
1512+ mFrameSkipped = retVal == vk::Result::eTimeout || retVal == vk::Result::eNotReady || retVal == vk::Result::eErrorOutOfDateKHR;
1513+ if (mFrameSkipped ) {
1514+ return false ;
14941515 }
1495- mMustUpdateSwapChain = retVal == vk::Result::eSuboptimalKHR;
14961516 CHKERR (retVal);
14971517 waitTimeline (mImageTimelineValue [mCurrentImageIndex ]); // Previous frame using this image's resources (command buffers, uniform buffer, ...) finished
14981518 if (mPresentFences .size ()) { // Previous present of this image done, its present semaphore can be signaled again
@@ -1519,6 +1539,20 @@ void GPUDisplayBackendVulkan::prepareDraw(const hmm_mat4& proj, const hmm_mat4&
15191539 if (!mCommandBufferUpToDate [mCurrentImageIndex ]) {
15201540 startFillCommandBuffer (mCurrentCommandBuffer , mCurrentImageIndex , toMixBuffer);
15211541 }
1542+ return true ;
1543+ }
1544+
1545+ void GPUDisplayBackendVulkan::handleSwapChainResult (vk::Result retVal, uint8_t suboptimalBit)
1546+ {
1547+ if (retVal == vk::Result::eErrorOutOfDateKHR || (retVal == vk::Result::eSuboptimalKHR && !(mSwapChainSuboptimal & suboptimalBit))) {
1548+ mMustUpdateSwapChain = true ;
1549+ if (retVal == vk::Result::eSuboptimalKHR) {
1550+ mSwapChainSuboptimal |= suboptimalBit;
1551+ }
1552+ }
1553+ if (retVal == vk::Result::eSuccess && !mMustUpdateSwapChain ) {
1554+ mSwapChainSuboptimal &= ~suboptimalBit;
1555+ }
15221556}
15231557
15241558void GPUDisplayBackendVulkan::finishDraw (bool doScreenshot, bool toMixBuffer, float includeMixImage)
@@ -1588,10 +1622,13 @@ void GPUDisplayBackendVulkan::finishFrame(bool doScreenshot, bool toMixBuffer, f
15881622 presentFenceInfo = vk::SwapchainPresentFenceInfoKHR{1 , &mPresentFences [mCurrentImageIndex ], presentInfo.pNext };
15891623 presentInfo.pNext = &presentFenceInfo;
15901624 }
1591- vk::Result retVal = mGraphicsQueue .presentKHR (&presentInfo);
1592- if (retVal == vk::Result::eErrorOutOfDateKHR || retVal == vk::Result::eSuboptimalKHR) {
1593- mMustUpdateSwapChain = true ;
1594- } else {
1625+ vk::Result retVal;
1626+ {
1627+ std::lock_guard<std::mutex> guard (mQueueMutex );
1628+ retVal = mGraphicsQueue .presentKHR (&presentInfo);
1629+ }
1630+ handleSwapChainResult (retVal, 4 );
1631+ if (retVal != vk::Result::eErrorOutOfDateKHR) {
15951632 CHKERR (retVal);
15961633 }
15971634 }
@@ -1740,7 +1777,7 @@ void GPUDisplayBackendVulkan::pointSizeFactor(float factor)
17401777 if (mCommandBufferUpToDate [mCurrentImageIndex ]) {
17411778 return ;
17421779 }
1743- float size = mDisplay ->cfgL ().pointSize * mDownsampleFactor * factor + (mDisplay ->cfgL ().smoothPoints ? SMOOTH_POINT_EXTRA_SIZE : 0 .f );
1780+ float size = std::min ( mDisplay ->cfgL ().pointSize * mDownsampleFactor * factor + (mDisplay ->cfgL ().smoothPoints ? SMOOTH_POINT_EXTRA_SIZE : 0 .f ), mMaxPointSize );
17441781 mCurrentCommandBuffer .pushConstants (mPipelineLayout , vk::ShaderStageFlagBits::eVertex, sizeof (std::array<float , 4 >), sizeof (size), &size);
17451782}
17461783
@@ -1761,11 +1798,12 @@ void GPUDisplayBackendVulkan::lineWidthFactor(float factor)
17611798 }
17621799 float width = mDisplay ->cfgL ().lineWidth * mDownsampleFactor * factor;
17631800 if (useSmoothLineShader ()) {
1764- width += SMOOTH_LINE_EXTRA_WIDTH ;
1801+ const float expand = mStrictLines ? 1 .f : 1 .4143f ; // Non-strict wide lines may be parallelograms, widen so that the perpendicular width is covered
1802+ width = std::min (width + SMOOTH_LINE_EXTRA_WIDTH , mMaxLineWidth / expand); // The shader must see the width that is actually rasterized
17651803 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
1804+ width *= expand;
17671805 }
1768- mCurrentCommandBuffer .setLineWidth (width);
1806+ mCurrentCommandBuffer .setLineWidth (std::min ( width, mMaxLineWidth ) );
17691807}
17701808
17711809void GPUDisplayBackendVulkan::needRecordCommandBuffers ()
0 commit comments