Outputs: Defensive checks, and don't wait for a Metal drawable twice in a frame

- Metal: when a layer has no drawable to give (it can take a second to
  say so), don't ask again in the same frame for every pass to it.
- Swapchains other than the primary are created, resized and destroyed
  between frames (Metal asserts it), and destroyed with the last
  reference to them (ReleaseAssertLast, Metal and Vulkan).
- BindFramebufferSamplesAsTexture asserts it isn't given a swapchain,
  like BindFramebufferAsTexture.
- An offscreen Metal swapchain can't be resized, asserted.
- VulkanRenderManager asserts that no additional swapchain outlives it.
- Native_RemoveOutputAndWait asserts it isn't called on the thread that
  handles the requests, where it would always time out.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-10-22 23:08:52 +02:00
1 parent ba55ee7e5f
commit da33eab776
6 files changed
+26 -3

No files matched your search

+4
View File
@@ -217,6 +217,8 @@ static uint64_t g_outputRequestsQueued = 0;
static uint64_t g_outputRequestsDone = 0;
// Whether the outputs have swapchains, between NativeInitGraphics and NativeShutdownGraphics.
static bool g_outputsLive = false;
// The thread that handles the requests, which mustn't wait for them.
static std::thread::id g_outputRequestThread;
static std::atomic<int> g_nextOutputId{1};
static void ProcessOutputRequests() {
@@ -236,6 +238,7 @@ static void ProcessOutputRequests() {
}
std::lock_guard<std::mutex> guard(g_outputRequestLock);
g_outputRequestsDone = queued;
g_outputRequestThread = std::this_thread::get_id();
g_outputRequestsDoneCond.notify_all();
}
@@ -2098,6 +2101,7 @@ void Native_RemoveOutput(int id) {
bool Native_RemoveOutputAndWait(int id) {
Native_RemoveOutput(id);
std::unique_lock<std::mutex> lock(g_outputRequestLock);
_dbg_assert_msg_(std::this_thread::get_id() != g_outputRequestThread, "Waiting for output requests on the thread that handles them");
const uint64_t queued = g_outputRequestsQueued;
// Without a device, the outputs have no swapchains to let go of.
const bool done = g_outputRequestsDoneCond.wait_for(lock, std::chrono::seconds(3), [=] {