Checkpoint

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2023-09-23 11:06:10 -06:00
parent df388d9f33
commit 3fbe93f029
8 changed files with 501 additions and 176 deletions

14
Cargo.lock generated
View file

@ -1039,6 +1039,20 @@ name = "bytemuck"
version = "1.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "965ab7eb5f8f97d2a083c799f3a1b994fc397b2fe2da5d1da1626ce15a39f2b1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.29",
]
[[package]]
name = "byteorder"

View file

@ -55,7 +55,7 @@ usvg = { version = "0.14", features = [] }
uuid = { version = "1.1.2", features = ["v4"] }
waker-fn = "1.1.0"
slotmap = "1.0.6"
bytemuck = "1.14.0"
bytemuck = { version = "1.14.0", features = ["derive"] }
schemars.workspace = true
plane-split = "0.18.0"

View file

@ -4,6 +4,8 @@ use std::{
process::{self, Command},
};
use cbindgen::Config;
fn main() {
generate_dispatch_bindings();
let header_path = generate_shader_bindings();
@ -32,21 +34,29 @@ fn generate_dispatch_bindings() {
fn generate_shader_bindings() -> PathBuf {
let output_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("scene.h");
let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let mut config = Config::default();
config.include_guard = Some("SCENE_H".into());
config.language = cbindgen::Language::C;
config.export.include.extend([
"Bounds".into(),
"Corners".into(),
"Edges".into(),
"Size".into(),
"Pixels".into(),
"PointF".into(),
"Hsla".into(),
"Quad".into(),
"QuadInputIndex".into(),
"QuadUniforms".into(),
]);
config.no_includes = true;
config.enumeration.prefix_with_name = true;
cbindgen::Builder::new()
.with_language(cbindgen::Language::C)
.with_include_guard("SCENE_H")
.with_src(crate_dir.join("src/scene.rs"))
.with_src(crate_dir.join("src/geometry.rs"))
.with_src(crate_dir.join("src/color.rs"))
.with_no_includes()
.include_item("Quad")
.include_item("Bounds")
.include_item("Corners")
.include_item("Edges")
.include_item("Size")
.include_item("Pixels")
.include_item("Point")
.include_item("Hsla")
.with_src(crate_dir.join("src/platform/mac/metal_renderer.rs"))
.with_config(config)
.generate()
.expect("Unable to generate bindings")
.write_to_file(&output_path);

View file

@ -118,7 +118,7 @@ impl TryFrom<&'_ str> for Rgba {
}
}
#[derive(Default, Copy, Clone, Debug, PartialEq)]
#[derive(Default, Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
#[repr(C)]
pub struct Hsla {
pub h: f32,
@ -128,8 +128,6 @@ pub struct Hsla {
}
impl Eq for Hsla {}
unsafe impl Zeroable for Hsla {}
unsafe impl Pod for Hsla {}
pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Hsla {
Hsla {

View file

@ -72,7 +72,6 @@ impl<T: Clone + Debug> Clone for Point<T> {
}
unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Point<T> {}
unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Point<T> {}
#[derive(Refineable, Default, Clone, Copy, Debug, PartialEq)]
@ -83,6 +82,9 @@ pub struct Size<T: Clone + Debug> {
pub height: T,
}
unsafe impl<T: Clone + Debug + Zeroable + Pod> Zeroable for Size<T> {}
unsafe impl<T: Clone + Debug + Zeroable + Pod> Pod for Size<T> {}
pub fn size<T: Clone + Debug>(width: T, height: T) -> Size<T> {
Size { width, height }
}

View file

@ -1,153 +1,249 @@
// use cocoa::{
// base::{NO, YES},
// foundation::NSUInteger,
// quartzcore::AutoresizingMask,
// };
// use core_foundation::base::TCFType;
// use foreign_types::ForeignTypeRef;
// use log::warn;
// use media::core_video::{self, CVMetalTextureCache};
// use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
// use objc::{self, msg_send, sel, sel_impl};
// use shaders::ToFloat2 as _;
// use std::{collections::HashMap, ffi::c_void, iter::Peekable, mem, ptr, sync::Arc, vec};
use crate::{point, size, Pixels, PointF, Quad, Scene, Size};
use bytemuck::{Pod, Zeroable};
use cocoa::{
base::{NO, YES},
foundation::NSUInteger,
quartzcore::AutoresizingMask,
};
use metal::{CommandQueue, MTLPixelFormat, MTLResourceOptions, NSRange};
use objc::{self, msg_send, sel, sel_impl};
use std::{ffi::c_void, mem, ptr};
// use crate::{Quad, Scene};
const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
const INSTANCE_BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
// const SHADERS_METALLIB: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
// const BUFFER_SIZE: usize = 8192 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
pub struct Renderer {
layer: metal::MetalLayer,
command_queue: CommandQueue,
quad_pipeline_state: metal::RenderPipelineState,
unit_vertices: metal::Buffer,
instances: metal::Buffer,
}
// pub struct Renderer {
// layer: metal::MetalLayer,
// command_queue: CommandQueue,
// quad_pipeline_state: metal::RenderPipelineState,
// buffer: metal::Buffer,
// }
impl Renderer {
pub fn new(is_opaque: bool) -> Self {
const PIXEL_FORMAT: MTLPixelFormat = MTLPixelFormat::BGRA8Unorm;
// impl Renderer {
// pub fn new(is_opaque: bool, fonts: Arc<dyn platform::FontSystem>) -> Self {
// const PIXEL_FORMAT: MTLPixelFormat = MTLPixelFormat::BGRA8Unorm;
let device: metal::Device = if let Some(device) = metal::Device::system_default() {
device
} else {
log::error!("unable to access a compatible graphics device");
std::process::exit(1);
};
// let device: metal::Device = if let Some(device) = metal::Device::system_default() {
// device
// } else {
// log::error!("unable to access a compatible graphics device");
// std::process::exit(1);
// };
let layer = metal::MetalLayer::new();
layer.set_device(&device);
layer.set_pixel_format(PIXEL_FORMAT);
layer.set_presents_with_transaction(true);
layer.set_opaque(is_opaque);
unsafe {
let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO];
let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES];
let _: () = msg_send![
&*layer,
setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
| AutoresizingMask::HEIGHT_SIZABLE
];
}
// let layer = metal::MetalLayer::new();
// layer.set_device(&device);
// layer.set_pixel_format(PIXEL_FORMAT);
// layer.set_presents_with_transaction(true);
// layer.set_opaque(is_opaque);
// unsafe {
// let _: () = msg_send![&*layer, setAllowsNextDrawableTimeout: NO];
// let _: () = msg_send![&*layer, setNeedsDisplayOnBoundsChange: YES];
// let _: () = msg_send![
// &*layer,
// setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
// | AutoresizingMask::HEIGHT_SIZABLE
// ];
// }
let library = device
.new_library_with_data(SHADERS_METALLIB)
.expect("error building metal library");
// let library = device
// .new_library_with_data(SHADERS_METALLIB)
// .expect("error building metal library");
let unit_vertices = [point(1., 1.), point(1., 0.), point(0., 0.), point(0., 1.)];
let unit_vertices = device.new_buffer_with_data(
unit_vertices.as_ptr() as *const c_void,
(unit_vertices.len() * mem::size_of::<PointF>()) as u64,
MTLResourceOptions::StorageModeManaged,
);
let instances = device.new_buffer(
INSTANCE_BUFFER_SIZE as u64,
MTLResourceOptions::StorageModeManaged,
);
// let buffer = device.new_buffer(BUFFER_SIZE as u64, MTLResourceOptions::StorageModeManaged);
let quad_pipeline_state = build_pipeline_state(
&device,
&library,
"quad",
"quad_vertex",
"quad_fragment",
PIXEL_FORMAT,
);
// let quad_pipeline_state = build_pipeline_state(
// &device,
// &library,
// "quad",
// "quad_vertex",
// "quad_fragment",
// PIXEL_FORMAT,
// );
Self {
layer,
command_queue: device.new_command_queue(),
quad_pipeline_state,
unit_vertices,
instances,
}
}
// Self {
// layer,
// command_queue: device.new_command_queue(),
// quad_pipeline_state,
// buffer,
// }
// }
pub fn draw(&mut self, scene: &Scene, scale_factor: f32) {
let layer = self.layer.clone();
let viewport_size = layer.drawable_size();
let viewport_size: Size<Pixels> =
size(viewport_size.width.into(), viewport_size.height.into());
let drawable = if let Some(drawable) = layer.next_drawable() {
drawable
} else {
log::error!(
"failed to retrieve next drawable, drawable size: {:?}",
viewport_size
);
return;
};
let command_queue = self.command_queue.clone();
let command_buffer = command_queue.new_command_buffer();
let render_pass_descriptor = metal::RenderPassDescriptor::new();
let color_attachment = render_pass_descriptor
.color_attachments()
.object_at(0)
.unwrap();
color_attachment.set_texture(Some(drawable.texture()));
color_attachment.set_load_action(metal::MTLLoadAction::Clear);
color_attachment.set_store_action(metal::MTLStoreAction::Store);
let alpha = if self.layer.is_opaque() { 1. } else { 0. };
color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., alpha));
let command_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
// pub fn draw(&mut self, scene: &Scene) {
// draw_quads(scene);
// }
command_encoder.set_viewport(metal::MTLViewport {
originX: 0.0,
originY: 0.0,
width: viewport_size.width.into(),
height: viewport_size.height.into(),
znear: 0.0,
zfar: 1.0,
});
// fn draw_quads(
// &mut self,
// quads: &[Quad],
// scale_factor: f32,
// offset: &mut usize,
// drawable_size: Vector2F,
// command_encoder: &metal::RenderCommandEncoderRef,
// ) {
// if quads.is_empty() {
// return;
// }
// align_offset(offset);
// let next_offset = *offset + quads.len() * mem::size_of::<shaders::GPUIQuad>();
// assert!(
// next_offset <= INSTANCE_BUFFER_SIZE,
// "instance buffer exhausted"
// );
let mut buffer_offset = 0;
self.draw_quads(
&scene.opaque_primitives().quads,
&mut buffer_offset,
scale_factor,
viewport_size,
scene.max_order(),
command_encoder,
);
// command_encoder.set_render_pipeline_state(&self.quad_pipeline_state);
// command_encoder.set_vertex_buffer(
// shaders::GPUIQuadInputIndex_GPUIQuadInputIndexVertices as u64,
// Some(&self.unit_vertices),
// 0,
// );
// command_encoder.set_vertex_buffer(
// shaders::GPUIQuadInputIndex_GPUIQuadInputIndexQuads as u64,
// Some(&self.instances),
// *offset as u64,
// );
// command_encoder.set_vertex_bytes(
// shaders::GPUIQuadInputIndex_GPUIQuadInputIndexUniforms as u64,
// mem::size_of::<shaders::GPUIUniforms>() as u64,
// [shaders::GPUIUniforms {
// viewport_size: drawable_size.to_float2(),
// }]
// .as_ptr() as *const c_void,
// );
self.instances.did_modify_range(NSRange {
location: 0,
length: buffer_offset as NSUInteger,
});
command_buffer.commit();
command_buffer.wait_until_completed();
drawable.present();
}
// let buffer_contents = unsafe {
// (self.instances.contents() as *mut u8).add(*offset) as *mut shaders::GPUIQuad
// };
// for (ix, quad) in quads.iter().enumerate() {
// let bounds = quad.bounds * scale_factor;
// let shader_quad = shaders::GPUIQuad {
// origin: bounds.origin().round().to_float2(),
// size: bounds.size().round().to_float2(),
// background_color: quad
// .background
// .unwrap_or_else(Color::transparent_black)
// .to_uchar4(),
// border_top: quad.border.top * scale_factor,
// border_right: quad.border.right * scale_factor,
// border_bottom: quad.border.bottom * scale_factor,
// border_left: quad.border.left * scale_factor,
// border_color: quad.border.color.to_uchar4(),
// corner_radius_top_left: quad.corner_radii.top_left * scale_factor,
// corner_radius_top_right: quad.corner_radii.top_right * scale_factor,
// corner_radius_bottom_right: quad.corner_radii.bottom_right * scale_factor,
// corner_radius_bottom_left: quad.corner_radii.bottom_left * scale_factor,
// };
// unsafe {
// *(buffer_contents.add(ix)) = shader_quad;
// }
// }
fn draw_quads(
&mut self,
quads: &[Quad],
offset: &mut usize,
scale_factor: f32,
viewport_size: Size<Pixels>,
max_order: u32,
command_encoder: &metal::RenderCommandEncoderRef,
) {
if quads.is_empty() {
return;
}
align_offset(offset);
// command_encoder.draw_primitives_instanced(
// metal::MTLPrimitiveType::Triangle,
// 0,
// 6,
// quads.len() as u64,
// );
// *offset = next_offset;
// }
// }
command_encoder.set_render_pipeline_state(&self.quad_pipeline_state);
command_encoder.set_vertex_buffer(
QuadInputIndex::Vertices as u64,
Some(&self.unit_vertices),
0,
);
command_encoder.set_vertex_buffer(
QuadInputIndex::Quads as u64,
Some(&self.instances),
*offset as u64,
);
let quad_uniforms = QuadUniforms {
viewport_size,
scale_factor,
max_order,
};
let quad_uniform_bytes = bytemuck::bytes_of(&quad_uniforms);
command_encoder.set_vertex_bytes(
QuadInputIndex::Uniforms as u64,
quad_uniform_bytes.len() as u64,
quad_uniform_bytes.as_ptr() as *const c_void,
);
let quad_bytes = bytemuck::cast_slice(quads);
let buffer_contents = unsafe { (self.instances.contents() as *mut u8).add(*offset) };
unsafe {
ptr::copy_nonoverlapping(quad_bytes.as_ptr(), buffer_contents, quad_bytes.len());
}
let next_offset = *offset + quad_bytes.len();
assert!(
next_offset <= INSTANCE_BUFFER_SIZE,
"instance buffer exhausted"
);
command_encoder.draw_primitives_instanced(
metal::MTLPrimitiveType::TriangleStrip,
0,
4,
quads.len() as u64,
);
*offset = next_offset;
}
}
fn build_pipeline_state(
device: &metal::DeviceRef,
library: &metal::LibraryRef,
label: &str,
vertex_fn_name: &str,
fragment_fn_name: &str,
pixel_format: metal::MTLPixelFormat,
) -> metal::RenderPipelineState {
let vertex_fn = library
.get_function(vertex_fn_name, None)
.expect("error locating vertex function");
let fragment_fn = library
.get_function(fragment_fn_name, None)
.expect("error locating fragment function");
let descriptor = metal::RenderPipelineDescriptor::new();
descriptor.set_label(label);
descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
color_attachment.set_pixel_format(pixel_format);
color_attachment.set_blending_enabled(true);
color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha);
color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::One);
color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::One);
device
.new_render_pipeline_state(&descriptor)
.expect("could not create render pipeline state")
}
// Align to multiples of 256 make Metal happy.
fn align_offset(offset: &mut usize) {
*offset = ((*offset + 255) / 256) * 256;
}
#[repr(C)]
enum QuadInputIndex {
Vertices,
Quads,
Uniforms,
}
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub(crate) struct QuadUniforms {
viewport_size: Size<Pixels>,
scale_factor: f32,
max_order: u32,
}

