mirror of
https://github.com/hacksider/Deep-Live-Cam.git
synced 2026-07-25 03:20:51 +02:00
Address PR #1775 review: pipelined-detection race and CUDA-graph monkey-patch
- core._run_pipe_pipeline: hand the background detector its own copy of the frame. The frame processors mutate in place via paste-back, which was racing with concurrent face detection on the same buffer. - face_swapper._init_cuda_graph_session: replace the `swapper.session.run` monkey-patch with a `_CudaGraphSessionAdapter` that proxies every attribute to the underlying session and only overrides `.run()`. Guarded so repeat init does not double-wrap. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -357,8 +357,11 @@ def _run_pipe_pipeline(
|
||||
# Start detecting on THIS frame eagerly — the result
|
||||
# will be used for the next iteration. At video
|
||||
# frame rates the face barely moves between frames.
|
||||
# Hand the detector its own copy: the frame processors
|
||||
# below mutate `frame` in place (paste-back), which
|
||||
# would otherwise race with detection.
|
||||
pending_detect = detect_executor.submit(
|
||||
get_one_face, frame)
|
||||
get_one_face, frame.copy())
|
||||
else:
|
||||
target_face = None
|
||||
|
||||
|
||||
@@ -174,6 +174,36 @@ _cuda_graph_session = {
|
||||
_cuda_graph_lock = threading.Lock()
|
||||
|
||||
|
||||
class _CudaGraphSessionAdapter:
|
||||
"""Drop-in wrapper around an ONNX Runtime session.
|
||||
|
||||
Routes ``.run()`` through CUDA graph replay when a recorded graph is
|
||||
available, and transparently proxies every other attribute to the
|
||||
underlying session so insightface's INSwapper sees an unchanged API.
|
||||
"""
|
||||
|
||||
def __init__(self, underlying):
|
||||
# Use object.__setattr__ to bypass our own __setattr__.
|
||||
object.__setattr__(self, "_underlying", underlying)
|
||||
|
||||
def run(self, output_names, input_dict, **kwargs):
|
||||
if _cuda_graph_session['recorded']:
|
||||
try:
|
||||
keys = list(input_dict.keys())
|
||||
blob = input_dict[keys[0]]
|
||||
latent = input_dict[keys[1]]
|
||||
return [_cuda_graph_swap_inference(blob, latent)]
|
||||
except Exception:
|
||||
pass
|
||||
return self._underlying.run(output_names, input_dict, **kwargs)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._underlying, name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
setattr(self._underlying, name, value)
|
||||
|
||||
|
||||
def _init_cuda_graph_session(model_path: str, swapper):
|
||||
"""Create a CUDA-graph-enabled ONNX session for the swap model.
|
||||
|
||||
@@ -209,22 +239,14 @@ def _init_cuda_graph_session(model_path: str, swapper):
|
||||
_cuda_graph_session['ort_latent'] = ort_latent
|
||||
_cuda_graph_session['recorded'] = True
|
||||
|
||||
# Monkey-patch the swapper's session.run to use CUDA graph replay
|
||||
_original_run = swapper.session.run
|
||||
# Wrap swapper.session in an adapter instead of rebinding
|
||||
# session.run. insightface's INSwapper.get() reads .run via the
|
||||
# session attribute, so either works; the adapter survives any
|
||||
# later attribute reads on the session and keeps the original
|
||||
# session object untouched.
|
||||
if not isinstance(swapper.session, _CudaGraphSessionAdapter):
|
||||
swapper.session = _CudaGraphSessionAdapter(swapper.session)
|
||||
|
||||
def _graph_run(output_names, input_dict, **kwargs):
|
||||
if _cuda_graph_session['recorded']:
|
||||
try:
|
||||
# input_dict has 'target' (blob) and 'source' (latent)
|
||||
keys = list(input_dict.keys())
|
||||
blob = input_dict[keys[0]]
|
||||
latent = input_dict[keys[1]]
|
||||
return [_cuda_graph_swap_inference(blob, latent)]
|
||||
except Exception:
|
||||
pass
|
||||
return _original_run(output_names, input_dict, **kwargs)
|
||||
|
||||
swapper.session.run = _graph_run
|
||||
import sys
|
||||
print(f"[{NAME}] CUDA graph session initialized (swap model)")
|
||||
sys.stdout.flush()
|
||||
|
||||
Reference in New Issue
Block a user