Conversation
Frames drawn while forceRedrawFramesWithoutSwap_ > 1 never reach glfwSwapBuffers, so everything put into the main framebuffer in them is thrown away. Skip sceneTexture_->draw() and the main framebuffer clear in such frames, and fill the background inside the scene texture only when the scene is actually rendered. captureUIScreenShot() used to read the back buffer of whatever frame had been drawn last, which is no longer complete; it now draws one swapping frame and reads it right before the swap. Its pixel buffer was also allocated as size.x * size.x instead of size.x * size.y. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
preDrawSignal() may draw, so clearFramebuffers() must stay in front of it; instead skip preDrawSignal() itself in a frame that is not shown, together with drawScene(), postDrawSignal() and the scene texture. ShadowsGL binds its framebuffer in preDraw_ and consumes it in postDraw_, so the two signals have to be skipped as a pair. ImGuiMenu::finishFrame() can still force the swap of a frame drawFull() has already skipped; such a frame is now held back and one more is drawn. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The scene texture outlives the frame it is rendered in - a later visible frame draws it without rendering the scene again, and it can be read - so nothing on its path is conditional any more, and it is rendered whenever the scene is dirty rather than only in a frame that is swapped. Only what goes into the main framebuffer is skipped in a frame that is not shown: its clearFramebuffers() and sceneTexture_->draw(). Without a scene texture the scene is drawn right in the main framebuffer, so there both draw signals and drawScene() are skipped too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Only the frames that reach
glfwSwapBuffersare ever seen:so everything such a frame puts in the main framebuffer is thrown away. The scene texture is the opposite: it outlives the frame, a later visible frame draws it without rendering the scene again, and it can be read. So nothing on its path is conditional, and it is now rendered whenever the scene is dirty rather than only in a frame that is swapped — the texture is current in every frame.
What a non-swapped frame skips:
clearFramebuffers()of the main framebuffer;sceneTexture_->draw();preDrawSignal(),drawScene()andpostDrawSignal()as well. The two signals are skipped as a pair becauseShadowsGLbinds its framebuffer inpreDraw_and consumes it inpostDraw_.preDrawSignal()may draw, so bothclearFramebuffers()keep their place in front of it, andrenderSceneis still decided after it.Rendering the scene earlier does not add work in the common case:
drawScene()ends inresetRedraw_(), so a scene dirtied once during a run of non-swapped frames is rendered once either way, just in the first frame of the run instead of the last. It does render more than before when something dirties the scene on every frame of such a run.One frame that could become visible after being skipped
ImGuiMenu::finishFrame()callsforceSwapOnFrame()when a secondary ImGui viewport is about to disappear — afterdrawFull()has already decided the frame is invisible. Such a frame would be presented with an uncleared main framebuffer, so it is now held back (swapped = falseplus one more forced frame) instead.captureUIScreenShotIt read the back buffer of whatever frame had been drawn last, which is no longer a complete image. It now draws one frame, forced to swap, and reads it in a callback fired just before
glfwSwapBuffers— a defined back buffer, rather than relying on the contents surviving a swap as it did whenever the previous frame had been swapped. Its pixel buffer was also allocated assize.x * size.xrather thansize.x * size.y, a heap overflow for any capture area taller than it is wide.Not done
ImGui_ImplOpenGL3_RenderDrawData()inImGuiMenu::finishFrame()is the remaining invisible GPU work in a non-swapped frame, but since ImGui 1.92 it is also what applies pendingImTextureDataupdates, so it cannot simply be skipped.🤖 Generated with Claude Code