View file

@ -1 +1,190 @@
#include <metal_stdlib>
#include <simd/simd.h>
using namespace metal;
float4 hsla_to_rgba(Hsla hsla);
float4 to_device_position(float2 pixel_position, uint order, uint max_order, float2 viewport_size);
struct QuadVertexOutput {
float4 position [[position]];
uint quad_id;
};
vertex QuadVertexOutput quad_vertex(
uint unit_vertex_id [[vertex_id]],
uint quad_id [[instance_id]],
constant float2 *unit_vertices [[buffer(QuadInputIndex_Vertices)]],
constant Quad *quads [[buffer(QuadInputIndex_Quads)]],
constant QuadUniforms *uniforms [[buffer(QuadInputIndex_Uniforms)]]
) {
float2 unit_vertex = unit_vertices[unit_vertex_id];
Quad quad = quads[quad_id];
float2 position_2d = unit_vertex * float2(quad.bounds.size.width, quad.bounds.size.height) + float2(quad.bounds.origin.x, quad.bounds.origin.y);
float2 viewport_size = float2(uniforms->viewport_size.width, uniforms->viewport_size.height);
float4 device_position = to_device_position(position_2d, quad.order, uniforms->max_order, viewport_size);
return QuadVertexOutput { device_position, quad_id };
}
fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]], constant Quad *quads [[buffer(QuadInputIndex_Quads)]]) {
Quad quad = quads[input.quad_id];
float2 half_size = float2( quad.bounds.size.width, quad.bounds.size.height ) / 2.;
float2 center = float2( quad.bounds.origin.x, quad.bounds.origin.y ) + half_size;
float2 center_to_point = input.position.xy - center;
float corner_radius;
if (center_to_point.x < 0.) {
if (center_to_point.y < 0.) {
corner_radius = quad.corner_radii.top_left;
} else {
corner_radius = quad.corner_radii.bottom_left;
}
} else {
if (center_to_point.y < 0.) {
corner_radius = quad.corner_radii.top_right;
} else {
corner_radius = quad.corner_radii.bottom_right;
}
}
float2 rounded_edge_to_point = fabs(center_to_point) - half_size + corner_radius;
float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - corner_radius;
float vertical_border = center_to_point.x <= 0. ? quad.border_widths.left : quad.border_widths.right;
float horizontal_border = center_to_point.y <= 0. ? quad.border_widths.top : quad.border_widths.bottom;
float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border);
float2 point_to_inset_corner = fabs(center_to_point) - inset_size;
float border_width;
if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
border_width = 0.;
} else if (point_to_inset_corner.y > point_to_inset_corner.x) {
border_width = horizontal_border;
} else {
border_width = vertical_border;
}
float4 color;
if (border_width == 0.) {
color = float4(quad.background.h, quad.background.s, quad.background.l, quad.background.a);
} else {
float inset_distance = distance + border_width;
// Decrease border's opacity as we move inside the background.
quad.border_color.a *= 1. - saturate(0.5 - inset_distance);
// Alpha-blend the border and the background.
float output_alpha = quad.border_color.a + quad.background.a * (1. - quad.border_color.a);
float3 premultiplied_border_rgb = float3(quad.border_color.h, quad.border_color.s, quad.border_color.l) * quad.border_color.a;
float3 premultiplied_background_rgb = float3(quad.background.h, quad.background.s, quad.background.l) * quad.background.a;
float3 premultiplied_output_rgb = premultiplied_border_rgb + premultiplied_background_rgb * (1. - quad.border_color.a);
color = float4(premultiplied_output_rgb.x, premultiplied_output_rgb.y, premultiplied_output_rgb.z, output_alpha);
}
return color;
}
float4 hsla_to_rgba(Hsla hsla) {
float h = hsla.h;
float s = hsla.s;
float l = hsla.l;
float a = hsla.a;
float c = (1.0 - fabs(2.0*l - 1.0)) * s;
float x = c * (1.0 - fabs(fmod(h, 2.0) - 1.0));
float m = l - c/2.0;
float r = 0.0;
float g = 0.0;
float b = 0.0;
if (h >= 0.0 && h < 1.0) {
r = c;
g = x;
b = 0.0;
} else if (h >= 1.0 && h < 2.0) {
r = x;
g = c;
b = 0.0;
} else if (h >= 2.0 && h < 3.0) {
r = 0.0;
g = c;
b = x;
} else if (h >= 3.0 && h < 4.0) {
r = 0.0;
g = x;
b = c;
} else if (h >= 4.0 && h < 5.0) {
r = x;
g = 0.0;
b = c;
} else {
r = c;
g = 0.0;
b = x;
}
float4 rgba;
rgba.x = (r + m);
rgba.y = (g + m);
rgba.z = (b + m);
rgba.w = a;
return rgba;
}
float4 to_device_position(float2 pixel_position, uint order, uint max_order, float2 viewport_size) {
return float4(pixel_position / viewport_size * float2(2., -2.) + float2(-1., 1.), (1. - order / max_order), 1.);
}
// fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]]) {
// float2 half_size = input.size / 2.;
// float2 center = input.origin + half_size;
// float2 center_to_point = input.position.xy - center;
// float corner_radius;
// if (center_to_point.x < 0.) {
// if (center_to_point.y < 0.) {
// corner_radius = input.corner_radius_top_left;
// } else {
// corner_radius = input.corner_radius_bottom_left;
// }
// } else {
// if (center_to_point.y < 0.) {
// corner_radius = input.corner_radius_top_right;
// } else {
// corner_radius = input.corner_radius_bottom_right;
// }
// }
// float2 rounded_edge_to_point = fabs(center_to_point) - half_size + corner_radius;
// float distance = length(max(0., rounded_edge_to_point)) + min(0., max(rounded_edge_to_point.x, rounded_edge_to_point.y)) - corner_radius;
// float vertical_border = center_to_point.x <= 0. ? input.border_left : input.border_right;
// float horizontal_border = center_to_point.y <= 0. ? input.border_top : input.border_bottom;
// float2 inset_size = half_size - corner_radius - float2(vertical_border, horizontal_border);
// float2 point_to_inset_corner = fabs(center_to_point) - inset_size;
// float border_width;
// if (point_to_inset_corner.x < 0. && point_to_inset_corner.y < 0.) {
// border_width = 0.;
// } else if (point_to_inset_corner.y > point_to_inset_corner.x) {
// border_width = horizontal_border;
// } else {
// border_width = vertical_border;
// }
// float4 color;
// if (border_width == 0.) {
// color = input.background_color;
// } else {
// float inset_distance = distance + border_width;
// // Decrease border's opacity as we move inside the background.
// input.border_color.a *= 1. - saturate(0.5 - inset_distance);
// // Alpha-blend the border and the background.
// float output_alpha = input.border_color.a + input.background_color.a * (1. - input.border_color.a);
// float3 premultiplied_border_rgb = input.border_color.rgb * input.border_color.a;
// float3 premultiplied_background_rgb = input.background_color.rgb * input.background_color.a;
// float3 premultiplied_output_rgb = premultiplied_border_rgb + premultiplied_background_rgb * (1. - input.border_color.a);
// color = float4(premultiplied_output_rgb / output_alpha, output_alpha);
// }
// return color * float4(1., 1., 1., saturate(0.5 - distance));
// }

