fix(comparison): Fix pipeline construction for proper headline rendering
- Add source stage (headlines, poetry, or empty) - Add viewport filter and font stage for headlines/poetry - Add camera stages (camera_update and camera) - Add effect stages based on preset - Fix stage order: message_overlay BEFORE display - Add null display stage with recording enabled - Capture frames from null display recording The fix ensures that the comparison framework uses the same pipeline structure as the main pipeline runner, producing proper block character rendering for headlines and poetry sources.
This commit is contained in:
@@ -108,7 +108,88 @@ def capture_frames(
|
||||
)
|
||||
ctx.params = params
|
||||
|
||||
# Add message overlay stage if enabled
|
||||
# Add stages based on source type (similar to pipeline_runner)
|
||||
from engine.display import DisplayRegistry
|
||||
from engine.pipeline.adapters import create_stage_from_display
|
||||
from engine.data_sources.sources import EmptyDataSource
|
||||
from engine.pipeline.adapters import DataSourceStage
|
||||
|
||||
# Add source stage
|
||||
if preset.source == "empty":
|
||||
source_stage = DataSourceStage(
|
||||
EmptyDataSource(width=preset.viewport_width, height=preset.viewport_height),
|
||||
name="empty",
|
||||
)
|
||||
else:
|
||||
# For headlines/poetry, use the actual source
|
||||
from engine.data_sources.sources import HeadlinesDataSource, PoetryDataSource
|
||||
|
||||
if preset.source == "headlines":
|
||||
source_stage = DataSourceStage(HeadlinesDataSource(), name="headlines")
|
||||
elif preset.source == "poetry":
|
||||
source_stage = DataSourceStage(PoetryDataSource(), name="poetry")
|
||||
else:
|
||||
# Fallback to empty
|
||||
source_stage = DataSourceStage(
|
||||
EmptyDataSource(
|
||||
width=preset.viewport_width, height=preset.viewport_height
|
||||
),
|
||||
name="empty",
|
||||
)
|
||||
pipeline.add_stage("source", source_stage)
|
||||
|
||||
# Add font stage for headlines/poetry (with viewport filter)
|
||||
if preset.source in ["headlines", "poetry"]:
|
||||
from engine.pipeline.adapters import FontStage, ViewportFilterStage
|
||||
|
||||
# Add viewport filter to prevent rendering all items
|
||||
pipeline.add_stage(
|
||||
"viewport_filter", ViewportFilterStage(name="viewport-filter")
|
||||
)
|
||||
# Add font stage for block character rendering
|
||||
pipeline.add_stage("font", FontStage(name="font"))
|
||||
else:
|
||||
# Fallback to simple conversion for empty/other sources
|
||||
from engine.pipeline.adapters import SourceItemsToBufferStage
|
||||
|
||||
pipeline.add_stage("render", SourceItemsToBufferStage(name="items-to-buffer"))
|
||||
|
||||
# Add camera stage
|
||||
from engine.camera import Camera
|
||||
from engine.pipeline.adapters import CameraStage, CameraClockStage
|
||||
|
||||
# Create camera based on preset
|
||||
if preset.camera == "feed":
|
||||
camera = Camera.feed()
|
||||
elif preset.camera == "scroll":
|
||||
camera = Camera.scroll(speed=0.1)
|
||||
elif preset.camera == "horizontal":
|
||||
camera = Camera.horizontal(speed=0.1)
|
||||
else:
|
||||
camera = Camera.feed()
|
||||
|
||||
camera.set_canvas_size(preset.viewport_width, preset.viewport_height * 2)
|
||||
|
||||
# Add camera update (for animation)
|
||||
pipeline.add_stage("camera_update", CameraClockStage(camera, name="camera-clock"))
|
||||
# Add camera stage
|
||||
pipeline.add_stage("camera", CameraStage(camera, name=preset.camera))
|
||||
|
||||
# Add effects
|
||||
if preset.effects:
|
||||
from engine.effects.registry import EffectRegistry
|
||||
from engine.pipeline.adapters import create_stage_from_effect
|
||||
|
||||
effect_registry = EffectRegistry()
|
||||
for effect_name in preset.effects:
|
||||
effect = effect_registry.get(effect_name)
|
||||
if effect:
|
||||
pipeline.add_stage(
|
||||
f"effect_{effect_name}",
|
||||
create_stage_from_effect(effect, effect_name),
|
||||
)
|
||||
|
||||
# Add message overlay stage if enabled (BEFORE display)
|
||||
if getattr(preset, "enable_message_overlay", False):
|
||||
from engine.pipeline.adapters import MessageOverlayConfig, MessageOverlayStage
|
||||
|
||||
@@ -120,9 +201,21 @@ def capture_frames(
|
||||
"message_overlay", MessageOverlayStage(config=overlay_config)
|
||||
)
|
||||
|
||||
# Add null display stage (LAST)
|
||||
null_display = DisplayRegistry.create("null")
|
||||
if null_display:
|
||||
pipeline.add_stage("display", create_stage_from_display(null_display, "null"))
|
||||
|
||||
# Build pipeline
|
||||
pipeline.build()
|
||||
|
||||
# Enable recording on null display if available
|
||||
display_stage = pipeline._stages.get("display")
|
||||
if display_stage and hasattr(display_stage, "_display"):
|
||||
backend = display_stage._display
|
||||
if hasattr(backend, "start_recording"):
|
||||
backend.start_recording()
|
||||
|
||||
# Capture frames
|
||||
frames = []
|
||||
start_time = time.time()
|
||||
@@ -132,18 +225,36 @@ def capture_frames(
|
||||
stage_result = pipeline.execute()
|
||||
frame_time = time.time() - frame_start
|
||||
|
||||
# Extract buffer from result
|
||||
buffer = stage_result.data if stage_result.success else []
|
||||
# Get frames from display recording
|
||||
display_stage = pipeline._stages.get("display")
|
||||
if display_stage and hasattr(display_stage, "_display"):
|
||||
backend = display_stage._display
|
||||
if hasattr(backend, "get_recorded_data"):
|
||||
recorded_frames = backend.get_recorded_data()
|
||||
# Add render_time_ms to each frame
|
||||
for frame in recorded_frames:
|
||||
frame["render_time_ms"] = frame_time * 1000
|
||||
frames = recorded_frames
|
||||
|
||||
frames.append(
|
||||
{
|
||||
"frame_number": i,
|
||||
"buffer": buffer,
|
||||
"width": preset.viewport_width,
|
||||
"height": preset.viewport_height,
|
||||
"render_time_ms": frame_time * 1000,
|
||||
}
|
||||
)
|
||||
# Fallback: create empty frames if no recording
|
||||
if not frames:
|
||||
for i in range(frame_count):
|
||||
frames.append(
|
||||
{
|
||||
"frame_number": i,
|
||||
"buffer": [],
|
||||
"width": preset.viewport_width,
|
||||
"height": preset.viewport_height,
|
||||
"render_time_ms": frame_time * 1000,
|
||||
}
|
||||
)
|
||||
|
||||
# Stop recording on null display
|
||||
display_stage = pipeline._stages.get("display")
|
||||
if display_stage and hasattr(display_stage, "_display"):
|
||||
backend = display_stage._display
|
||||
if hasattr(backend, "stop_recording"):
|
||||
backend.stop_recording()
|
||||
|
||||
total_time = time.time() - start_time
|
||||
|
||||
|
||||
Reference in New Issue
Block a user