///|
/// A renderer owner that keeps the raster factory and GPU queue model under one
/// lifecycle. `Recorded` queue completions are internal handoff signals; only
/// the window-backed presenter can produce a public `Presented` completion.
pub struct SkiaHybridRenderer {
priv renderer : SkiaRasterRenderer
priv worker : @render_common.GpuRenderWorker
priv surface_route : SkiaSurfaceRoute
priv requested : NativeRendererMode
priv mut fallback_to_raster : Bool
priv pending_completions : Array[@render.RenderPresentCompletion]
}
///|
pub fn create_hybrid_renderer(
platform : NativeGpuPlatform,
requested : NativeRendererMode,
metrics : @core.SurfaceMetrics,
target : SkiaPresentTarget,
gpu_available~ : Bool,
gpu_promoted~ : Bool,
gpu_target? : HostGpuPresentTarget? = None,
font_resolution? : SkiaFontResolution = SkiaFontResolution::SystemFontMgr,
async_image_loading? : Bool = false,
) -> SkiaHybridRenderer raise SkiaRendererError {
let surface_route = resolve_surface_route(
platform,
requested,
gpu_available~,
gpu_promoted~,
)
let gpu_target = if surface_route.is_gpu() { gpu_target } else { None }
let renderer = create_with_present_target_and_route(
metrics,
target,
surface_route~,
gpu_target~,
font_resolution~,
async_image_loading~,
)
{
renderer,
worker: @render_common.GpuRenderWorker::new(),
surface_route,
requested,
fallback_to_raster: false,
pending_completions: [],
}
}
///|
pub fn SkiaHybridRenderer::surface_route(
self : SkiaHybridRenderer,
) -> SkiaSurfaceRoute {
self.surface_route
}
///|
pub fn SkiaHybridRenderer::requested_mode(
self : SkiaHybridRenderer,
) -> NativeRendererMode {
self.requested
}
///|
pub fn SkiaHybridRenderer::render_frame(
self : SkiaHybridRenderer,
submission : @render.RenderFrameSubmission,
) -> @render.RenderFrameResult {
let frame = submission.frame()
if self.fallback_to_raster || !self.surface_route.is_gpu() {
return self.renderer.render_frame(frame)
}
let input = @render.RenderFrameInput::new(
metrics=self.renderer.metrics(),
commands=frame.commands,
damage=frame.damage,
clear_color=Some(frame.clear_color),
)
let result = self.worker.submit(input, token=submission.token())
self.worker.poll()
let completions = self.worker.drain_present_completions()
if completions.any(c => {
c.status() is @render.RenderPresentStatus::FallbackToRaster
}) {
self.fallback_to_raster = true
self.pending_completions.append(
completions.filter(c => {
c.status() is @render.RenderPresentStatus::FallbackToRaster
}),
)
return @render.RenderFrameResult::new(presented=false, queued=false)
}
let mut recorded_completion : @render.RenderPresentCompletion? = None
for completion in completions {
if completion.status() is @render.RenderPresentStatus::Recorded {
recorded_completion = Some(completion)
break
}
}
match recorded_completion {
Some(recorded) => {
let presented = self.renderer.render_frame(frame)
if presented.presented {
self.pending_completions.push(
@render.RenderPresentCompletion::new(
token=recorded.token(),
submitted_at_ms=recorded.submitted_at_ms(),
completed_at_ms=recorded.completed_at_ms(),
presented_at_ms=recorded.completed_at_ms(),
status=@render.RenderPresentStatus::Presented,
gpu_context_generation=recorded.gpu_context_generation(),
),
)
}
presented
}
None => result
}
}
///|
pub fn SkiaHybridRenderer::drain_present_completions(
self : SkiaHybridRenderer,
) -> Array[@render.RenderPresentCompletion] {
let completions = self.pending_completions.copy()
self.pending_completions.clear()
completions
}
///|
pub fn SkiaHybridRenderer::fallback_to_raster(
self : SkiaHybridRenderer,
) -> Bool {
self.fallback_to_raster
}
///|
pub fn SkiaRasterRenderer::metrics(
self : SkiaRasterRenderer,
) -> @core.SurfaceMetrics {
self.metrics
}