///|
pub fn draw_commands_to_host_calls(
commands : Array[@core.DrawCommand],
size : @core.Size,
) -> Array[WebGpuHostCall] {
let calls : Array[WebGpuHostCall] = [WebGpuHostCall::BeginFrame(size)]
for command in commands {
match command {
@core.DrawCommand::Clear(color) =>
calls.push(WebGpuHostCall::Clear(color))
@core.DrawCommand::FillRect(rect, color) =>
calls.push(WebGpuHostCall::FillRect(rect, color))
@core.DrawCommand::StrokeRect(rect, color, width) =>
calls.push(WebGpuHostCall::StrokeRect(rect, color, width))
@core.DrawCommand::FillRoundedRect(rect, color) =>
calls.push(WebGpuHostCall::FillRoundedRect(rect, color))
@core.DrawCommand::StrokeRoundedRect(rect, color, width) =>
calls.push(WebGpuHostCall::StrokeRoundedRect(rect, color, width))
@core.DrawCommand::FillRoundedRectBrush(rect, brush) =>
calls.push(WebGpuHostCall::FillRoundedRectBrush(rect, brush))
@core.DrawCommand::StrokeRoundedRectBrush(rect, brush, width) =>
calls.push(WebGpuHostCall::StrokeRoundedRectBrush(rect, brush, width))
@core.DrawCommand::DrawShadow(shadow) =>
calls.push(WebGpuHostCall::DrawShadow(shadow))
@core.DrawCommand::DrawText(run) =>
calls.push(WebGpuHostCall::DrawText(run))
@core.DrawCommand::DrawImage(run) =>
calls.push(WebGpuHostCall::DrawImage(run))
@core.DrawCommand::DrawPath(path) =>
calls.push(WebGpuHostCall::DrawPath(path))
@core.DrawCommand::PushClip(rect) =>
calls.push(WebGpuHostCall::PushClip(rect))
@core.DrawCommand::PopClip => calls.push(WebGpuHostCall::PopClip)
@core.DrawCommand::PushRoundedClip(rect) =>
calls.push(WebGpuHostCall::PushRoundedClip(rect))
@core.DrawCommand::PopRoundedClip =>
calls.push(WebGpuHostCall::PopRoundedClip)
@core.DrawCommand::PushTransform(transform) =>
calls.push(WebGpuHostCall::PushTransform(transform))
@core.DrawCommand::PopTransform =>
calls.push(WebGpuHostCall::PopTransform)
@core.DrawCommand::PushOpacity(opacity) =>
calls.push(WebGpuHostCall::PushOpacity(opacity))
@core.DrawCommand::PopOpacity => calls.push(WebGpuHostCall::PopOpacity)
@core.DrawCommand::PushLayer(layer) =>
calls.push(WebGpuHostCall::PushLayer(layer))
@core.DrawCommand::PopLayer => calls.push(WebGpuHostCall::PopLayer)
@core.DrawCommand::BeginRetainedLayer(spec) =>
calls.push(
WebGpuHostCall::PushLayer(
@core.LayerSpec::new(
mask=@core.LayerMask::RectMask(spec.frame),
offscreen=true,
),
),
)
@core.DrawCommand::EndRetainedLayer(_) =>
calls.push(WebGpuHostCall::PopLayer)
@core.DrawCommand::PushFilter(filter) =>
calls.push(WebGpuHostCall::PushFilter(filter))
@core.DrawCommand::PopFilter => calls.push(WebGpuHostCall::PopFilter)
@core.DrawCommand::DrawShaderEffect(spec) =>
calls.push(WebGpuHostCall::DrawShaderEffect(spec))
}
}
calls.push(WebGpuHostCall::Present)
calls
}
///|
fn ensure_required_capabilities(
capabilities : WebGpuHostCapabilities,
) -> Unit raise WebGpuHostError {
if !capabilities.webgpu_available() {
raise WebGpuHostError::Unavailable
}
if !capabilities.adapter_ready() {
raise WebGpuHostError::AdapterUnavailable
}
if !capabilities.can_render() {
raise WebGpuHostError::PipelineUnavailable
}
}
///|
fn submit_host_calls(
renderer : HostWebGpuRenderer,
calls : Array[WebGpuHostCall],
) -> Unit raise WebGpuHostError {
for call in calls {
match call {
BeginFrame(size) =>
raise_for_host_status(
host_begin_frame(renderer, size.width, size.height),
)
Clear(color) =>
raise_for_host_status(
host_clear(renderer, color.r, color.g, color.b, color.a),
)
FillRect(rect, color) =>
raise_for_host_status(
host_fill_rect(
renderer,
rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height,
color.r,
color.g,
color.b,
color.a,
),
)
StrokeRect(rect, color, width) =>
raise_for_host_status(
host_stroke_rect(
renderer,
rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height,
color.r,
color.g,
color.b,
color.a,
width,
),
)
FillRoundedRect(rounded, color) =>
raise_for_host_status(
host_fill_rounded_rect(
renderer,
rounded.rect.origin.x,
rounded.rect.origin.y,
rounded.rect.size.width,
rounded.rect.size.height,
rounded.radius,
color.r,
color.g,
color.b,
color.a,
),
)
StrokeRoundedRect(rounded, color, width) =>
raise_for_host_status(
host_stroke_rounded_rect(
renderer,
rounded.rect.origin.x,
rounded.rect.origin.y,
rounded.rect.size.width,
rounded.rect.size.height,
rounded.radius,
color.r,
color.g,
color.b,
color.a,
width,
),
)
FillRoundedRectBrush(rounded, brush) =>
submit_fill_rounded_rect_brush(renderer, rounded, brush)
StrokeRoundedRectBrush(rounded, brush, width) =>
submit_stroke_rounded_rect_brush(renderer, rounded, brush, width)
DrawShadow(shadow) =>
raise_for_host_status(
host_draw_shadow(
renderer,
shadow.rect.rect.origin.x,
shadow.rect.rect.origin.y,
shadow.rect.rect.size.width,
shadow.rect.rect.size.height,
shadow.rect.radius,
shadow.offset.x,
shadow.offset.y,
shadow.blur_radius,
shadow.spread,
shadow.color.r,
shadow.color.g,
shadow.color.b,
shadow.color.a,
),
)
DrawText(run) =>
raise_for_host_status(
host_draw_text(
renderer,
host_string(run.text),
run.frame.origin.x,
run.frame.origin.y,
run.frame.size.width,
run.frame.size.height,
host_string(run.font.css_family()),
host_string(run.font.css_style()),
run.font.size,
run.font.weight,
run.color.r,
run.color.g,
run.color.b,
run.color.a,
text_align_host_code(run.align),
),
)
DrawImage(run) =>
raise_for_host_status(
host_draw_image(
renderer,
host_string(run.source),
run.frame.origin.x,
run.frame.origin.y,
run.frame.size.width,
run.frame.size.height,
run.opacity,
image_fit_host_code(run.fit),
),
)
DrawPath(path) => submit_draw_path(renderer, path)
PushClip(rect) =>
raise_for_host_status(
host_push_clip(
renderer,
rect.origin.x,
rect.origin.y,
rect.size.width,
rect.size.height,
),
)
PopClip => raise_for_host_status(host_pop_clip(renderer))
PushRoundedClip(rounded) => submit_push_rounded_clip(renderer, rounded)
PopRoundedClip => raise_for_host_status(host_pop_layer(renderer))
PushTransform(transform) =>
raise_for_host_status(
host_push_transform(
renderer,
transform.a,
transform.b,
transform.c,
transform.d,
transform.tx,
transform.ty,
),
)
PopTransform => raise_for_host_status(host_pop_transform(renderer))
PushOpacity(opacity) =>
raise_for_host_status(host_push_opacity(renderer, opacity))
PopOpacity => raise_for_host_status(host_pop_opacity(renderer))
PushLayer(layer) => submit_push_layer(renderer, layer)
PopLayer => raise_for_host_status(host_pop_layer(renderer))
PushFilter(filter) => submit_push_filter(renderer, filter)
PopFilter => raise_for_host_status(host_pop_filter(renderer))
DrawShaderEffect(spec) => submit_draw_shader_effect(renderer, spec)
Present => raise_for_host_status(host_present(renderer))
}
}
}
///|
fn submit_push_layer(
renderer : HostWebGpuRenderer,
layer : @core.LayerSpec,
) -> Unit raise WebGpuHostError {
let mask = layer_mask_payload(layer.mask)
raise_for_host_status(
host_push_layer(
renderer,
layer.opacity,
blend_mode_host_code(layer.blend_mode),
mask.kind,
mask.rect.origin.x,
mask.rect.origin.y,
mask.rect.size.width,
mask.rect.size.height,
mask.radius,
layer.offscreen,
),
)
}
///|
fn submit_push_rounded_clip(
renderer : HostWebGpuRenderer,
rounded : @core.RoundedRect,
) -> Unit raise WebGpuHostError {
let mask = layer_mask_payload(@core.LayerMask::RoundedMask(rounded))
raise_for_host_status(
host_push_layer(
renderer,
1.0,
blend_mode_host_code(@core.BlendMode::SourceOver),
mask.kind,
mask.rect.origin.x,
mask.rect.origin.y,
mask.rect.size.width,
mask.rect.size.height,
mask.radius,
true,
),
)
}
///|
fn submit_push_filter(
renderer : HostWebGpuRenderer,
filter : @core.FilterEffect,
) -> Unit raise WebGpuHostError {
let payload = filter_effect_payload(filter)
raise_for_host_status(
host_push_filter(
renderer,
payload.kind,
payload.amount,
host_string(payload.matrix_values),
),
)
}
///|
fn submit_draw_shader_effect(
renderer : HostWebGpuRenderer,
spec : @core.ShaderEffectSpec,
) -> Unit raise WebGpuHostError {
let fallback = brush_payload(spec.fallback, spec.frame)
raise_for_host_status(
host_draw_shader_effect(
renderer,
host_string(spec.name),
spec.frame.origin.x,
spec.frame.origin.y,
spec.frame.size.width,
spec.frame.size.height,
host_string(double_array_string(spec.uniforms)),
fallback.start.x,
fallback.start.y,
fallback.end.x,
fallback.end.y,
fallback.start_color.r,
fallback.start_color.g,
fallback.start_color.b,
fallback.start_color.a,
fallback.end_color.r,
fallback.end_color.g,
fallback.end_color.b,
fallback.end_color.a,
),
)
}
///|
fn submit_draw_path(
renderer : HostWebGpuRenderer,
path : @core.PathSpec,
) -> Unit raise WebGpuHostError {
let mesh = @render_common.tessellate_path(path)
raise_for_host_status(
host_draw_path_mesh(renderer, host_string(path_mesh_payload(mesh))),
)
}