From 1edc4bc298e7894749bc2beb48d6dd8bf2d4944e Mon Sep 17 00:00:00 2001 From: Kenneth Estanislao Date: Wed, 1 Apr 2026 23:55:43 +0800 Subject: [PATCH] DML Lock fixed for cuda and CPU --- modules/face_analyser.py | 16 +++-- modules/processors/frame/face_swapper.py | 7 +- modules/ui.py | 81 +++--------------------- 3 files changed, 28 insertions(+), 76 deletions(-) diff --git a/modules/face_analyser.py b/modules/face_analyser.py index 5860880..88d14aa 100644 --- a/modules/face_analyser.py +++ b/modules/face_analyser.py @@ -34,9 +34,15 @@ def get_face_analyser() -> Any: return FACE_ANALYSER +def _is_dml() -> bool: + return any("DmlExecutionProvider" in p for p in modules.globals.execution_providers) + + def get_one_face(frame: Frame) -> Any: - import modules.globals as g - with g.dml_lock: + if _is_dml(): + with modules.globals.dml_lock: + face = get_face_analyser().get(frame) + else: face = get_face_analyser().get(frame) try: return min(face, key=lambda x: x.bbox[0]) @@ -45,9 +51,11 @@ def get_one_face(frame: Frame) -> Any: def get_many_faces(frame: Frame) -> Any: - import modules.globals as g try: - with g.dml_lock: + if _is_dml(): + with modules.globals.dml_lock: + return get_face_analyser().get(frame) + else: return get_face_analyser().get(frame) except IndexError: return None diff --git a/modules/processors/frame/face_swapper.py b/modules/processors/frame/face_swapper.py index 57bf508..9492e44 100644 --- a/modules/processors/frame/face_swapper.py +++ b/modules/processors/frame/face_swapper.py @@ -152,7 +152,12 @@ def swap_face(source_face: Face, target_face: Face, temp_frame: Frame) -> Frame: if not temp_frame.flags['C_CONTIGUOUS']: temp_frame = np.ascontiguousarray(temp_frame) - with modules.globals.dml_lock: + if any("DmlExecutionProvider" in p for p in modules.globals.execution_providers): + with modules.globals.dml_lock: + swapped_frame_raw = face_swapper.get( + temp_frame, target_face, source_face, paste_back=True + ) + else: swapped_frame_raw = face_swapper.get( temp_frame, target_face, source_face, paste_back=True ) diff --git a/modules/ui.py b/modules/ui.py index 6041fc4..5fa00ca 100644 --- a/modules/ui.py +++ b/modules/ui.py @@ -1097,41 +1097,10 @@ def _capture_thread_func(cap, capture_queue, stop_event): pass -def _detection_thread_func(latest_frame_holder, detection_result, detection_lock, stop_event): - """Detection thread: continuously runs face detection on the latest - captured frame and stores results in detection_result under detection_lock. - - This decouples face detection (~15-30ms) from face swapping (~5-10ms) - so the swap loop never blocks on detection, significantly improving - live mode FPS.""" - while not stop_event.is_set(): - with detection_lock: - frame = latest_frame_holder[0] - - if frame is None: - time.sleep(0.2) - continue - - if modules.globals.many_faces: - many = get_many_faces(frame) - with detection_lock: - detection_result['target_face'] = None - detection_result['many_faces'] = many - else: - face = get_one_face(frame) - with detection_lock: - detection_result['target_face'] = face - detection_result['many_faces'] = None - - -def _processing_thread_func(capture_queue, processed_queue, stop_event, - latest_frame_holder, detection_result, detection_lock): - """Processing thread: takes raw frames from capture_queue, reads the - latest detection result from the shared detection_result dict, applies - face swap/enhancement, and puts results into processed_queue. - - Face detection runs concurrently in _detection_thread_func — this thread - only reads cached results so it never blocks on detection.""" +def _processing_thread_func(capture_queue, processed_queue, stop_event): + """Processing thread: takes raw frames from capture_queue, runs face + detection (throttled to every 3rd frame), applies face swap/enhancement, + and puts results into processed_queue.""" frame_processors = get_frame_processors_modules(modules.globals.frame_processors) source_image = None last_source_path = None @@ -1139,6 +1108,9 @@ def _processing_thread_func(capture_queue, processed_queue, stop_event, fps_update_interval = 0.5 frame_count = 0 fps = 0 + det_count = 0 + cached_target_face = None + cached_many_faces = None while not stop_event.is_set(): try: @@ -1151,34 +1123,20 @@ def _processing_thread_func(capture_queue, processed_queue, stop_event, if modules.globals.live_mirror: temp_frame = gpu_flip(temp_frame, 1) - # Publish the mirrored frame for the detection thread to pick up - with detection_lock: - latest_frame_holder[0] = temp_frame - if not modules.globals.map_faces: if modules.globals.source_path and modules.globals.source_path != last_source_path: last_source_path = modules.globals.source_path source_image = get_one_face(cv2.imread(modules.globals.source_path)) - # Read latest detection results (brief lock to avoid blocking detection thread) - # Run detection inline since detection thread is disabled # Run detection every 3 frames, reuse cached result otherwise - if not hasattr(_processing_thread_func, '_det_count'): - _processing_thread_func._det_count = 0 - _processing_thread_func._det_count += 1 - - if _processing_thread_func._det_count % 3 == 0: + det_count += 1 + if det_count % 3 == 0: if modules.globals.many_faces: cached_target_face = None cached_many_faces = get_many_faces(temp_frame) - detection_result['many_faces'] = cached_many_faces else: cached_target_face = get_one_face(temp_frame) cached_many_faces = None - detection_result['target_face'] = cached_target_face - else: - cached_target_face = detection_result.get('target_face') - cached_many_faces = detection_result.get('many_faces') for frame_processor in frame_processors: if frame_processor.NAME == "DLC.FACE-ENHANCER": @@ -1271,14 +1229,6 @@ def create_webcam_preview(camera_index: int): processed_queue = queue.Queue(maxsize=2) stop_event = threading.Event() - # Shared state for the detection pipeline. - # latest_frame_holder[0] is the most recent raw frame for the detection - # thread; detection_result holds the last detected faces for the - # processing thread to read. Both are guarded by detection_lock. - detection_lock = threading.Lock() - latest_frame_holder = [None] - detection_result = {'target_face': None, 'many_faces': None} - # Start capture thread cap_thread = threading.Thread( target=_capture_thread_func, @@ -1287,20 +1237,10 @@ def create_webcam_preview(camera_index: int): ) cap_thread.start() - # Start detection thread — runs face detection asynchronously so the - # processing/swap thread never blocks on it - det_thread = threading.Thread( - target=_detection_thread_func, - args=(latest_frame_holder, detection_result, detection_lock, stop_event), - daemon=True, - ) - # det_thread.start() - # Start processing thread proc_thread = threading.Thread( target=_processing_thread_func, - args=(capture_queue, processed_queue, stop_event, - latest_frame_holder, detection_result, detection_lock), + args=(capture_queue, processed_queue, stop_event), daemon=True, ) proc_thread.start() @@ -1309,7 +1249,6 @@ def create_webcam_preview(camera_index: int): def _cleanup(): stop_event.set() cap_thread.join(timeout=2.0) - det_thread.join(timeout=2.0) proc_thread.join(timeout=2.0) cap.release() PREVIEW.withdraw()