mirror of
https://github.com/zed-industries/zed.git
synced 2025-01-27 12:54:42 +00:00
Checkpoint
Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
parent
df388d9f33
commit
3fbe93f029
8 changed files with 501 additions and 176 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
// }
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue