diff --git a/sexp_effects/primitive_libs/streaming_gpu.py b/sexp_effects/primitive_libs/streaming_gpu.py index 8a9fc85..bcc3a99 100644 --- a/sexp_effects/primitive_libs/streaming_gpu.py +++ b/sexp_effects/primitive_libs/streaming_gpu.py @@ -1035,7 +1035,78 @@ def prim_autonomous_pipeline(img, effects_list, dynamic_expressions, frame_num, return pipeline(gpu_img, int(frame_num), float(fps)) +# ============================================================ +# GPU Image Primitives (keep images on GPU) +# ============================================================ + +def gpu_make_image(w, h, color=None): + """Create a new image on GPU filled with color (default black). + + Unlike image:make-image, this keeps the image on GPU memory, + avoiding CPU<->GPU transfers in the pipeline. + """ + if not GPU_AVAILABLE: + # Fallback to CPU + import numpy as np + if color is None: + color = [0, 0, 0] + img = np.zeros((int(h), int(w), 3), dtype=np.uint8) + img[:] = color + return img + + w, h = int(w), int(h) + if color is None: + color = [0, 0, 0] + + # Create on GPU directly + img = cp.zeros((h, w, 3), dtype=cp.uint8) + img[:, :, 0] = int(color[0]) if len(color) > 0 else 0 + img[:, :, 1] = int(color[1]) if len(color) > 1 else 0 + img[:, :, 2] = int(color[2]) if len(color) > 2 else 0 + + return img + + +def gpu_gradient_image(w, h, color1=None, color2=None, direction='horizontal'): + """Create a gradient image on GPU. + + Args: + w, h: Dimensions + color1, color2: Start and end colors [r, g, b] + direction: 'horizontal', 'vertical', 'diagonal' + """ + if not GPU_AVAILABLE: + return gpu_make_image(w, h, color1) + + w, h = int(w), int(h) + if color1 is None: + color1 = [0, 0, 0] + if color2 is None: + color2 = [255, 255, 255] + + img = cp.zeros((h, w, 3), dtype=cp.uint8) + + if direction == 'horizontal': + for c in range(3): + grad = cp.linspace(color1[c], color2[c], w, dtype=cp.float32) + img[:, :, c] = grad[cp.newaxis, :].astype(cp.uint8) + elif direction == 'vertical': + for c in range(3): + grad = cp.linspace(color1[c], color2[c], h, dtype=cp.float32) + img[:, :, c] = grad[:, cp.newaxis].astype(cp.uint8) + elif direction == 'diagonal': + for c in range(3): + x_grad = cp.linspace(0, 1, w, dtype=cp.float32)[cp.newaxis, :] + y_grad = cp.linspace(0, 1, h, dtype=cp.float32)[:, cp.newaxis] + combined = (x_grad + y_grad) / 2 + img[:, :, c] = (color1[c] + (color2[c] - color1[c]) * combined).astype(cp.uint8) + + return img + + # Add GPU-specific primitives PRIMITIVES['fused-pipeline'] = prim_fused_pipeline PRIMITIVES['autonomous-pipeline'] = prim_autonomous_pipeline +PRIMITIVES['gpu-make-image'] = gpu_make_image +PRIMITIVES['gpu-gradient'] = gpu_gradient_image # (The GPU video source will be added by create_cid_primitives in the task) diff --git a/test_autonomous.sexp b/test_autonomous.sexp index fbb1835..9e190a2 100644 --- a/test_autonomous.sexp +++ b/test_autonomous.sexp @@ -27,10 +27,10 @@ :ripple_phase "t * 2.0f" :brightness_factor "0.8f + 0.4f * sinf(t * 2.0f)"}) - ;; Frame pipeline - creates image and applies autonomous pipeline + ;; Frame pipeline - creates image ON GPU and applies autonomous pipeline (frame - (let [;; Create base gradient (still needs Python for now) - base (image:make-image 1920 1080 [128 100 200])] + (let [;; Create base image ON GPU (no CPU involvement!) + base (streaming_gpu:gpu-make-image 1920 1080 [128 100 200])] ;; Apply autonomous pipeline - ALL EFFECTS + ALL MATH ON GPU! (streaming_gpu:autonomous-pipeline base effects expressions frame-num 30.0))))