Fix WGC cleanup - capture start_free_threaded() return value
Some checks failed
Validate / validate (push) Failing after 9s
Some checks failed
Validate / validate (push) Failing after 9s
The critical bug was not capturing the return value from start_free_threaded(), which returns a CaptureControl object with the proper wait() method. Changes: - Store CaptureControl from start_free_threaded() return value - Use CaptureControl.wait() to block until thread finishes (not timeout) - Remove redundant capture_control storage from frame callback - Cleanup now properly releases GPU resources and yellow border Tested: Yellow border now disappears immediately after capture test ends. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -92,10 +92,6 @@ class WGCEngine(CaptureEngine):
|
||||
try:
|
||||
logger.debug("WGC frame callback triggered")
|
||||
|
||||
# Store capture_control reference for cleanup
|
||||
if self._capture_control is None:
|
||||
self._capture_control = capture_control
|
||||
|
||||
# Get frame buffer as numpy array
|
||||
frame_buffer = frame.frame_buffer
|
||||
width = frame.width
|
||||
@@ -126,8 +122,9 @@ class WGCEngine(CaptureEngine):
|
||||
self._capture_instance.closed_handler = on_closed
|
||||
|
||||
# Start capture using free-threaded mode (non-blocking)
|
||||
# IMPORTANT: start_free_threaded() returns a CaptureControl object for cleanup
|
||||
logger.debug("Starting WGC capture (free-threaded mode)...")
|
||||
self._capture_instance.start_free_threaded()
|
||||
self._capture_control = self._capture_instance.start_free_threaded()
|
||||
|
||||
# Wait for first frame to arrive (with timeout)
|
||||
logger.debug("Waiting for first WGC frame...")
|
||||
@@ -152,48 +149,43 @@ class WGCEngine(CaptureEngine):
|
||||
|
||||
def cleanup(self) -> None:
|
||||
"""Cleanup WGC resources."""
|
||||
# For free-threaded captures, cleanup is tricky:
|
||||
# 1. Stop capture via capture_control if available
|
||||
# 2. Explicitly delete the capture instance (releases COM objects)
|
||||
# 3. Wait for resources to be freed (give Windows time to cleanup)
|
||||
# 4. Force garbage collection multiple times to ensure COM cleanup
|
||||
# Proper cleanup for free-threaded captures:
|
||||
# 1. Stop capture via CaptureControl.stop() (signals thread to stop)
|
||||
# 2. Wait for thread to finish using CaptureControl.wait() (blocks until done)
|
||||
# 3. Delete capture instance (releases COM objects)
|
||||
# 4. Force garbage collection (ensures COM cleanup)
|
||||
|
||||
if self._capture_control:
|
||||
try:
|
||||
logger.debug("Stopping WGC capture session via capture_control...")
|
||||
logger.debug("Stopping WGC capture thread...")
|
||||
self._capture_control.stop()
|
||||
|
||||
logger.debug("Waiting for WGC capture thread to finish...")
|
||||
# This will block until the capture thread actually finishes
|
||||
# This is the CORRECT way to wait for cleanup (not a timeout!)
|
||||
self._capture_control.wait()
|
||||
logger.debug("WGC capture thread finished successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Error stopping WGC capture_control: {e}")
|
||||
logger.error(f"Error during WGC capture control cleanup: {e}", exc_info=True)
|
||||
finally:
|
||||
self._capture_control = None
|
||||
|
||||
# Explicitly delete the capture instance BEFORE waiting
|
||||
# This is critical for releasing COM objects and GPU resources
|
||||
# Now that the thread has stopped, delete the capture instance
|
||||
if self._capture_instance:
|
||||
try:
|
||||
logger.debug("Explicitly deleting WGC capture instance...")
|
||||
logger.debug("Deleting WGC capture instance...")
|
||||
instance = self._capture_instance
|
||||
self._capture_instance = None
|
||||
del instance
|
||||
logger.debug("WGC capture instance deleted")
|
||||
except Exception as e:
|
||||
logger.error(f"Error releasing WGC capture instance: {e}")
|
||||
logger.error(f"Error deleting WGC capture instance: {e}", exc_info=True)
|
||||
self._capture_instance = None
|
||||
|
||||
# Force garbage collection multiple times to ensure COM cleanup
|
||||
# COM objects may require multiple GC passes to fully release
|
||||
logger.debug("Forcing garbage collection for COM cleanup...")
|
||||
for i in range(3):
|
||||
gc.collect()
|
||||
time.sleep(0.05) # Small delay between GC passes
|
||||
|
||||
logger.debug("Waiting for WGC resources to be fully released...")
|
||||
# Give Windows time to clean up the capture session and remove border
|
||||
time.sleep(0.3)
|
||||
|
||||
# Final GC pass
|
||||
# Force garbage collection to release COM objects
|
||||
logger.debug("Running garbage collection for COM cleanup...")
|
||||
gc.collect()
|
||||
logger.debug("Final garbage collection completed")
|
||||
logger.debug("Garbage collection completed")
|
||||
|
||||
with self._frame_lock:
|
||||
self._latest_frame = None
|
||||
|
||||
Reference in New Issue
Block a user