///|
extern "C" fn wgpu_ptr_from_u64(value : UInt64) -> @wgpu_c.OpaquePtr = "moui_wgpu_ptr_from_u64"
///|
extern "C" fn wgpu_windows_hinstance() -> @wgpu_c.OpaquePtr = "moui_wgpu_windows_hinstance"
///|
/// Build a native WGPU provider. The composition root supplies the platform
/// policy used to interpret the renderer-neutral native handles.
pub fn native(
platform~ : WgpuNativePlatform,
text_engine~ : NativeTextEngine,
) -> @render.RendererProvider {
let image_decoder = native_image_decoder()
let caps = wgpu_provider_capabilities(
provider_id="wgpu",
backend=@render.RendererBackendKind::NativeWgpu,
)
@render.RendererProvider::new(
id="wgpu",
descriptor=renderer_descriptor(),
capabilities=() => caps,
bind=surface => {
match @render_common.host_surface_metrics_error(surface) {
Some(message) => return @render.RendererBindResult::Rejected(message)
None => ()
}
guard surface.native_surface() is Some(native_surface) else {
return @render.RendererBindResult::Rejected(
"wgpu requires a native surface capability",
)
}
match native_surface_error(platform, native_surface) {
Some(message) => return @render.RendererBindResult::Rejected(message)
None => ()
}
let renderer = create_native_renderer(
surface.metrics(),
platform,
native_surface,
text_engine,
) catch {
err =>
return @render.RendererBindResult::Rejected(
"wgpu unavailable for \{platform.label()}: \{err.message()}",
)
}
@render.RendererBindResult::Bound(
renderer.to_renderer_session(
image_source=surface
.image_source()
.unwrap_or(@render.HostImageSource::unavailable()),
image_decoder=Some(image_decoder),
native_surface=Some(native_surface),
),
)
},
)
}
///|
fn native_image_decoder() -> @render.RendererImageDecoder {
@render.RendererImageDecoder::new(decode=(source, bytes) => {
match bytes {
Some(data) =>
native_image_load_completion_from_bytes(
source,
data,
background_io=true,
)
None => native_image_load_completion(source)
}
})
}
///|
fn create_native_renderer(
metrics : @core.SurfaceMetrics,
platform : WgpuNativePlatform,
native_surface : @render.NativeSurface,
text_engine : NativeTextEngine,
) -> WgpuRenderer raise @wgpu_mbt.WgpuError {
let surface = create_native_host_surface(platform, native_surface)
create_with_host_surface_and_text_engine(metrics, surface, text_engine~)
}
///|
fn create_native_host_surface(
platform : WgpuNativePlatform,
native_surface : @render.NativeSurface,
) -> HostWgpuSurface raise @wgpu_mbt.WgpuError {
let instance = @wgpu_mbt.Instance::create()
let surface = match platform {
WgpuNativePlatform::MacOS => {
let native_descriptor = @wgpu_mbt.surface_descriptor_metal_layer_new(
wgpu_ptr_from_u64(native_surface.surface_handle()),
)
let surface = instance.create_surface(native_descriptor)
@wgpu_mbt.surface_descriptor_free(native_descriptor)
surface
}
WgpuNativePlatform::Windows =>
instance.create_surface_windows_hwnd(
wgpu_windows_hinstance(),
wgpu_ptr_from_u64(native_surface.surface_handle()),
)
WgpuNativePlatform::Linux =>
instance.create_surface_wayland(
wgpu_ptr_from_u64(native_surface.display_handle().unwrap()),
wgpu_ptr_from_u64(native_surface.surface_handle()),
)
}
HostWgpuSurface::new(instance~, surface~)
}
///|
fn native_surface_error(
platform : WgpuNativePlatform,
surface : @render.NativeSurface,
) -> String? {
if surface.surface_handle() == 0UL {
return Some("wgpu requires a non-zero surface handle")
}
match platform {
WgpuNativePlatform::Linux =>
match surface.display_handle() {
Some(display) if display != 0UL => None
_ => Some("wgpu linux requires a non-zero display handle")
}
WgpuNativePlatform::MacOS | WgpuNativePlatform::Windows => None
}
}
///|
pub fn WgpuRenderer::to_renderer_session(
self : WgpuRenderer,
image_source? : @render.HostImageSource = @render.HostImageSource::unavailable(),
image_decoder? : @render.RendererImageDecoder? = None,
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 => {
let frame = submission.frame()
self.render(frame.commands)
@render.RenderFrameResult::new(presented=true)
},
text_system=() => self.text_system(),
present_count=() => self.present_count(),
recover=_reason => @render.RecoveryResult::FallbackToCpu,
dispose=() => self.dispose(),
image_records=() => self.image_resources(),
apply_image_load_completion=completion => {
self.apply_image_resource_load_completion(completion)
},
image_source~,
image_decoder~,
native_surface~,
)
}