From dc1f9899489917d3fab0fb87de85cfec5c6b6403 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Nov 2026 08:35:50 +0000 Subject: [PATCH 1/3] Initial plan From 3f256dbf6da9aaedd2c56e1563977abb8fa6a9dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Nov 2026 08:39:08 +0000 Subject: [PATCH 2/3] Fix WGC full-frame capture resizing Co-authored-by: BANSAFAn <86590991+BANSAFAn@users.noreply.github.com> --- src/platform/windows/capture_backend.zig | 13 +++++++---- src/platform/windows/wgc_bridge.c | 28 ++++++++++++++++++++++-- src/platform/windows/wgc_capture.zig | 20 +++++++++++++++++ src/ui/ui_controller.zig | 2 ++ 4 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/platform/windows/capture_backend.zig b/src/platform/windows/capture_backend.zig index 7b4806b..7efdaf7 100644 --- a/src/platform/windows/capture_backend.zig +++ b/src/platform/windows/capture_backend.zig @@ -145,11 +145,16 @@ pub const CaptureBackend = struct { } pub fn acquireFrame(self: *CaptureBackend, timeout_ms: u32, pts_nanos: u64) !?types.VideoFrame { - switch (self.backend_type) { - .dxgi => return self.dxgi_cap.?.acquireFrame(timeout_ms, pts_nanos), - .wgc => return self.wgc_cap.?.acquireFrame(timeout_ms, pts_nanos), - .game => return self.game_cap.?.acquireFrame(timeout_ms, pts_nanos), + const frame = switch (self.backend_type) { + .dxgi => try self.dxgi_cap.?.acquireFrame(timeout_ms, pts_nanos), + .wgc => try self.wgc_cap.?.acquireFrame(timeout_ms, pts_nanos), + .game => try self.game_cap.?.acquireFrame(timeout_ms, pts_nanos), + }; + if (frame) |f| { + self.width = f.width; + self.height = f.height; } + return frame; } pub fn getD3DDevice(self: *CaptureBackend) *d3d11.ID3D11Device { diff --git a/src/platform/windows/wgc_bridge.c b/src/platform/windows/wgc_bridge.c index 8eb24a7..d45a0a4 100644 --- a/src/platform/windows/wgc_bridge.c +++ b/src/platform/windows/wgc_bridge.c @@ -458,8 +458,32 @@ bool wgc_capture_acquire_frame(WgcCapture* cap, void* dest_texture, uint32_t tim SizeInt32 contentSize = { 0, 0 }; frame->lpVtbl->get_ContentSize(frame, &contentSize); if (contentSize.Width > 0 && contentSize.Height > 0) { - cap->width = (uint32_t)contentSize.Width; - cap->height = (uint32_t)contentSize.Height; + const uint32_t new_width = (uint32_t)contentSize.Width; + const uint32_t new_height = (uint32_t)contentSize.Height; + if (new_width != cap->width || new_height != cap->height) { + SizeInt32 new_size = { (int32_t)new_width, (int32_t)new_height }; + HRESULT recreate_hr = cap->frame_pool->lpVtbl->Recreate( + cap->frame_pool, + cap->winrt_device, + 87, // DirectXPixelFormat_B8G8R8A8UIntNormalized + 2, + new_size + ); + if (FAILED(recreate_hr)) { + fprintf(stderr, "[WGC] FramePool Recreate failed: 0x%08X (new size: %ux%u)\n", (unsigned int)recreate_hr, new_width, new_height); + } + + cap->width = new_width; + cap->height = new_height; + if (out_width) *out_width = cap->width; + if (out_height) *out_height = cap->height; + + frame->lpVtbl->base.Release((IInspectable_Local*)frame); + return false; + } + + cap->width = new_width; + cap->height = new_height; } if (out_width) *out_width = cap->width; if (out_height) *out_height = cap->height; diff --git a/src/platform/windows/wgc_capture.zig b/src/platform/windows/wgc_capture.zig index 8bc1438..0926525 100644 --- a/src/platform/windows/wgc_capture.zig +++ b/src/platform/windows/wgc_capture.zig @@ -26,6 +26,23 @@ pub const WgcCapture = struct { height: u32, frame_count: u64 = 0, + fn recreateDestTexture(self: *WgcCapture, new_w: u32, new_h: u32) !void { + const tex_desc = d3d11.D3D11_TEXTURE2D_DESC{ + .Width = new_w, + .Height = new_h, + .Format = d3d11.DXGI_FORMAT_B8G8R8A8_UNORM, + .Usage = d3d11.D3D11_USAGE_DEFAULT, + .BindFlags = d3d11.D3D11_BIND_RENDER_TARGET | d3d11.D3D11_BIND_SHADER_RESOURCE, + .MiscFlags = 0x2, // D3D11_RESOURCE_MISC_SHARED + }; + + const new_tex = try self.d3d.device.createTexture2D(&tex_desc); + _ = self.dest_texture.release(); + self.dest_texture = new_tex; + self.width = new_w; + self.height = new_h; + } + pub fn isSupported() bool { return wgc_is_supported(); } @@ -117,6 +134,9 @@ pub const WgcCapture = struct { var h: u32 = self.height; const ok = wgc_capture_acquire_frame(self.handle, @ptrCast(self.dest_texture), timeout_ms, &w, &h); + if ((w != self.width or h != self.height) and w > 0 and h > 0) { + try self.recreateDestTexture(w, h); + } if (!ok) return null; self.width = w; diff --git a/src/ui/ui_controller.zig b/src/ui/ui_controller.zig index c589db7..58d7030 100644 --- a/src/ui/ui_controller.zig +++ b/src/ui/ui_controller.zig @@ -364,6 +364,8 @@ pub fn runUi(allocator: std.mem.Allocator, cfg: config.Config, headless_test_ms: const now_ns = clock.MonotonicClock.nowNanos(); if (pb.acquireFrame(2, now_ns)) |maybe_frame| { if (maybe_frame) |frame| { + ui_state.preview_width = @intCast(frame.width); + ui_state.preview_height = @intCast(frame.height); app_window.imgui_update_preview_d3d11(frame.gpu_texture_ptr, @intCast(frame.width), @intCast(frame.height)); } } else |_| {} From fb5181280478922759b054e9b02a361e91c86727 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Nov 2026 08:42:36 +0000 Subject: [PATCH 3/3] Optimize preview copy and harden game frame sizing Co-authored-by: BANSAFAn <86590991+BANSAFAn@users.noreply.github.com> --- src/platform/windows/game_capture.zig | 23 ++++++++++++++++---- src/ui/imgui_bridge.cpp | 31 +++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/src/platform/windows/game_capture.zig b/src/platform/windows/game_capture.zig index 1fa7731..89b4103 100644 --- a/src/platform/windows/game_capture.zig +++ b/src/platform/windows/game_capture.zig @@ -145,16 +145,31 @@ pub const GameCapture = struct { return null; // Timeout / skipped frame } + const ipc = self.ipc_ptr.?; + if (!ipc.active or ipc.shared_handle == 0) return null; + // Check if game resized swapchain or recreated shared texture - if (self.ipc_ptr.?.shared_handle != self.current_shared_handle) { + if (ipc.shared_handle != self.current_shared_handle) { log.info("Game texture resized to {d}x{d}, reopening shared handle...", .{ - self.ipc_ptr.?.width, - self.ipc_ptr.?.height, + ipc.width, + ipc.height, }); try self.reopenSharedTexture(); } - const tex = self.shared_texture orelse return null; + var tex = self.shared_texture orelse return null; + if (ipc.width > 0 and ipc.height > 0 and (ipc.width != self.width or ipc.height != self.height)) { + const tex_desc = tex.getDesc(); + if (tex_desc.Width != ipc.width or tex_desc.Height != ipc.height) { + log.info("Game frame size changed to {d}x{d} with same shared handle, reopening texture...", .{ ipc.width, ipc.height }); + try self.reopenSharedTexture(); + tex = self.shared_texture orelse return null; + } else { + self.width = ipc.width; + self.height = ipc.height; + } + } + self.frame_index += 1; return types.VideoFrame{ diff --git a/src/ui/imgui_bridge.cpp b/src/ui/imgui_bridge.cpp index 3436ba8..c693644 100644 --- a/src/ui/imgui_bridge.cpp +++ b/src/ui/imgui_bridge.cpp @@ -80,6 +80,8 @@ static int g_currentHotkeyPreset = -1; // Live Screen Preview Engine State static ID3D11Texture2D* g_pPreviewTexture = NULL; static ID3D11ShaderResourceView* g_pPreviewSRV = NULL; +static ID3D11Texture2D* g_pPreviewSharedOpenedTexture = NULL; +static HANDLE g_previewSharedHandle = NULL; static int g_previewWidth = 0; static int g_previewHeight = 0; static bool g_previewEnabled = false; @@ -89,6 +91,14 @@ static uint64_t g_previewFrameCount = 0; static CRITICAL_SECTION g_previewCs; static bool g_previewCsInit = false; +static void reset_preview_shared_cache(void) { + if (g_pPreviewSharedOpenedTexture) { + g_pPreviewSharedOpenedTexture->Release(); + g_pPreviewSharedOpenedTexture = NULL; + } + g_previewSharedHandle = NULL; +} + extern "C" { void imgui_set_preview_enabled(bool enabled) { g_previewEnabled = enabled; @@ -125,6 +135,7 @@ void imgui_update_preview_d3d11(void* d3d11_texture, int width, int height) { if (!g_pPreviewTexture || g_previewWidth != req_w || g_previewHeight != req_h) { if (g_pPreviewSRV) { g_pPreviewSRV->Release(); g_pPreviewSRV = NULL; } if (g_pPreviewTexture) { g_pPreviewTexture->Release(); g_pPreviewTexture = NULL; } + reset_preview_shared_cache(); D3D11_TEXTURE2D_DESC desc = {}; desc.Width = (UINT)req_w; @@ -160,13 +171,23 @@ void imgui_update_preview_d3d11(void* d3d11_texture, int width, int height) { if (SUCCEEDED(srcTex->QueryInterface(__uuidof(IDXGIResource), (void**)&dxgiRes)) && dxgiRes) { HANDLE sharedHandle = NULL; if (SUCCEEDED(dxgiRes->GetSharedHandle(&sharedHandle)) && sharedHandle) { - ID3D11Texture2D* openedTex = NULL; - if (SUCCEEDED(g_pd3dDevice->OpenSharedResource(sharedHandle, __uuidof(ID3D11Texture2D), (void**)&openedTex)) && openedTex) { - g_pd3dDeviceContext->CopyResource((ID3D11Resource*)g_pPreviewTexture, (ID3D11Resource*)openedTex); - openedTex->Release(); + if (g_previewSharedHandle != sharedHandle || !g_pPreviewSharedOpenedTexture) { + reset_preview_shared_cache(); + if (FAILED(g_pd3dDevice->OpenSharedResource(sharedHandle, __uuidof(ID3D11Texture2D), (void**)&g_pPreviewSharedOpenedTexture)) || !g_pPreviewSharedOpenedTexture) { + g_previewSharedHandle = NULL; + } else { + g_previewSharedHandle = sharedHandle; + } + } + if (g_pPreviewSharedOpenedTexture) { + g_pd3dDeviceContext->CopyResource((ID3D11Resource*)g_pPreviewTexture, (ID3D11Resource*)g_pPreviewSharedOpenedTexture); } + } else { + reset_preview_shared_cache(); } dxgiRes->Release(); + } else { + reset_preview_shared_cache(); } } g_previewFrameCount++; @@ -1906,6 +1927,7 @@ void imgui_window_render_ui(UiState* state) { if (g_previewCsInit) EnterCriticalSection(&g_previewCs); if (g_pPreviewSRV) { g_pPreviewSRV->Release(); g_pPreviewSRV = NULL; } if (g_pPreviewTexture) { g_pPreviewTexture->Release(); g_pPreviewTexture = NULL; } + reset_preview_shared_cache(); g_previewWidth = 0; g_previewHeight = 0; if (g_previewCsInit) LeaveCriticalSection(&g_previewCs); @@ -1959,6 +1981,7 @@ void imgui_window_destroy(void) { ImGui::DestroyContext(); cleanup_render_target(); + reset_preview_shared_cache(); if (g_pSwapChain) { g_pSwapChain->Release(); g_pSwapChain = NULL; } if (g_pd3dDeviceContext) { g_pd3dDeviceContext->Release(); g_pd3dDeviceContext = NULL; } if (g_pd3dDevice) { g_pd3dDevice->Release(); g_pd3dDevice = NULL; }