///|
/// Build a direct-Metal provider for a host GPU surface. The platform backend
/// owns the window and its `CAMetalLayer`; this provider only configures that
/// layer and draws into it, so nothing here creates a device-backed surface of
/// its own (ADR 0044 boundary: Apple frameworks yes, graphics libraries no).
///
/// The composition root injects the native text engine, which is why this
/// module depends on no text provider: the same Metal route can be driven by
/// CoreText, DirectWrite or Fontconfig coverage.
pub fn metal(text_engine~ : NativeTextEngine) -> @render.RendererProvider {
let caps = metal_feature_capabilities()
let cache = @native_text.NativeTextCache::new_with_engine(text_engine)
@render.RendererProvider::new(
id="metal-direct",
descriptor=metal_renderer_descriptor(caps),
capabilities=() => caps,
bind=surface => {
match @render_common.host_surface_metrics_error(surface) {
Some(message) => return Rejected(message)
None => ()
}
guard surface.native_surface() is Some(native_surface) else {
return Rejected("metal requires a native surface capability")
}
let context = metal_context_create()
guard context != 0UL else { return Rejected("metal device unavailable") }
guard metal_context_prepare(context, native_surface.surface_handle()) == 1 else {
metal_context_release(context)
return Rejected(
"metal could not prepare the host surface layer or its render pipelines",
)
}
Bound(
MetalRenderer::new(
metrics=surface.metrics(),
context~,
layer=native_surface.surface_handle(),
cache~,
image_source=surface
.image_source()
.unwrap_or(@render.HostImageSource::unavailable()),
).to_renderer_session(native_surface=Some(native_surface)),
)
},
)
}
///|
fn MetalRenderer::to_renderer_session(
self : MetalRenderer,
native_surface~ : @render.NativeSurface? = None,
) -> @render.RendererSession {
@render.RendererSession::new(
resize=metrics => {
match native_surface {
Some(surface) => surface.resize(metrics)
None => ()
}
self.resize(metrics)
},
render_frame=submission => self.render_frame(submission.frame()),
text_system=() => @native_text.native_text_system_with_cache(self.cache),
present_count=() => self.present_count(),
recover=_reason => {
RecoveryFailed(
"metal direct has no gpu recovery path before plan stage L4",
)
},
dispose=() => self.dispose(),
image_source=self.image_source,
image_decoder=Some(metal_image_decoder()),
image_records=() => self.images.lifecycle.snapshot(),
apply_image_load_completion=completion => {
apply_metal_image_completion(
self.images,
context=self.context,
completion,
)
},
native_surface~,
)
}