View file

@ -1,12 +1,18 @@
use std::cmp;
use super::{Bounds, Hsla, Pixels, Point};
use crate::{Corners, Edges, FontId, GlyphId};
use bytemuck::{Pod, Zeroable};
use plane_split::BspSplitter;
// Exported to metal
pub type PointF = Point<f32>;
pub struct Scene {
opaque_primitives: PrimitiveBatch,
transparent_primitives: slotmap::SlotMap<slotmap::DefaultKey, Primitive>,
splitter: BspSplitter<slotmap::DefaultKey>,
max_order: u32,
}
impl Scene {
@ -15,14 +21,17 @@ impl Scene {
opaque_primitives: PrimitiveBatch::default(),
transparent_primitives: slotmap::SlotMap::new(),
splitter: BspSplitter::new(),
max_order: 0,
}
}
pub fn insert(&mut self, primitive: impl Into<Primitive>, is_transparent: bool) {
let primitive = primitive.into();
self.max_order = cmp::max(self.max_order, primitive.order());
if is_transparent {
self.transparent_primitives.insert(primitive.into());
self.transparent_primitives.insert(primitive);
} else {
match primitive.into() {
match primitive {
Primitive::Quad(quad) => self.opaque_primitives.quads.push(quad),
Primitive::Glyph(glyph) => self.opaque_primitives.glyphs.push(glyph),
Primitive::Underline(underline) => {
@ -35,6 +44,10 @@ impl Scene {
pub fn opaque_primitives(&self) -> &PrimitiveBatch {
&self.opaque_primitives
}
pub fn max_order(&self) -> u32 {
self.max_order
}
}
#[derive(Clone, Debug)]
@ -45,6 +58,14 @@ pub enum Primitive {
}
impl Primitive {
pub fn order(&self) -> u32 {
match self {
Primitive::Quad(quad) => quad.order,
Primitive::Glyph(glyph) => glyph.order,
Primitive::Underline(underline) => underline.order,
}
}
pub fn is_transparent(&self) -> bool {
match self {
Primitive::Quad(quad) => {
@ -63,10 +84,10 @@ pub struct PrimitiveBatch {
pub underlines: Vec<Underline>,
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
#[repr(C)]
pub struct Quad {
pub order: f32,
pub order: u32,
pub bounds: Bounds<Pixels>,
pub clip_bounds: Bounds<Pixels>,
pub clip_corner_radii: Corners<Pixels>,
@ -92,29 +113,16 @@ impl Quad {
}
}
unsafe impl Zeroable for Quad {}
unsafe impl Pod for Quad {}
impl From<Quad> for Primitive {
fn from(quad: Quad) -> Self {
Primitive::Quad(quad)
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub(crate) struct QuadUniforms {
viewport_size: [f32; 2],
}
unsafe impl Zeroable for QuadUniforms {}
unsafe impl Pod for QuadUniforms {}
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct RenderedGlyph {
pub order: u32,
pub font_id: FontId,
pub font_size: f32,
pub id: GlyphId,
@ -128,19 +136,27 @@ impl From<RenderedGlyph> for Primitive {
}
}
#[derive(Copy, Clone, Default, Debug)]
#[derive(Copy, Clone, Default, Debug, Zeroable, Pod)]
#[repr(C)]
pub struct Underline {
pub order: u32,
pub origin: Point<Pixels>,
pub width: Pixels,
pub thickness: Pixels,
pub color: Hsla,
pub squiggly: bool,
pub style: LineStyle,
}
unsafe impl Zeroable for Underline {}
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
#[repr(C)]
pub enum LineStyle {
#[default]
Solid = 0,
Squiggly = 1,
}
unsafe impl Pod for Underline {}
unsafe impl Zeroable for LineStyle {}
unsafe impl Pod for LineStyle {}
impl From<Underline> for Primitive {
fn from(underline: Underline) -> Self {