From 4e670903d44346713ef3c98ebdc9e466e01dd1f9 Mon Sep 17 00:00:00 2001 From: Dan Fritchman Date: Tue, 31 Jan 2023 09:13:22 -0800 Subject: [PATCH 1/6] Add layout21wgpu crate --- Cargo.toml | 2 + layout21wgpu/Cargo.toml | 40 +++ layout21wgpu/src/lib.rs | 470 +++++++++++++++++++++++++++++++++++ layout21wgpu/src/main.rs | 5 + layout21wgpu/src/shader.wgsl | 22 ++ 5 files changed, 539 insertions(+) create mode 100644 layout21wgpu/Cargo.toml create mode 100644 layout21wgpu/src/lib.rs create mode 100644 layout21wgpu/src/main.rs create mode 100644 layout21wgpu/src/shader.wgsl diff --git a/Cargo.toml b/Cargo.toml index e580050..648525c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,10 @@ members = [ "layout21raw", "layout21tetris", "layout21utils", + "layout21wgpu", "lef21", ] +resolver = "2" # Inherited Package Attributes # Thanks https://doc.rust-lang.org/cargo/reference/workspaces.html#the-package-table! diff --git a/layout21wgpu/Cargo.toml b/layout21wgpu/Cargo.toml new file mode 100644 index 0000000..fb859d6 --- /dev/null +++ b/layout21wgpu/Cargo.toml @@ -0,0 +1,40 @@ +[package] +description = "Layout21 WGPU" +name = "layout21wgpu" + +# Shared layout21 attributes +authors.workspace = true +categories.workspace = true +documentation.workspace = true +edition.workspace = true +exclude.workspace = true +homepage.workspace = true +include.workspace = true +keywords.workspace = true +license.workspace = true +readme.workspace = true +repository.workspace = true +version.workspace = true +workspace = "../" + +[dependencies] +# Local workspace dependencies +layout21protos = {path = "../layout21protos", version = "3.0.0-pre.3"} +layout21raw = {path = "../layout21raw", version = "3.0.0-pre.3"} +layout21utils = {path = "../layout21utils", version = "3.0.0-pre.3"} + +# Crates.io +wgpu = "0.15" +derive_builder = "0.9" +derive_more = "0.99.16" +num-integer = "0.1" +num-traits = "0.2" +serde = {version = "1.0", features = ["derive"]} +serde_derive = "1.0.88" +slotmap = {version = "1.0", features = ["serde"]} +winit = "0.27.5" +rand = "0.8.5" +log = "0.4.17" +bytemuck = { version = "1.4", features = ["derive"] } +env_logger = "0.10.0" +pollster = "0.2.5" diff --git a/layout21wgpu/src/lib.rs b/layout21wgpu/src/lib.rs new file mode 100644 index 0000000..8fbb433 --- /dev/null +++ b/layout21wgpu/src/lib.rs @@ -0,0 +1,470 @@ +use std::{iter, mem}; + +use bytemuck::{Pod, Zeroable}; + +use layout21protos::{self, conv as proto_converters}; +use log::{debug, error, info, log_enabled, Level}; +use rand::Rng; +use wgpu::util::DeviceExt; +use winit::{ + event::*, + event_loop::{ControlFlow, EventLoop}, + window::{Window, WindowBuilder}, +}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +struct Vertex { + position: [f32; 2], + color: Color, +} +impl Vertex { + const ATTRIBUTES: [wgpu::VertexAttribute; 2] = + wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3]; + fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &Self::ATTRIBUTES, + } + } +} + +struct Rect { + p0: [f32; 2], + p1: [f32; 2], + color: Color, +} +impl Rect { + /// Triangulated vertices + fn vertices(&self) -> [Vertex; 6] { + let Rect { p0, p1, color } = self; + let [x0, y0] = *p0; + let [x1, y1] = *p1; + [ + Vertex { + position: [x0, y0], + color: *color, + }, + Vertex { + position: [x1, y0], + color: *color, + }, + Vertex { + position: [x0, y1], + color: *color, + }, + Vertex { + position: [x0, y1], + color: *color, + }, + Vertex { + position: [x1, y0], + color: *color, + }, + Vertex { + position: [x1, y1], + color: *color, + }, + ] + } +} + +const NUM_RECTS: usize = 10_000; +const NUM_VERTICES: usize = NUM_RECTS * 6; +struct RectHolder { + rects: Vec, + vertices: Vec, +} +impl RectHolder { + /// Generate a random set of rectangles + fn random() -> RectHolder { + let mut rects = Vec::with_capacity(NUM_RECTS); + let mut rng = rand::thread_rng(); + let mut random = || rng.gen_range(-1.0..1.0); + for _ in 0..NUM_RECTS { + let r = Rect { + p0: [random(), random()], + p1: [random(), random()], + color: Color([random(), random(), random()]), + }; + rects.push(r); + } + let mut vertices = Vec::with_capacity(6 * NUM_RECTS); + for rect in rects.iter() { + vertices.extend_from_slice(&rect.vertices()); + } + Self { rects, vertices } + } +} + +// const NUM_TRIANGLES: usize = 100_000; +// const NUM_VERTICES: usize = NUM_TRIANGLES * 3; + +// fn generate_vertices() -> Vec { +// let mut rng = rand::thread_rng(); +// let mut random = || rng.gen_range(-1.0..1.0); +// let mut vertices = Vec::new(); +// for _ in 0..NUM_VERTICES { +// let x = random(); +// let y = random(); +// let color = Color([random(), random(), random()]); +// vertices.push(Vertex { +// position: [x, y], +// color, +// }); +// } +// vertices +// } + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +struct Color(pub [f32; 3]); + +// const RED: Color = Color([1.0, 0.0, 0.0]); +// const GREEN: Color = Color([0.0, 1.0, 0.0]); +// const BLUE: Color = Color([0.0, 0.0, 1.0]); +// const BLACK: Color = Color([0.0, 0.0, 0.0]); +// const WHITE: Color = Color([1.0, 1.0, 1.0]); + +// const VERTICES: &[Vertex] = &[ +// Vertex { +// // vertex a +// position: [-0.5, -0.5], +// color: RED, +// }, +// Vertex { +// // vertex b +// position: [0.5, -0.5], +// color: RED, +// }, +// Vertex { +// // vertex d +// position: [-0.5, 0.5], +// color: RED, +// }, +// Vertex { +// // vertex d +// position: [-0.5, 0.5], +// color: Color([1.0, 1.0, 0.0]), +// }, +// Vertex { +// // vertex b +// position: [0.5, -0.5], +// color: GREEN, +// }, +// Vertex { +// // vertex c +// position: [0.5, 0.5], +// color: BLUE, +// }, +// Vertex { +// position: [-0.75, 0.25], +// color: Color([1.0, 1.0, 0.0]), +// }, +// Vertex { +// position: [0.75, -0.25], +// color: GREEN, +// }, +// Vertex { +// position: [0.15, 0.95], +// color: BLUE, +// }, +// ]; + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + config: wgpu::SurfaceConfiguration, + size: winit::dpi::PhysicalSize, + pipeline: wgpu::RenderPipeline, + vertex_buffer_stuff: VertexBufferStuff, + // vertex_buffer: wgpu::Buffer, + // vertices: Vec, +} +struct OneOfThese { + vertex_buffer: wgpu::Buffer, + vertices: Vec, +} +impl OneOfThese { + fn new(device: &wgpu::Device) -> Self { + // let vertices = generate_vertices(); + let vertices = RectHolder::random().vertices; + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + Self { + vertex_buffer, + vertices, + } + } +} +struct VertexBufferStuff { + one: OneOfThese, + two: OneOfThese, + idx: usize, +} +impl VertexBufferStuff { + fn new(device: &wgpu::Device) -> Self { + let one = OneOfThese::new(device); + let two = OneOfThese::new(device); + Self { one, two, idx: 0 } + } + fn current(&self) -> &OneOfThese { + match self.idx { + 0 => &self.one, + 1 => &self.two, + _ => unreachable!(), + } + } + fn swap(&mut self) { + self.idx = (self.idx + 1) % 2; + } +} + +impl State { + async fn new(window: &Window) -> Self { + let size = window.inner_size(); + let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends, + dx12_shader_compiler: wgpu::Dx12Compiler::default(), + }); + let surface = unsafe { instance.create_surface(window) }.unwrap(); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await + .unwrap(); + + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + }, + None, // Trace path + ) + .await + .unwrap(); + let swapchain_capabilities = surface.get_capabilities(&adapter); + let swapchain_format = swapchain_capabilities.formats[0]; + + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: swapchain_format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Fifo, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: Vec::new(), + }; + surface.configure(&device, &config); + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent::OVER, + alpha: wgpu::BlendComponent::OVER, + }), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + // let vertices = generate_vertices(); + // let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + // label: Some("Vertex Buffer"), + // contents: bytemuck::cast_slice(&vertices), + // usage: wgpu::BufferUsages::VERTEX, + // }); + let vertex_buffer_stuff = VertexBufferStuff::new(&device); + + Self { + surface, + device, + queue, + config, + size, + pipeline, + vertex_buffer_stuff, + // vertex_buffer, + // vertices + } + } + + pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.size = new_size; + self.config.width = new_size.width; + self.config.height = new_size.height; + self.surface.configure(&self.device, &self.config); + } + } + + #[allow(unused_variables)] + fn input(&mut self, event: &WindowEvent) -> bool { + false + } + + fn update(&mut self) {} + + fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + //let output = self.surface.get_current_frame()?.output; + // self.vertex_buffer_stuff = VertexBufferStuff::new(&self.device); + // self.vertices = generate_vertices(); + // self.vertex_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + // label: Some("Vertex Buffer"), + // contents: bytemuck::cast_slice(&self.vertices), + // usage: wgpu::BufferUsages::VERTEX, + // }); + + self.vertex_buffer_stuff.swap(); + + let output = self.surface.get_current_texture()?; + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.2, + g: 0.247, + b: 0.314, + a: 1.0, + }), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.pipeline); + render_pass.set_vertex_buffer(0, self.vertex_buffer().slice(..)); + render_pass.draw(0..NUM_VERTICES as u32, 0..1); + } + + self.queue.submit(iter::once(encoder.finish())); + output.present(); + + Ok(()) + } + fn vertex_buffer(&self) -> &wgpu::Buffer { + &self.vertex_buffer_stuff.current().vertex_buffer + } +} + +pub fn run() { + env_logger::init(); + let something: layout21protos::Library = + proto_converters::open(&resource("sky130_fd_sc_hd__dfxtp_1.pb")).unwrap(); + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + window.set_title(&*format!("{}", "Layout21 Viewer")); + let mut state = pollster::block_on(State::new(&window)); + + error!("START!!!"); + + event_loop.run(move |event, _, control_flow| match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => { + if !state.input(event) { + match event { + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + input: + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + }, + .. + } => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(physical_size) => { + state.resize(*physical_size); + window.request_redraw(); + } + WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + state.resize(**new_inner_size); + } + _ => {} + } + } + } + Event::RedrawRequested(_) => { + error!("REDRAW!!!"); + state.update(); + match state.render() { + Ok(_) => {} + Err(wgpu::SurfaceError::Lost) => state.resize(state.size), + Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, + Err(e) => eprintln!("{:?}", e), + } + } + Event::MainEventsCleared => { + error!("REQUESTING!!!"); + window.request_redraw(); + } + _ => {} + }); +} + +/// Grab the full path of resource-file `fname` +fn resource(rname: &str) -> String { + format!( + "{}/../layout21converters/resources/{}", + env!("CARGO_MANIFEST_DIR"), + rname + ) +} diff --git a/layout21wgpu/src/main.rs b/layout21wgpu/src/main.rs new file mode 100644 index 0000000..b05b64e --- /dev/null +++ b/layout21wgpu/src/main.rs @@ -0,0 +1,5 @@ +use layout21wgpu::run; + +fn main() { + run() +} diff --git a/layout21wgpu/src/shader.wgsl b/layout21wgpu/src/shader.wgsl new file mode 100644 index 0000000..8d3211c --- /dev/null +++ b/layout21wgpu/src/shader.wgsl @@ -0,0 +1,22 @@ +struct VertexInput { + @location(0) pos: vec2, + @location(1) color: vec3, +}; + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) color: vec4, +}; + +@vertex +fn vs_main(in: VertexInput) -> VertexOutput { + var out: VertexOutput; + out.color = vec4(in.color, 0.5); + out.position = vec4(in.pos, 0.0, 1.0); + return out; +} + +@fragment +fn fs_main(in: VertexOutput) -> @location(0) vec4 { + return in.color; +} \ No newline at end of file From e2989e743fd4fe88d6906a16aaeac8f70b0b5efc Mon Sep 17 00:00:00 2001 From: Dan Fritchman Date: Fri, 3 Feb 2023 17:15:51 -0800 Subject: [PATCH 2/6] WIP layout -> wgpu --- layout21wgpu/src/lib.rs | 71 +++++++++++++++++++++++++++++++++-------- 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/layout21wgpu/src/lib.rs b/layout21wgpu/src/lib.rs index 8fbb433..2d96ffa 100644 --- a/layout21wgpu/src/lib.rs +++ b/layout21wgpu/src/lib.rs @@ -1,8 +1,10 @@ -use std::{iter, mem}; +use std::{iter, marker::PhantomData, mem}; use bytemuck::{Pod, Zeroable}; use layout21protos::{self, conv as proto_converters}; +use layout21raw as raw; +use layout21utils::Ptr; use log::{debug, error, info, log_enabled, Level}; use rand::Rng; use wgpu::util::DeviceExt; @@ -12,6 +14,43 @@ use winit::{ window::{Window, WindowBuilder}, }; +#[derive(Debug)] +struct LayoutDisplay { + lib: raw::Library, + cell: Ptr, + bbox: Option<[[raw::Int; 2]; 2]>, +} +impl LayoutDisplay { + fn from_proto() -> Self { + let proto_lib: layout21protos::Library = + proto_converters::open(&resource("sky130_fd_sc_hd__dfxtp_1.pb")).unwrap(); + let rawlib = raw::Library::from_proto(proto_lib, None).unwrap(); + Self::from_rawlib(rawlib) + } + fn from_rawlib(rawlib: raw::Library) -> Self { + let cell = rawlib.cells[0].clone(); + + Self { + lib: rawlib, + cell, + bbox: None, + } + } + fn something(&self) { + let bbox = self.bbox.unwrap().clone(); + let cell = self.cell.read().unwrap(); + let layout = cell.layout.as_ref().unwrap(); + for elem in &layout.elems { + let shape = &elem.inner; + match shape { + raw::Shape::Rect(r) => unimplemented!(), + raw::Shape::Path(p) => unimplemented!(), + raw::Shape::Polygon(p) => unimplemented!(), + } + } + } +} + #[repr(C)] #[derive(Copy, Clone, Debug, Pod, Zeroable)] struct Vertex { @@ -172,17 +211,6 @@ struct Color(pub [f32; 3]); // }, // ]; -struct State { - surface: wgpu::Surface, - device: wgpu::Device, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - size: winit::dpi::PhysicalSize, - pipeline: wgpu::RenderPipeline, - vertex_buffer_stuff: VertexBufferStuff, - // vertex_buffer: wgpu::Buffer, - // vertices: Vec, -} struct OneOfThese { vertex_buffer: wgpu::Buffer, vertices: Vec, @@ -225,6 +253,19 @@ impl VertexBufferStuff { } } +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + config: wgpu::SurfaceConfiguration, + size: winit::dpi::PhysicalSize, + pipeline: wgpu::RenderPipeline, + vertex_buffer_stuff: VertexBufferStuff, + layout_display: LayoutDisplay, + // vertex_buffer: wgpu::Buffer, + // vertices: Vec, +} + impl State { async fn new(window: &Window) -> Self { let size = window.inner_size(); @@ -317,6 +358,8 @@ impl State { // }); let vertex_buffer_stuff = VertexBufferStuff::new(&device); + let layout_display = LayoutDisplay::from_proto(); + Self { surface, device, @@ -325,6 +368,7 @@ impl State { size, pipeline, vertex_buffer_stuff, + layout_display, // vertex_buffer, // vertices } @@ -405,8 +449,7 @@ impl State { pub fn run() { env_logger::init(); - let something: layout21protos::Library = - proto_converters::open(&resource("sky130_fd_sc_hd__dfxtp_1.pb")).unwrap(); + let event_loop = EventLoop::new(); let window = WindowBuilder::new().build(&event_loop).unwrap(); window.set_title(&*format!("{}", "Layout21 Viewer")); From e4cf153a43d3fdafcb1f042d53969544012962e4 Mon Sep 17 00:00:00 2001 From: Dan Fritchman Date: Wed, 8 Feb 2023 14:46:43 -0800 Subject: [PATCH 3/6] Initial WGPU rendering rects & paths --- layout21raw/src/geom.rs | 54 ++++++----- layout21wgpu/src/lib.rs | 176 +++++++++++++++++++++++------------ layout21wgpu/src/shader.wgsl | 2 +- 3 files changed, 147 insertions(+), 85 deletions(-) diff --git a/layout21raw/src/geom.rs b/layout21raw/src/geom.rs index 841e3a2..a0b95ae 100644 --- a/layout21raw/src/geom.rs +++ b/layout21raw/src/geom.rs @@ -100,6 +100,32 @@ pub struct Path { pub points: Vec, pub width: usize, } +impl Path { + /// Convert a Manhattan path into a vector of rectangles. + /// Returns `None` if the path is not Manhattan. + pub fn rects(&self) -> Option> { + let (points, width) = (&self.points, self.width); + let width = Int::try_from(width).unwrap(); // FIXME: probably store these signed, check them on creation + let mut rects: Vec = Vec::with_capacity(self.points.len()); + for k in 0..points.len() - 1 { + let rect = if points[k].x == points[k + 1].x { + Rect { + p0: Point::new(points[k].x - width / 2, points[k].y), + p1: Point::new(points[k].x + width / 2, points[k + 1].y), + } + } else if points[k].y == points[k + 1].y { + Rect { + p0: Point::new(points[k].x, points[k].y - width / 2), + p1: Point::new(points[k + 1].x, points[k].y + width / 2), + } + } else { + return None; // Non-Manhattan Path + }; + rects.push(rect); + } + Some(rects) + } +} /// # Polygon /// /// Closed n-sided polygon with arbitrary number of vertices. @@ -314,33 +340,15 @@ impl ShapeTrait for Path { /// Boolean indication of whether the [Shape] contains [Point] `pt`. /// Containment is *inclusive* for all [Shape] types. /// [Point]s on their boundary, which generally include all points specifying the shape itself, are regarded throughout as "inside" the shape. + /// + /// Note checks for [`Path`] are valid solely for Manhattan paths, i.e. those with segments solely running vertically or horizontally. fn contains(&self, pt: &Point) -> bool { - // Break into segments, and check for intersection with each - // Probably not the most efficient way to do this, but a start. - // Only "Manhattan paths", i.e. those with segments solely running vertically or horizontally, are supported. // FIXME: even with this method, there are some small pieces at corners which we'll miss. // Whether these are relevant in real life, tbd. - let (points, width) = (&self.points, self.width); - let width = Int::try_from(width).unwrap(); // FIXME: probably store these signed, check them on creation - for k in 0..points.len() - 1 { - let rect = if points[k].x == points[k + 1].x { - Rect { - p0: Point::new(points[k].x - width / 2, points[k].y), - p1: Point::new(points[k].x + width / 2, points[k + 1].y), - } - } else if points[k].y == points[k + 1].y { - Rect { - p0: Point::new(points[k].x, points[k].y - width / 2), - p1: Point::new(points[k + 1].x, points[k].y + width / 2), - } - } else { - unimplemented!("Unsupported Non-Manhattan Path") - }; - if rect.contains(pt) { - return true; - } + match self.rects() { + None => false, // FIXME! non-Manhattan paths + Some(rects) => rects.iter().any(|r| r.contains(pt)), } - false } fn to_poly(&self) -> Polygon { unimplemented!("Path::to_poly") diff --git a/layout21wgpu/src/lib.rs b/layout21wgpu/src/lib.rs index 2d96ffa..7f537cd 100644 --- a/layout21wgpu/src/lib.rs +++ b/layout21wgpu/src/lib.rs @@ -1,4 +1,4 @@ -use std::{iter, marker::PhantomData, mem}; +use std::{collections::HashMap, iter, marker::PhantomData, mem}; use bytemuck::{Pod, Zeroable}; @@ -14,40 +14,120 @@ use winit::{ window::{Window, WindowBuilder}, }; +#[derive(Debug, Clone, Copy)] +struct Tbd; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +struct Color(pub [f32; 3]); + +const RED: Color = Color([1.0, 0.0, 0.0]); +const GREEN: Color = Color([0.0, 1.0, 0.0]); +const BLUE: Color = Color([0.0, 0.0, 1.0]); +const BLACK: Color = Color([0.0, 0.0, 0.0]); +const WHITE: Color = Color([1.0, 1.0, 1.0]); + +const colors: [Color; 4] = [RED, GREEN, BLUE, WHITE]; +#[derive(Debug)] +struct ColorWheel { + index: usize, +} +impl ColorWheel { + fn next(&mut self) -> Color { + let color = colors[self.index]; + self.index = (self.index + 1) % colors.len(); + color + } +} + #[derive(Debug)] struct LayoutDisplay { + // Data elements lib: raw::Library, cell: Ptr, - bbox: Option<[[raw::Int; 2]; 2]>, + // Rendering elements + bbox: [[raw::Int; 2]; 2], + vertices: Vec, + layer_colors: HashMap, } impl LayoutDisplay { fn from_proto() -> Self { let proto_lib: layout21protos::Library = proto_converters::open(&resource("sky130_fd_sc_hd__dfxtp_1.pb")).unwrap(); let rawlib = raw::Library::from_proto(proto_lib, None).unwrap(); - Self::from_rawlib(rawlib) - } - fn from_rawlib(rawlib: raw::Library) -> Self { let cell = rawlib.cells[0].clone(); - - Self { - lib: rawlib, - cell, - bbox: None, - } + Self::build(rawlib, cell) } - fn something(&self) { - let bbox = self.bbox.unwrap().clone(); - let cell = self.cell.read().unwrap(); - let layout = cell.layout.as_ref().unwrap(); + fn build(rawlib: raw::Library, cell: Ptr) -> Self { + let cell1 = cell.read().unwrap(); + let layout = cell1.layout.as_ref().unwrap(); + + let mut layer_colors: HashMap = HashMap::new(); + + let mut rects: Vec = Vec::with_capacity(layout.elems.len()); + // let bbox = self.bbox.unwrap().clone(); + let bbox = [[0, 10_000], [0, 4000]]; + + let mut color_wheel = ColorWheel { index: 0 }; + for elem in &layout.elems { + let color = layer_colors + .entry(elem.layer) + .or_insert_with(|| color_wheel.next()); let shape = &elem.inner; match shape { - raw::Shape::Rect(r) => unimplemented!(), - raw::Shape::Path(p) => unimplemented!(), - raw::Shape::Polygon(p) => unimplemented!(), + raw::Shape::Rect(r) => { + let rect = Rect { + p0: [ + r.p0.x as f32 / bbox[0][1] as f32, + r.p0.y as f32 / bbox[1][1] as f32, + ], + p1: [ + r.p1.x as f32 / bbox[0][1] as f32, + r.p1.y as f32 / bbox[1][1] as f32, + ], + color: color_wheel.next(), + }; + rects.push(rect); + } + raw::Shape::Path(p) => { + let path_rects = p.rects(); + if path_rects.is_none(){ + continue; + } + let path_rects = path_rects.unwrap(); + for r in path_rects.iter() { + let rect = Rect { + p0: [ + r.p0.x as f32 / bbox[0][1] as f32, + r.p0.y as f32 / bbox[1][1] as f32, + ], + p1: [ + r.p1.x as f32 / bbox[0][1] as f32, + r.p1.y as f32 / bbox[1][1] as f32, + ], + color: color_wheel.next(), + }; + rects.push(rect); + } + } + raw::Shape::Polygon(_) => { + error!("Not implemented: poly") + } } } + + let mut vertices: Vec = Vec::with_capacity(rects.len() * 6); + for rect in rects.iter() { + vertices.extend_from_slice(&rect.vertices()); + } + Self { + lib: rawlib, + cell: cell.clone(), + bbox, + vertices, + layer_colors, + } } } @@ -109,8 +189,7 @@ impl Rect { } } -const NUM_RECTS: usize = 10_000; -const NUM_VERTICES: usize = NUM_RECTS * 6; +// const NUM_VERTICES: usize = NUM_RECTS * 6; struct RectHolder { rects: Vec, vertices: Vec, @@ -118,6 +197,7 @@ struct RectHolder { impl RectHolder { /// Generate a random set of rectangles fn random() -> RectHolder { + let NUM_RECTS: usize = 10_000; let mut rects = Vec::with_capacity(NUM_RECTS); let mut rng = rand::thread_rng(); let mut random = || rng.gen_range(-1.0..1.0); @@ -156,16 +236,6 @@ impl RectHolder { // vertices // } -#[repr(C)] -#[derive(Copy, Clone, Debug, Pod, Zeroable)] -struct Color(pub [f32; 3]); - -// const RED: Color = Color([1.0, 0.0, 0.0]); -// const GREEN: Color = Color([0.0, 1.0, 0.0]); -// const BLUE: Color = Color([0.0, 0.0, 1.0]); -// const BLACK: Color = Color([0.0, 0.0, 0.0]); -// const WHITE: Color = Color([1.0, 1.0, 1.0]); - // const VERTICES: &[Vertex] = &[ // Vertex { // // vertex a @@ -211,46 +281,27 @@ struct Color(pub [f32; 3]); // }, // ]; -struct OneOfThese { +struct VertexBufferStuff { vertex_buffer: wgpu::Buffer, - vertices: Vec, + layout_display: LayoutDisplay, } -impl OneOfThese { +impl VertexBufferStuff { fn new(device: &wgpu::Device) -> Self { + let layout_display = LayoutDisplay::from_proto(); + // let vertices = generate_vertices(); - let vertices = RectHolder::random().vertices; + // let vertices = RectHolder::random().vertices; + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&vertices), + contents: bytemuck::cast_slice(&layout_display.vertices), usage: wgpu::BufferUsages::VERTEX, }); Self { vertex_buffer, - vertices, - } - } -} -struct VertexBufferStuff { - one: OneOfThese, - two: OneOfThese, - idx: usize, -} -impl VertexBufferStuff { - fn new(device: &wgpu::Device) -> Self { - let one = OneOfThese::new(device); - let two = OneOfThese::new(device); - Self { one, two, idx: 0 } - } - fn current(&self) -> &OneOfThese { - match self.idx { - 0 => &self.one, - 1 => &self.two, - _ => unreachable!(), + layout_display, } } - fn swap(&mut self) { - self.idx = (self.idx + 1) % 2; - } } struct State { @@ -400,7 +451,7 @@ impl State { // usage: wgpu::BufferUsages::VERTEX, // }); - self.vertex_buffer_stuff.swap(); + // self.vertex_buffer_stuff.swap(); let output = self.surface.get_current_texture()?; let view = output @@ -434,7 +485,10 @@ impl State { render_pass.set_pipeline(&self.pipeline); render_pass.set_vertex_buffer(0, self.vertex_buffer().slice(..)); - render_pass.draw(0..NUM_VERTICES as u32, 0..1); + render_pass.draw( + 0..self.vertex_buffer_stuff.layout_display.vertices.len() as u32, + 0..1, + ); } self.queue.submit(iter::once(encoder.finish())); @@ -443,7 +497,7 @@ impl State { Ok(()) } fn vertex_buffer(&self) -> &wgpu::Buffer { - &self.vertex_buffer_stuff.current().vertex_buffer + &self.vertex_buffer_stuff.vertex_buffer } } diff --git a/layout21wgpu/src/shader.wgsl b/layout21wgpu/src/shader.wgsl index 8d3211c..657471b 100644 --- a/layout21wgpu/src/shader.wgsl +++ b/layout21wgpu/src/shader.wgsl @@ -11,7 +11,7 @@ struct VertexOutput { @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; - out.color = vec4(in.color, 0.5); + out.color = vec4(in.color, 0.1); out.position = vec4(in.pos, 0.0, 1.0); return out; } From ad293f9d043a021d907624d2363a37e95eb0d3d9 Mon Sep 17 00:00:00 2001 From: Dan Fritchman Date: Wed, 8 Feb 2023 16:05:05 -0800 Subject: [PATCH 4/6] Initial polygon tessellation --- layout21wgpu/Cargo.toml | 1 + layout21wgpu/src/lib.rs | 569 +------------------------------- layout21wgpu/src/run.rs | 606 +++++++++++++++++++++++++++++++++++ layout21wgpu/src/shader.wgsl | 2 +- 4 files changed, 610 insertions(+), 568 deletions(-) create mode 100644 layout21wgpu/src/run.rs diff --git a/layout21wgpu/Cargo.toml b/layout21wgpu/Cargo.toml index fb859d6..1f0d484 100644 --- a/layout21wgpu/Cargo.toml +++ b/layout21wgpu/Cargo.toml @@ -38,3 +38,4 @@ log = "0.4.17" bytemuck = { version = "1.4", features = ["derive"] } env_logger = "0.10.0" pollster = "0.2.5" +lyon = "1.0.1" diff --git a/layout21wgpu/src/lib.rs b/layout21wgpu/src/lib.rs index 7f537cd..39dde27 100644 --- a/layout21wgpu/src/lib.rs +++ b/layout21wgpu/src/lib.rs @@ -1,567 +1,2 @@ -use std::{collections::HashMap, iter, marker::PhantomData, mem}; - -use bytemuck::{Pod, Zeroable}; - -use layout21protos::{self, conv as proto_converters}; -use layout21raw as raw; -use layout21utils::Ptr; -use log::{debug, error, info, log_enabled, Level}; -use rand::Rng; -use wgpu::util::DeviceExt; -use winit::{ - event::*, - event_loop::{ControlFlow, EventLoop}, - window::{Window, WindowBuilder}, -}; - -#[derive(Debug, Clone, Copy)] -struct Tbd; - -#[repr(C)] -#[derive(Copy, Clone, Debug, Pod, Zeroable)] -struct Color(pub [f32; 3]); - -const RED: Color = Color([1.0, 0.0, 0.0]); -const GREEN: Color = Color([0.0, 1.0, 0.0]); -const BLUE: Color = Color([0.0, 0.0, 1.0]); -const BLACK: Color = Color([0.0, 0.0, 0.0]); -const WHITE: Color = Color([1.0, 1.0, 1.0]); - -const colors: [Color; 4] = [RED, GREEN, BLUE, WHITE]; -#[derive(Debug)] -struct ColorWheel { - index: usize, -} -impl ColorWheel { - fn next(&mut self) -> Color { - let color = colors[self.index]; - self.index = (self.index + 1) % colors.len(); - color - } -} - -#[derive(Debug)] -struct LayoutDisplay { - // Data elements - lib: raw::Library, - cell: Ptr, - // Rendering elements - bbox: [[raw::Int; 2]; 2], - vertices: Vec, - layer_colors: HashMap, -} -impl LayoutDisplay { - fn from_proto() -> Self { - let proto_lib: layout21protos::Library = - proto_converters::open(&resource("sky130_fd_sc_hd__dfxtp_1.pb")).unwrap(); - let rawlib = raw::Library::from_proto(proto_lib, None).unwrap(); - let cell = rawlib.cells[0].clone(); - Self::build(rawlib, cell) - } - fn build(rawlib: raw::Library, cell: Ptr) -> Self { - let cell1 = cell.read().unwrap(); - let layout = cell1.layout.as_ref().unwrap(); - - let mut layer_colors: HashMap = HashMap::new(); - - let mut rects: Vec = Vec::with_capacity(layout.elems.len()); - // let bbox = self.bbox.unwrap().clone(); - let bbox = [[0, 10_000], [0, 4000]]; - - let mut color_wheel = ColorWheel { index: 0 }; - - for elem in &layout.elems { - let color = layer_colors - .entry(elem.layer) - .or_insert_with(|| color_wheel.next()); - let shape = &elem.inner; - match shape { - raw::Shape::Rect(r) => { - let rect = Rect { - p0: [ - r.p0.x as f32 / bbox[0][1] as f32, - r.p0.y as f32 / bbox[1][1] as f32, - ], - p1: [ - r.p1.x as f32 / bbox[0][1] as f32, - r.p1.y as f32 / bbox[1][1] as f32, - ], - color: color_wheel.next(), - }; - rects.push(rect); - } - raw::Shape::Path(p) => { - let path_rects = p.rects(); - if path_rects.is_none(){ - continue; - } - let path_rects = path_rects.unwrap(); - for r in path_rects.iter() { - let rect = Rect { - p0: [ - r.p0.x as f32 / bbox[0][1] as f32, - r.p0.y as f32 / bbox[1][1] as f32, - ], - p1: [ - r.p1.x as f32 / bbox[0][1] as f32, - r.p1.y as f32 / bbox[1][1] as f32, - ], - color: color_wheel.next(), - }; - rects.push(rect); - } - } - raw::Shape::Polygon(_) => { - error!("Not implemented: poly") - } - } - } - - let mut vertices: Vec = Vec::with_capacity(rects.len() * 6); - for rect in rects.iter() { - vertices.extend_from_slice(&rect.vertices()); - } - Self { - lib: rawlib, - cell: cell.clone(), - bbox, - vertices, - layer_colors, - } - } -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Pod, Zeroable)] -struct Vertex { - position: [f32; 2], - color: Color, -} -impl Vertex { - const ATTRIBUTES: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3]; - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &Self::ATTRIBUTES, - } - } -} - -struct Rect { - p0: [f32; 2], - p1: [f32; 2], - color: Color, -} -impl Rect { - /// Triangulated vertices - fn vertices(&self) -> [Vertex; 6] { - let Rect { p0, p1, color } = self; - let [x0, y0] = *p0; - let [x1, y1] = *p1; - [ - Vertex { - position: [x0, y0], - color: *color, - }, - Vertex { - position: [x1, y0], - color: *color, - }, - Vertex { - position: [x0, y1], - color: *color, - }, - Vertex { - position: [x0, y1], - color: *color, - }, - Vertex { - position: [x1, y0], - color: *color, - }, - Vertex { - position: [x1, y1], - color: *color, - }, - ] - } -} - -// const NUM_VERTICES: usize = NUM_RECTS * 6; -struct RectHolder { - rects: Vec, - vertices: Vec, -} -impl RectHolder { - /// Generate a random set of rectangles - fn random() -> RectHolder { - let NUM_RECTS: usize = 10_000; - let mut rects = Vec::with_capacity(NUM_RECTS); - let mut rng = rand::thread_rng(); - let mut random = || rng.gen_range(-1.0..1.0); - for _ in 0..NUM_RECTS { - let r = Rect { - p0: [random(), random()], - p1: [random(), random()], - color: Color([random(), random(), random()]), - }; - rects.push(r); - } - let mut vertices = Vec::with_capacity(6 * NUM_RECTS); - for rect in rects.iter() { - vertices.extend_from_slice(&rect.vertices()); - } - Self { rects, vertices } - } -} - -// const NUM_TRIANGLES: usize = 100_000; -// const NUM_VERTICES: usize = NUM_TRIANGLES * 3; - -// fn generate_vertices() -> Vec { -// let mut rng = rand::thread_rng(); -// let mut random = || rng.gen_range(-1.0..1.0); -// let mut vertices = Vec::new(); -// for _ in 0..NUM_VERTICES { -// let x = random(); -// let y = random(); -// let color = Color([random(), random(), random()]); -// vertices.push(Vertex { -// position: [x, y], -// color, -// }); -// } -// vertices -// } - -// const VERTICES: &[Vertex] = &[ -// Vertex { -// // vertex a -// position: [-0.5, -0.5], -// color: RED, -// }, -// Vertex { -// // vertex b -// position: [0.5, -0.5], -// color: RED, -// }, -// Vertex { -// // vertex d -// position: [-0.5, 0.5], -// color: RED, -// }, -// Vertex { -// // vertex d -// position: [-0.5, 0.5], -// color: Color([1.0, 1.0, 0.0]), -// }, -// Vertex { -// // vertex b -// position: [0.5, -0.5], -// color: GREEN, -// }, -// Vertex { -// // vertex c -// position: [0.5, 0.5], -// color: BLUE, -// }, -// Vertex { -// position: [-0.75, 0.25], -// color: Color([1.0, 1.0, 0.0]), -// }, -// Vertex { -// position: [0.75, -0.25], -// color: GREEN, -// }, -// Vertex { -// position: [0.15, 0.95], -// color: BLUE, -// }, -// ]; - -struct VertexBufferStuff { - vertex_buffer: wgpu::Buffer, - layout_display: LayoutDisplay, -} -impl VertexBufferStuff { - fn new(device: &wgpu::Device) -> Self { - let layout_display = LayoutDisplay::from_proto(); - - // let vertices = generate_vertices(); - // let vertices = RectHolder::random().vertices; - - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&layout_display.vertices), - usage: wgpu::BufferUsages::VERTEX, - }); - Self { - vertex_buffer, - layout_display, - } - } -} - -struct State { - surface: wgpu::Surface, - device: wgpu::Device, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - size: winit::dpi::PhysicalSize, - pipeline: wgpu::RenderPipeline, - vertex_buffer_stuff: VertexBufferStuff, - layout_display: LayoutDisplay, - // vertex_buffer: wgpu::Buffer, - // vertices: Vec, -} - -impl State { - async fn new(window: &Window) -> Self { - let size = window.inner_size(); - let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends, - dx12_shader_compiler: wgpu::Dx12Compiler::default(), - }); - let surface = unsafe { instance.create_surface(window) }.unwrap(); - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .unwrap(); - - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - }, - None, // Trace path - ) - .await - .unwrap(); - let swapchain_capabilities = surface.get_capabilities(&adapter); - let swapchain_format = swapchain_capabilities.formats[0]; - - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: swapchain_format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Fifo, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: Vec::new(), - }; - surface.configure(&device, &config); - - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), - }); - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[], - push_constant_ranges: &[], - }); - - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::desc()], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent::OVER, - alpha: wgpu::BlendComponent::OVER, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - - // let vertices = generate_vertices(); - // let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - // label: Some("Vertex Buffer"), - // contents: bytemuck::cast_slice(&vertices), - // usage: wgpu::BufferUsages::VERTEX, - // }); - let vertex_buffer_stuff = VertexBufferStuff::new(&device); - - let layout_display = LayoutDisplay::from_proto(); - - Self { - surface, - device, - queue, - config, - size, - pipeline, - vertex_buffer_stuff, - layout_display, - // vertex_buffer, - // vertices - } - } - - pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { - if new_size.width > 0 && new_size.height > 0 { - self.size = new_size; - self.config.width = new_size.width; - self.config.height = new_size.height; - self.surface.configure(&self.device, &self.config); - } - } - - #[allow(unused_variables)] - fn input(&mut self, event: &WindowEvent) -> bool { - false - } - - fn update(&mut self) {} - - fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - //let output = self.surface.get_current_frame()?.output; - // self.vertex_buffer_stuff = VertexBufferStuff::new(&self.device); - // self.vertices = generate_vertices(); - // self.vertex_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - // label: Some("Vertex Buffer"), - // contents: bytemuck::cast_slice(&self.vertices), - // usage: wgpu::BufferUsages::VERTEX, - // }); - - // self.vertex_buffer_stuff.swap(); - - let output = self.surface.get_current_texture()?; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.2, - g: 0.247, - b: 0.314, - a: 1.0, - }), - store: true, - }, - })], - depth_stencil_attachment: None, - }); - - render_pass.set_pipeline(&self.pipeline); - render_pass.set_vertex_buffer(0, self.vertex_buffer().slice(..)); - render_pass.draw( - 0..self.vertex_buffer_stuff.layout_display.vertices.len() as u32, - 0..1, - ); - } - - self.queue.submit(iter::once(encoder.finish())); - output.present(); - - Ok(()) - } - fn vertex_buffer(&self) -> &wgpu::Buffer { - &self.vertex_buffer_stuff.vertex_buffer - } -} - -pub fn run() { - env_logger::init(); - - let event_loop = EventLoop::new(); - let window = WindowBuilder::new().build(&event_loop).unwrap(); - window.set_title(&*format!("{}", "Layout21 Viewer")); - let mut state = pollster::block_on(State::new(&window)); - - error!("START!!!"); - - event_loop.run(move |event, _, control_flow| match event { - Event::WindowEvent { - ref event, - window_id, - } if window_id == window.id() => { - if !state.input(event) { - match event { - WindowEvent::CloseRequested - | WindowEvent::KeyboardInput { - input: - KeyboardInput { - state: ElementState::Pressed, - virtual_keycode: Some(VirtualKeyCode::Escape), - .. - }, - .. - } => *control_flow = ControlFlow::Exit, - WindowEvent::Resized(physical_size) => { - state.resize(*physical_size); - window.request_redraw(); - } - WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - state.resize(**new_inner_size); - } - _ => {} - } - } - } - Event::RedrawRequested(_) => { - error!("REDRAW!!!"); - state.update(); - match state.render() { - Ok(_) => {} - Err(wgpu::SurfaceError::Lost) => state.resize(state.size), - Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, - Err(e) => eprintln!("{:?}", e), - } - } - Event::MainEventsCleared => { - error!("REQUESTING!!!"); - window.request_redraw(); - } - _ => {} - }); -} - -/// Grab the full path of resource-file `fname` -fn resource(rname: &str) -> String { - format!( - "{}/../layout21converters/resources/{}", - env!("CARGO_MANIFEST_DIR"), - rname - ) -} +mod run; +pub use crate::run::run; diff --git a/layout21wgpu/src/run.rs b/layout21wgpu/src/run.rs new file mode 100644 index 0000000..728f017 --- /dev/null +++ b/layout21wgpu/src/run.rs @@ -0,0 +1,606 @@ +use bytemuck::{Pod, Zeroable}; +use lyon::{ + geom::Box2D, + math::point, + path::polygon::Polygon, + tessellation::{ + geometry_builder::{BuffersBuilder, VertexBuffers}, + FillOptions, FillTessellator, FillVertex, FillVertexConstructor, + }, +}; +use std::{collections::HashMap, iter, marker::PhantomData, mem}; + +use layout21protos::{self, conv as proto_converters}; +use layout21raw as raw; +use layout21utils::Ptr; +use log::{debug, error, info, log_enabled, Level}; +use rand::Rng; +use wgpu::util::DeviceExt; +use winit::{ + event::*, + event_loop::{ControlFlow, EventLoop}, + window::{Window, WindowBuilder}, +}; + +#[derive(Debug, Clone, Copy)] +struct Tbd; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +struct Color(pub [f32; 3]); + +const COLORS: [Color; 7] = [ + Color([1.0, 0.0, 0.0]), // red + Color([0.0, 1.0, 0.0]), // green + Color([0.0, 0.0, 1.0]), // blue + Color([1.0, 1.0, 0.0]), // + Color([1.0, 0.0, 1.0]), // + Color([0.0, 1.0, 1.0]), // + Color([1.0, 1.0, 1.0]), // white +]; +#[derive(Debug)] +struct ColorWheel { + index: usize, +} +impl ColorWheel { + fn next(&mut self) -> Color { + let color = COLORS[self.index]; + self.index = (self.index + 1) % COLORS.len(); + color + } +} + +#[derive(Debug)] +struct LayoutDisplay { + // Data elements + lib: raw::Library, + cell: Ptr, + // Rendering elements + bbox: [[raw::Int; 2]; 2], + vertices: Vec, + layer_colors: HashMap, + geometry: VertexBuffers, +} +impl LayoutDisplay { + fn from_proto() -> Self { + let proto_lib: layout21protos::Library = + proto_converters::open(&resource("sky130_fd_sc_hd__dfxtp_1.pb")).unwrap(); + let rawlib = raw::Library::from_proto(proto_lib, None).unwrap(); + let cell = rawlib.cells[0].clone(); + Self::build(rawlib, cell) + } + fn build(rawlib: raw::Library, cell: Ptr) -> Self { + let cell1 = cell.read().unwrap(); + let layout = cell1.layout.as_ref().unwrap(); + + let mut layer_colors: HashMap = HashMap::new(); + + let mut rects: Vec = Vec::with_capacity(layout.elems.len()); + let bbox = [[0, 10_000], [0, 4000]]; // FIXME! + + let mut color_wheel = ColorWheel { index: 0 }; + let mut tessellator = FillTessellator::new(); + let mut geometry: VertexBuffers = VertexBuffers::new(); + + let mut k = 0; + for elem in &layout.elems { + let color = layer_colors + .entry(elem.layer) + .or_insert_with(|| color_wheel.next()) + .clone(); + + let shape = &elem.inner; + match shape { + raw::Shape::Rect(r) => { + // let rect = Rect { + // p0: [ + // r.p0.x as f32 / bbox[0][1] as f32, + // r.p0.y as f32 / bbox[1][1] as f32, + // ], + // p1: [ + // r.p1.x as f32 / bbox[0][1] as f32, + // r.p1.y as f32 / bbox[1][1] as f32, + // ], + // color, + // }; + // rects.push(rect); + + let lyon_rect = Box2D::new( + point( + r.p0.x as f32 / bbox[0][1] as f32, + r.p0.y as f32 / bbox[1][1] as f32, + ), + point( + r.p1.x as f32 / bbox[0][1] as f32, + r.p1.y as f32 / bbox[1][1] as f32, + ), + ); + tessellator + .tessellate_rectangle( + &lyon_rect, + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), + ) + .unwrap(); + } + raw::Shape::Path(p) => { + let path_rects = p.rects(); + if path_rects.is_none() { + continue; + } + let path_rects = path_rects.unwrap(); + for r in path_rects.iter() { + // let rect = Rect { + // p0: [ + // r.p0.x as f32 / bbox[0][1] as f32, + // r.p0.y as f32 / bbox[1][1] as f32, + // ], + // p1: [ + // r.p1.x as f32 / bbox[0][1] as f32, + // r.p1.y as f32 / bbox[1][1] as f32, + // ], + // color, + // }; + // rects.push(rect); + + let lyon_rect = Box2D::new( + point( + r.p0.x as f32 / bbox[0][1] as f32, + r.p0.y as f32 / bbox[1][1] as f32, + ), + point( + r.p1.x as f32 / bbox[0][1] as f32, + r.p1.y as f32 / bbox[1][1] as f32, + ), + ); + tessellator + .tessellate_rectangle( + &lyon_rect, + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), + ) + .unwrap(); + } + } + raw::Shape::Polygon(p) => { + let points: Vec<_> = p + .points + .iter() + .map(|p| { + lyon::math::Point::new( + p.x as f32 / bbox[0][1] as f32, + p.y as f32 / bbox[1][1] as f32, + ) + }) + .collect(); + let lyon_polygon: Polygon<_> = Polygon { + points: &points, + closed: true, + }; + tessellator + .tessellate_polygon( + lyon_polygon, + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), + ) + .unwrap(); + } + } + if k > 250 { + break; + } + k += 1; + } + + let mut vertices: Vec = Vec::with_capacity(rects.len() * 6); + for rect in rects.iter() { + vertices.extend_from_slice(&rect.vertices()); + } + Self { + lib: rawlib, + cell: cell.clone(), + bbox, + vertices, + layer_colors, + geometry, + } + } +} + +pub struct WithColor { + color: Color, +} +impl WithColor { + fn new(color: Color) -> Self { + Self { color } + } +} +impl FillVertexConstructor for WithColor { + fn new_vertex(&mut self, vertex: FillVertex) -> Vertex { + Vertex { + position: vertex.position().to_array(), + color: self.color.clone(), + } + } +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +struct Vertex { + position: [f32; 2], + color: Color, +} +impl Vertex { + const ATTRIBUTES: [wgpu::VertexAttribute; 2] = + wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3]; + fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &Self::ATTRIBUTES, + } + } +} + +struct Rect { + p0: [f32; 2], + p1: [f32; 2], + color: Color, +} +impl Rect { + /// Triangulated vertices + fn vertices(&self) -> [Vertex; 6] { + let Rect { p0, p1, color } = self; + let [x0, y0] = *p0; + let [x1, y1] = *p1; + [ + Vertex { + position: [x0, y0], + color: *color, + }, + Vertex { + position: [x1, y0], + color: *color, + }, + Vertex { + position: [x0, y1], + color: *color, + }, + Vertex { + position: [x0, y1], + color: *color, + }, + Vertex { + position: [x1, y0], + color: *color, + }, + Vertex { + position: [x1, y1], + color: *color, + }, + ] + } +} + +// const NUM_VERTICES: usize = NUM_RECTS * 6; +struct RectHolder { + rects: Vec, + vertices: Vec, +} +impl RectHolder { + /// Generate a random set of rectangles + fn random() -> RectHolder { + let NUM_RECTS: usize = 10_000; + let mut rects = Vec::with_capacity(NUM_RECTS); + let mut rng = rand::thread_rng(); + let mut random = || rng.gen_range(-1.0..1.0); + for _ in 0..NUM_RECTS { + let r = Rect { + p0: [random(), random()], + p1: [random(), random()], + color: Color([random(), random(), random()]), + }; + rects.push(r); + } + let mut vertices = Vec::with_capacity(6 * NUM_RECTS); + for rect in rects.iter() { + vertices.extend_from_slice(&rect.vertices()); + } + Self { rects, vertices } + } +} + +struct VertexBufferStuff { + vertex_buffer: wgpu::Buffer, + index_buffer: wgpu::Buffer, + layout_display: LayoutDisplay, +} +impl VertexBufferStuff { + fn new(device: &wgpu::Device) -> Self { + let layout_display = LayoutDisplay::from_proto(); + + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&layout_display.geometry.vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&layout_display.geometry.indices), + usage: wgpu::BufferUsages::INDEX, + }); + + Self { + index_buffer, + vertex_buffer, + layout_display, + } + } +} + +struct State { + surface: wgpu::Surface, + device: wgpu::Device, + queue: wgpu::Queue, + config: wgpu::SurfaceConfiguration, + size: winit::dpi::PhysicalSize, + pipeline: wgpu::RenderPipeline, + vertex_buffer_stuff: VertexBufferStuff, + layout_display: LayoutDisplay, +} + +impl State { + async fn new(window: &Window) -> Self { + let size = window.inner_size(); + let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends, + dx12_shader_compiler: wgpu::Dx12Compiler::default(), + }); + let surface = unsafe { instance.create_surface(window) }.unwrap(); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await + .unwrap(); + + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + }, + None, // Trace path + ) + .await + .unwrap(); + let swapchain_capabilities = surface.get_capabilities(&adapter); + let swapchain_format = swapchain_capabilities.formats[0]; + + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: swapchain_format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Fifo, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: Vec::new(), + }; + surface.configure(&device, &config); + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + // let vertices = generate_vertices(); + // let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + // label: Some("Vertex Buffer"), + // contents: bytemuck::cast_slice(&vertices), + // usage: wgpu::BufferUsages::VERTEX, + // }); + let vertex_buffer_stuff = VertexBufferStuff::new(&device); + + let layout_display = LayoutDisplay::from_proto(); + + Self { + surface, + device, + queue, + config, + size, + pipeline, + vertex_buffer_stuff, + layout_display, + // vertex_buffer, + // vertices + } + } + + pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.size = new_size; + self.config.width = new_size.width; + self.config.height = new_size.height; + self.surface.configure(&self.device, &self.config); + } + } + + #[allow(unused_variables)] + fn input(&mut self, event: &WindowEvent) -> bool { + false + } + + fn update(&mut self) {} + + fn render(&mut self) -> Result<(), wgpu::SurfaceError> { + //let output = self.surface.get_current_frame()?.output; + // self.vertex_buffer_stuff = VertexBufferStuff::new(&self.device); + // self.vertices = generate_vertices(); + // self.vertex_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + // label: Some("Vertex Buffer"), + // contents: bytemuck::cast_slice(&self.vertices), + // usage: wgpu::BufferUsages::VERTEX, + // }); + + // self.vertex_buffer_stuff.swap(); + + let output = self.surface.get_current_texture()?; + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.2, + g: 0.247, + b: 0.314, + a: 1.0, + }), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.pipeline); + render_pass.set_vertex_buffer(0, self.vertex_buffer().slice(..)); + render_pass.set_index_buffer( + self.vertex_buffer_stuff.index_buffer.slice(..), + wgpu::IndexFormat::Uint16, + ); + render_pass.draw_indexed( + 0..self // indices + .vertex_buffer_stuff + .layout_display + .geometry + .indices + .len() as u32, + 0, // base_vertex + 0..1, // instances + ); + } + + self.queue.submit(iter::once(encoder.finish())); + output.present(); + + Ok(()) + } + fn vertex_buffer(&self) -> &wgpu::Buffer { + &self.vertex_buffer_stuff.vertex_buffer + } +} + +pub fn run() { + env_logger::init(); + + let event_loop = EventLoop::new(); + let window = WindowBuilder::new().build(&event_loop).unwrap(); + window.set_title(&*format!("{}", "Layout21 Viewer")); + let mut state = pollster::block_on(State::new(&window)); + + error!("START!!!"); + + event_loop.run(move |event, _, control_flow| match event { + Event::WindowEvent { + ref event, + window_id, + } if window_id == window.id() => { + if !state.input(event) { + match event { + WindowEvent::CloseRequested + | WindowEvent::KeyboardInput { + input: + KeyboardInput { + state: ElementState::Pressed, + virtual_keycode: Some(VirtualKeyCode::Escape), + .. + }, + .. + } => *control_flow = ControlFlow::Exit, + WindowEvent::Resized(physical_size) => { + state.resize(*physical_size); + window.request_redraw(); + } + WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { + state.resize(**new_inner_size); + } + _ => {} + } + } + } + Event::RedrawRequested(_) => { + error!("REDRAW!!!"); + state.update(); + match state.render() { + Ok(_) => {} + Err(wgpu::SurfaceError::Lost) => state.resize(state.size), + Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, + Err(e) => eprintln!("{:?}", e), + } + } + Event::MainEventsCleared => { + // error!("REQUESTING!!!"); + // window.request_redraw(); + } + _ => {} + }); +} + +/// Grab the full path of resource-file `fname` +fn resource(rname: &str) -> String { + format!( + "{}/../layout21converters/resources/{}", + env!("CARGO_MANIFEST_DIR"), + rname + ) +} diff --git a/layout21wgpu/src/shader.wgsl b/layout21wgpu/src/shader.wgsl index 657471b..5da115f 100644 --- a/layout21wgpu/src/shader.wgsl +++ b/layout21wgpu/src/shader.wgsl @@ -11,7 +11,7 @@ struct VertexOutput { @vertex fn vs_main(in: VertexInput) -> VertexOutput { var out: VertexOutput; - out.color = vec4(in.color, 0.1); + out.color = vec4(in.color, 0.3); out.position = vec4(in.pos, 0.0, 1.0); return out; } From e0914f29a8fa1bbc1a24392b595ea99eeadd7446 Mon Sep 17 00:00:00 2001 From: Dan Fritchman Date: Wed, 8 Feb 2023 21:02:46 -0800 Subject: [PATCH 5/6] Module-ize --- layout21wgpu/src/color.rs | 29 ++ layout21wgpu/src/gpu.rs | 194 +++++++++++++ layout21wgpu/src/layout.rs | 166 +++++++++++ layout21wgpu/src/lib.rs | 13 + layout21wgpu/src/run.rs | 562 ++----------------------------------- layout21wgpu/src/vertex.rs | 23 ++ 6 files changed, 444 insertions(+), 543 deletions(-) create mode 100644 layout21wgpu/src/color.rs create mode 100644 layout21wgpu/src/gpu.rs create mode 100644 layout21wgpu/src/layout.rs create mode 100644 layout21wgpu/src/vertex.rs diff --git a/layout21wgpu/src/color.rs b/layout21wgpu/src/color.rs new file mode 100644 index 0000000..c358330 --- /dev/null +++ b/layout21wgpu/src/color.rs @@ -0,0 +1,29 @@ +use bytemuck::{Pod, Zeroable}; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +pub struct Color(pub [f32; 3]); + +pub const COLORS: [Color; 7] = [ + Color([1.0, 0.0, 0.0]), // red + Color([0.0, 1.0, 0.0]), // green + Color([0.0, 0.0, 1.0]), // blue + Color([1.0, 1.0, 0.0]), // + Color([1.0, 0.0, 1.0]), // + Color([0.0, 1.0, 1.0]), // + Color([1.0, 1.0, 1.0]), // white +]; +#[derive(Debug)] +pub(crate) struct ColorWheel { + index: usize, +} +impl ColorWheel { + pub fn new() -> Self { + Self { index: 0 } + } + pub fn next(&mut self) -> Color { + let color = COLORS[self.index]; + self.index = (self.index + 1) % COLORS.len(); + color + } +} diff --git a/layout21wgpu/src/gpu.rs b/layout21wgpu/src/gpu.rs new file mode 100644 index 0000000..e2ba7c4 --- /dev/null +++ b/layout21wgpu/src/gpu.rs @@ -0,0 +1,194 @@ +//! +//! # GPU Stuff +//! +//! All the WGPU machinery lives here. +//! Many, many terms of art fly around GPU world; get used to it. +//! + +use wgpu::util::DeviceExt; +use winit::window::Window; + +// Local Imports +use crate::{LayoutDisplay, Vertex}; + +/// # GPU Stuff +/// +/// *Microooo-processors*. Idunno what they are, you dunno what they are, CASH. +/// +pub struct GpuStuff { + pub surface: wgpu::Surface, + pub device: wgpu::Device, + pub queue: wgpu::Queue, + pub config: wgpu::SurfaceConfiguration, + pub size: winit::dpi::PhysicalSize, + pub pipeline: wgpu::RenderPipeline, + pub vertex_buffer: wgpu::Buffer, + pub index_buffer: wgpu::Buffer, +} +impl GpuStuff { + /// Create new [`GpuStuff`]. + /// + /// Much of the machinery and terminology lives here in the configuration phase. + /// Once this gets done, things really do calm down to just the vertex and index buffers, + /// and writing triangles to them. + /// + pub async fn new(window: &Window, layout: &LayoutDisplay) -> Self { + let size = window.inner_size(); + let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends, + dx12_shader_compiler: wgpu::Dx12Compiler::default(), + }); + let surface = unsafe { instance.create_surface(window) }.unwrap(); + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + compatible_surface: Some(&surface), + force_fallback_adapter: false, + }) + .await + .unwrap(); + + let (device, queue) = adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + }, + None, // Trace path + ) + .await + .unwrap(); + let swapchain_capabilities = surface.get_capabilities(&adapter); + let swapchain_format = swapchain_capabilities.formats[0]; + + let config = wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format: swapchain_format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Fifo, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: Vec::new(), + }; + surface.configure(&device, &config); + + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("Shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }); + + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("Render Pipeline Layout"), + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + + let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Render Pipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "vs_main", + buffers: &[Vertex::desc()], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + + let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Vertex Buffer"), + contents: bytemuck::cast_slice(&layout.geometry.vertices), + usage: wgpu::BufferUsages::VERTEX, + }); + let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Index Buffer"), + contents: bytemuck::cast_slice(&layout.geometry.indices), + usage: wgpu::BufferUsages::INDEX, + }); + + Self { + surface, + device, + queue, + config, + size, + pipeline, + vertex_buffer, + index_buffer, + } + } + + pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { + if new_size.width > 0 && new_size.height > 0 { + self.size = new_size; + self.config.width = new_size.width; + self.config.height = new_size.height; + self.surface.configure(&self.device, &self.config); + } + } + + /// Render the current frame + pub fn render(&self, max_index: u32) -> Result<(), wgpu::SurfaceError> { + let output = self.surface.get_current_texture()?; + let view = output + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("Render Encoder"), + }); + + { + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Render Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.2, + g: 0.247, + b: 0.314, + a: 1.0, + }), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + render_pass.set_pipeline(&self.pipeline); + render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); + render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); + render_pass.draw_indexed( + 0..max_index, // indices + 0, // base_vertex + 0..1, // instances + ); + } + + self.queue.submit(std::iter::once(encoder.finish())); + output.present(); + + Ok(()) + } +} diff --git a/layout21wgpu/src/layout.rs b/layout21wgpu/src/layout.rs new file mode 100644 index 0000000..6f7fc2f --- /dev/null +++ b/layout21wgpu/src/layout.rs @@ -0,0 +1,166 @@ +use lyon::{ + geom::Box2D, + math::point, + path::polygon::Polygon, + tessellation::{ + geometry_builder::{BuffersBuilder, VertexBuffers}, + FillOptions, FillTessellator, FillVertex, FillVertexConstructor, + }, +}; +use std::collections::HashMap; + +use layout21protos::{self, conv as proto_converters}; +use layout21raw as raw; +use layout21utils::Ptr; +use log::error; +// Local imports +use crate::{Color, ColorWheel, Vertex}; + +#[derive(Debug)] +pub struct LayoutDisplay { + // Data elements + pub lib: raw::Library, + pub cell: Ptr, + + // Rendering elements + pub bbox: raw::BoundBox, + pub layer_colors: HashMap, + pub geometry: VertexBuffers, +} +impl LayoutDisplay { + pub fn from_proto() -> Self { + let proto_lib: layout21protos::Library = + proto_converters::open(&resource("sky130_fd_sc_hd__dfxtp_1.pb")).unwrap(); + let rawlib = raw::Library::from_proto(proto_lib, None).unwrap(); + let cell = rawlib.cells[0].clone(); + Self::build(rawlib, cell) + } + pub fn build(rawlib: raw::Library, cell: Ptr) -> Self { + let cell1 = cell.read().unwrap(); + let layout = cell1.layout.as_ref().unwrap(); + + let mut layer_colors: HashMap = HashMap::new(); + + // let mut rects: Vec = Vec::with_capacity(layout.elems.len()); + let bbox: raw::BoundBox = layout.bbox(); + error!("bbox: {:?}", bbox); + + let mut color_wheel = ColorWheel::new(); + let mut tessellator = FillTessellator::new(); + let mut geometry: VertexBuffers = VertexBuffers::new(); + + for elem in &layout.elems { + let color = layer_colors + .entry(elem.layer) + .or_insert_with(|| color_wheel.next()) + .clone(); + + let shape = &elem.inner; + match shape { + raw::Shape::Rect(r) => { + let lyon_rect = Box2D::new( + point( + r.p0.x as f32 / bbox.p1.x as f32, + r.p0.y as f32 / bbox.p1.y as f32, + ), + point( + r.p1.x as f32 / bbox.p1.x as f32, + r.p1.y as f32 / bbox.p1.y as f32, + ), + ); + tessellator + .tessellate_rectangle( + &lyon_rect, + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), + ) + .unwrap(); + } + raw::Shape::Path(p) => { + let path_rects = p.rects(); + if path_rects.is_none() { + continue; + } + let path_rects = path_rects.unwrap(); + for r in path_rects.iter() { + let lyon_rect = Box2D::new( + point( + r.p0.x as f32 / bbox.p1.x as f32, + r.p0.y as f32 / bbox.p1.y as f32, + ), + point( + r.p1.x as f32 / bbox.p1.x as f32, + r.p1.y as f32 / bbox.p1.y as f32, + ), + ); + tessellator + .tessellate_rectangle( + &lyon_rect, + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), + ) + .unwrap(); + } + } + raw::Shape::Polygon(p) => { + let points: Vec<_> = p + .points + .iter() + .map(|p| { + lyon::math::Point::new( + p.x as f32 / bbox.p1.x as f32, + p.y as f32 / bbox.p1.y as f32, + ) + }) + .collect(); + let lyon_polygon: Polygon<_> = Polygon { + points: &points, + closed: true, + }; + tessellator + .tessellate_polygon( + lyon_polygon, + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), + ) + .unwrap(); + } + } + } + + Self { + lib: rawlib, + cell: cell.clone(), + bbox, + + layer_colors, + geometry, + } + } +} + +pub struct WithColor { + pub color: Color, +} +impl WithColor { + fn new(color: Color) -> Self { + Self { color } + } +} +impl FillVertexConstructor for WithColor { + fn new_vertex(&mut self, vertex: FillVertex) -> Vertex { + Vertex { + position: vertex.position().to_array(), + color: self.color.clone(), + } + } +} + +/// Grab the full path of resource-file `fname` +fn resource(rname: &str) -> String { + format!( + "{}/../layout21converters/resources/{}", + env!("CARGO_MANIFEST_DIR"), + rname + ) +} diff --git a/layout21wgpu/src/lib.rs b/layout21wgpu/src/lib.rs index 39dde27..4522549 100644 --- a/layout21wgpu/src/lib.rs +++ b/layout21wgpu/src/lib.rs @@ -1,2 +1,15 @@ +mod color; +use crate::color::{Color, ColorWheel}; + +mod gpu; +use crate::gpu::GpuStuff; + +mod vertex; +use crate::vertex::Vertex; + +mod layout; +use crate::layout::LayoutDisplay; + +// Primary public export: the run function mod run; pub use crate::run::run; diff --git a/layout21wgpu/src/run.rs b/layout21wgpu/src/run.rs index 728f017..8fe90c3 100644 --- a/layout21wgpu/src/run.rs +++ b/layout21wgpu/src/run.rs @@ -1,543 +1,28 @@ -use bytemuck::{Pod, Zeroable}; -use lyon::{ - geom::Box2D, - math::point, - path::polygon::Polygon, - tessellation::{ - geometry_builder::{BuffersBuilder, VertexBuffers}, - FillOptions, FillTessellator, FillVertex, FillVertexConstructor, - }, -}; -use std::{collections::HashMap, iter, marker::PhantomData, mem}; - -use layout21protos::{self, conv as proto_converters}; -use layout21raw as raw; -use layout21utils::Ptr; -use log::{debug, error, info, log_enabled, Level}; -use rand::Rng; -use wgpu::util::DeviceExt; +use log::error; use winit::{ event::*, event_loop::{ControlFlow, EventLoop}, window::{Window, WindowBuilder}, }; -#[derive(Debug, Clone, Copy)] -struct Tbd; - -#[repr(C)] -#[derive(Copy, Clone, Debug, Pod, Zeroable)] -struct Color(pub [f32; 3]); - -const COLORS: [Color; 7] = [ - Color([1.0, 0.0, 0.0]), // red - Color([0.0, 1.0, 0.0]), // green - Color([0.0, 0.0, 1.0]), // blue - Color([1.0, 1.0, 0.0]), // - Color([1.0, 0.0, 1.0]), // - Color([0.0, 1.0, 1.0]), // - Color([1.0, 1.0, 1.0]), // white -]; -#[derive(Debug)] -struct ColorWheel { - index: usize, -} -impl ColorWheel { - fn next(&mut self) -> Color { - let color = COLORS[self.index]; - self.index = (self.index + 1) % COLORS.len(); - color - } -} - -#[derive(Debug)] -struct LayoutDisplay { - // Data elements - lib: raw::Library, - cell: Ptr, - // Rendering elements - bbox: [[raw::Int; 2]; 2], - vertices: Vec, - layer_colors: HashMap, - geometry: VertexBuffers, -} -impl LayoutDisplay { - fn from_proto() -> Self { - let proto_lib: layout21protos::Library = - proto_converters::open(&resource("sky130_fd_sc_hd__dfxtp_1.pb")).unwrap(); - let rawlib = raw::Library::from_proto(proto_lib, None).unwrap(); - let cell = rawlib.cells[0].clone(); - Self::build(rawlib, cell) - } - fn build(rawlib: raw::Library, cell: Ptr) -> Self { - let cell1 = cell.read().unwrap(); - let layout = cell1.layout.as_ref().unwrap(); - - let mut layer_colors: HashMap = HashMap::new(); - - let mut rects: Vec = Vec::with_capacity(layout.elems.len()); - let bbox = [[0, 10_000], [0, 4000]]; // FIXME! - - let mut color_wheel = ColorWheel { index: 0 }; - let mut tessellator = FillTessellator::new(); - let mut geometry: VertexBuffers = VertexBuffers::new(); - - let mut k = 0; - for elem in &layout.elems { - let color = layer_colors - .entry(elem.layer) - .or_insert_with(|| color_wheel.next()) - .clone(); - - let shape = &elem.inner; - match shape { - raw::Shape::Rect(r) => { - // let rect = Rect { - // p0: [ - // r.p0.x as f32 / bbox[0][1] as f32, - // r.p0.y as f32 / bbox[1][1] as f32, - // ], - // p1: [ - // r.p1.x as f32 / bbox[0][1] as f32, - // r.p1.y as f32 / bbox[1][1] as f32, - // ], - // color, - // }; - // rects.push(rect); - - let lyon_rect = Box2D::new( - point( - r.p0.x as f32 / bbox[0][1] as f32, - r.p0.y as f32 / bbox[1][1] as f32, - ), - point( - r.p1.x as f32 / bbox[0][1] as f32, - r.p1.y as f32 / bbox[1][1] as f32, - ), - ); - tessellator - .tessellate_rectangle( - &lyon_rect, - &FillOptions::DEFAULT, - &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), - ) - .unwrap(); - } - raw::Shape::Path(p) => { - let path_rects = p.rects(); - if path_rects.is_none() { - continue; - } - let path_rects = path_rects.unwrap(); - for r in path_rects.iter() { - // let rect = Rect { - // p0: [ - // r.p0.x as f32 / bbox[0][1] as f32, - // r.p0.y as f32 / bbox[1][1] as f32, - // ], - // p1: [ - // r.p1.x as f32 / bbox[0][1] as f32, - // r.p1.y as f32 / bbox[1][1] as f32, - // ], - // color, - // }; - // rects.push(rect); - - let lyon_rect = Box2D::new( - point( - r.p0.x as f32 / bbox[0][1] as f32, - r.p0.y as f32 / bbox[1][1] as f32, - ), - point( - r.p1.x as f32 / bbox[0][1] as f32, - r.p1.y as f32 / bbox[1][1] as f32, - ), - ); - tessellator - .tessellate_rectangle( - &lyon_rect, - &FillOptions::DEFAULT, - &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), - ) - .unwrap(); - } - } - raw::Shape::Polygon(p) => { - let points: Vec<_> = p - .points - .iter() - .map(|p| { - lyon::math::Point::new( - p.x as f32 / bbox[0][1] as f32, - p.y as f32 / bbox[1][1] as f32, - ) - }) - .collect(); - let lyon_polygon: Polygon<_> = Polygon { - points: &points, - closed: true, - }; - tessellator - .tessellate_polygon( - lyon_polygon, - &FillOptions::DEFAULT, - &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), - ) - .unwrap(); - } - } - if k > 250 { - break; - } - k += 1; - } - - let mut vertices: Vec = Vec::with_capacity(rects.len() * 6); - for rect in rects.iter() { - vertices.extend_from_slice(&rect.vertices()); - } - Self { - lib: rawlib, - cell: cell.clone(), - bbox, - vertices, - layer_colors, - geometry, - } - } -} - -pub struct WithColor { - color: Color, -} -impl WithColor { - fn new(color: Color) -> Self { - Self { color } - } -} -impl FillVertexConstructor for WithColor { - fn new_vertex(&mut self, vertex: FillVertex) -> Vertex { - Vertex { - position: vertex.position().to_array(), - color: self.color.clone(), - } - } -} - -#[repr(C)] -#[derive(Copy, Clone, Debug, Pod, Zeroable)] -struct Vertex { - position: [f32; 2], - color: Color, -} -impl Vertex { - const ATTRIBUTES: [wgpu::VertexAttribute; 2] = - wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3]; - fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &Self::ATTRIBUTES, - } - } -} - -struct Rect { - p0: [f32; 2], - p1: [f32; 2], - color: Color, -} -impl Rect { - /// Triangulated vertices - fn vertices(&self) -> [Vertex; 6] { - let Rect { p0, p1, color } = self; - let [x0, y0] = *p0; - let [x1, y1] = *p1; - [ - Vertex { - position: [x0, y0], - color: *color, - }, - Vertex { - position: [x1, y0], - color: *color, - }, - Vertex { - position: [x0, y1], - color: *color, - }, - Vertex { - position: [x0, y1], - color: *color, - }, - Vertex { - position: [x1, y0], - color: *color, - }, - Vertex { - position: [x1, y1], - color: *color, - }, - ] - } -} - -// const NUM_VERTICES: usize = NUM_RECTS * 6; -struct RectHolder { - rects: Vec, - vertices: Vec, -} -impl RectHolder { - /// Generate a random set of rectangles - fn random() -> RectHolder { - let NUM_RECTS: usize = 10_000; - let mut rects = Vec::with_capacity(NUM_RECTS); - let mut rng = rand::thread_rng(); - let mut random = || rng.gen_range(-1.0..1.0); - for _ in 0..NUM_RECTS { - let r = Rect { - p0: [random(), random()], - p1: [random(), random()], - color: Color([random(), random(), random()]), - }; - rects.push(r); - } - let mut vertices = Vec::with_capacity(6 * NUM_RECTS); - for rect in rects.iter() { - vertices.extend_from_slice(&rect.vertices()); - } - Self { rects, vertices } - } -} - -struct VertexBufferStuff { - vertex_buffer: wgpu::Buffer, - index_buffer: wgpu::Buffer, - layout_display: LayoutDisplay, -} -impl VertexBufferStuff { - fn new(device: &wgpu::Device) -> Self { - let layout_display = LayoutDisplay::from_proto(); - - let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&layout_display.geometry.vertices), - usage: wgpu::BufferUsages::VERTEX, - }); - let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - label: Some("Index Buffer"), - contents: bytemuck::cast_slice(&layout_display.geometry.indices), - usage: wgpu::BufferUsages::INDEX, - }); +// Local imports +use crate::{GpuStuff, LayoutDisplay}; - Self { - index_buffer, - vertex_buffer, - layout_display, - } - } -} - -struct State { - surface: wgpu::Surface, - device: wgpu::Device, - queue: wgpu::Queue, - config: wgpu::SurfaceConfiguration, - size: winit::dpi::PhysicalSize, - pipeline: wgpu::RenderPipeline, - vertex_buffer_stuff: VertexBufferStuff, - layout_display: LayoutDisplay, +pub struct State { + gpu: GpuStuff, + layout: LayoutDisplay, } - impl State { async fn new(window: &Window) -> Self { - let size = window.inner_size(); - let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); - let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { - backends, - dx12_shader_compiler: wgpu::Dx12Compiler::default(), - }); - let surface = unsafe { instance.create_surface(window) }.unwrap(); - let adapter = instance - .request_adapter(&wgpu::RequestAdapterOptions { - power_preference: wgpu::PowerPreference::default(), - compatible_surface: Some(&surface), - force_fallback_adapter: false, - }) - .await - .unwrap(); - - let (device, queue) = adapter - .request_device( - &wgpu::DeviceDescriptor { - label: None, - features: wgpu::Features::empty(), - limits: wgpu::Limits::default(), - }, - None, // Trace path - ) - .await - .unwrap(); - let swapchain_capabilities = surface.get_capabilities(&adapter); - let swapchain_format = swapchain_capabilities.formats[0]; - - let config = wgpu::SurfaceConfiguration { - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - format: swapchain_format, - width: size.width, - height: size.height, - present_mode: wgpu::PresentMode::Fifo, - alpha_mode: wgpu::CompositeAlphaMode::Auto, - view_formats: Vec::new(), - }; - surface.configure(&device, &config); - - let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("Shader"), - source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), - }); - - let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render Pipeline Layout"), - bind_group_layouts: &[], - push_constant_ranges: &[], - }); - - let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Render Pipeline"), - layout: Some(&pipeline_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[Vertex::desc()], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: config.format, - blend: Some(wgpu::BlendState::ALPHA_BLENDING), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - - // let vertices = generate_vertices(); - // let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - // label: Some("Vertex Buffer"), - // contents: bytemuck::cast_slice(&vertices), - // usage: wgpu::BufferUsages::VERTEX, - // }); - let vertex_buffer_stuff = VertexBufferStuff::new(&device); - - let layout_display = LayoutDisplay::from_proto(); - - Self { - surface, - device, - queue, - config, - size, - pipeline, - vertex_buffer_stuff, - layout_display, - // vertex_buffer, - // vertices - } - } - - pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize) { - if new_size.width > 0 && new_size.height > 0 { - self.size = new_size; - self.config.width = new_size.width; - self.config.height = new_size.height; - self.surface.configure(&self.device, &self.config); - } + let layout = LayoutDisplay::from_proto(); + let gpu = GpuStuff::new(window, &layout).await; + Self { gpu, layout } } +} - #[allow(unused_variables)] - fn input(&mut self, event: &WindowEvent) -> bool { - false - } - - fn update(&mut self) {} - - fn render(&mut self) -> Result<(), wgpu::SurfaceError> { - //let output = self.surface.get_current_frame()?.output; - // self.vertex_buffer_stuff = VertexBufferStuff::new(&self.device); - // self.vertices = generate_vertices(); - // self.vertex_buffer = self.device.create_buffer_init(&wgpu::util::BufferInitDescriptor { - // label: Some("Vertex Buffer"), - // contents: bytemuck::cast_slice(&self.vertices), - // usage: wgpu::BufferUsages::VERTEX, - // }); - - // self.vertex_buffer_stuff.swap(); - - let output = self.surface.get_current_texture()?; - let view = output - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = self - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { - label: Some("Render Encoder"), - }); - - { - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("Render Pass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.2, - g: 0.247, - b: 0.314, - a: 1.0, - }), - store: true, - }, - })], - depth_stencil_attachment: None, - }); - - render_pass.set_pipeline(&self.pipeline); - render_pass.set_vertex_buffer(0, self.vertex_buffer().slice(..)); - render_pass.set_index_buffer( - self.vertex_buffer_stuff.index_buffer.slice(..), - wgpu::IndexFormat::Uint16, - ); - render_pass.draw_indexed( - 0..self // indices - .vertex_buffer_stuff - .layout_display - .geometry - .indices - .len() as u32, - 0, // base_vertex - 0..1, // instances - ); - } - - self.queue.submit(iter::once(encoder.finish())); - output.present(); - - Ok(()) - } - fn vertex_buffer(&self) -> &wgpu::Buffer { - &self.vertex_buffer_stuff.vertex_buffer - } +#[allow(unused_variables)] +fn handle_input(event: &WindowEvent) -> bool { + false } pub fn run() { @@ -555,7 +40,7 @@ pub fn run() { ref event, window_id, } if window_id == window.id() => { - if !state.input(event) { + if !handle_input(event) { match event { WindowEvent::CloseRequested | WindowEvent::KeyboardInput { @@ -568,11 +53,11 @@ pub fn run() { .. } => *control_flow = ControlFlow::Exit, WindowEvent::Resized(physical_size) => { - state.resize(*physical_size); + state.gpu.resize(*physical_size); window.request_redraw(); } WindowEvent::ScaleFactorChanged { new_inner_size, .. } => { - state.resize(**new_inner_size); + state.gpu.resize(**new_inner_size); } _ => {} } @@ -580,10 +65,10 @@ pub fn run() { } Event::RedrawRequested(_) => { error!("REDRAW!!!"); - state.update(); - match state.render() { + let max_index = state.layout.geometry.indices.len(); + match state.gpu.render(max_index as u32) { Ok(_) => {} - Err(wgpu::SurfaceError::Lost) => state.resize(state.size), + Err(wgpu::SurfaceError::Lost) => state.gpu.resize(state.gpu.size), Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit, Err(e) => eprintln!("{:?}", e), } @@ -595,12 +80,3 @@ pub fn run() { _ => {} }); } - -/// Grab the full path of resource-file `fname` -fn resource(rname: &str) -> String { - format!( - "{}/../layout21converters/resources/{}", - env!("CARGO_MANIFEST_DIR"), - rname - ) -} diff --git a/layout21wgpu/src/vertex.rs b/layout21wgpu/src/vertex.rs new file mode 100644 index 0000000..59cde91 --- /dev/null +++ b/layout21wgpu/src/vertex.rs @@ -0,0 +1,23 @@ +use bytemuck::{Pod, Zeroable}; + +// Local imports +use crate::Color; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +pub struct Vertex { + pub position: [f32; 2], + pub color: Color, +} +impl Vertex { + pub const ATTRIBUTES: [wgpu::VertexAttribute; 2] = + wgpu::vertex_attr_array![0 => Float32x2, 1 => Float32x3]; + + pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &Self::ATTRIBUTES, + } + } +} From c95405b161804dd29604ca734145eefe6604eff8 Mon Sep 17 00:00:00 2001 From: Dan Fritchman Date: Thu, 9 Feb 2023 10:49:53 -0800 Subject: [PATCH 6/6] Scaling, separate tessellation --- layout21wgpu/src/buffers.rs | 5 + layout21wgpu/src/gpu.rs | 16 +-- layout21wgpu/src/layout.rs | 235 ++++++++++++++++++++++-------------- layout21wgpu/src/lib.rs | 10 +- layout21wgpu/src/run.rs | 21 +++- 5 files changed, 183 insertions(+), 104 deletions(-) create mode 100644 layout21wgpu/src/buffers.rs diff --git a/layout21wgpu/src/buffers.rs b/layout21wgpu/src/buffers.rs new file mode 100644 index 0000000..aec0fb7 --- /dev/null +++ b/layout21wgpu/src/buffers.rs @@ -0,0 +1,5 @@ + +use lyon::tessellation::geometry_builder::VertexBuffers; +use crate::Vertex; + +pub type Buffers = VertexBuffers; diff --git a/layout21wgpu/src/gpu.rs b/layout21wgpu/src/gpu.rs index e2ba7c4..ce5ee78 100644 --- a/layout21wgpu/src/gpu.rs +++ b/layout21wgpu/src/gpu.rs @@ -9,7 +9,7 @@ use wgpu::util::DeviceExt; use winit::window::Window; // Local Imports -use crate::{LayoutDisplay, Vertex}; +use crate::{Buffers, Vertex}; /// # GPU Stuff /// @@ -32,7 +32,7 @@ impl GpuStuff { /// Once this gets done, things really do calm down to just the vertex and index buffers, /// and writing triangles to them. /// - pub async fn new(window: &Window, layout: &LayoutDisplay) -> Self { + pub async fn new(window: &Window, buffers: &Buffers) -> Self { let size = window.inner_size(); let backends = wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all); let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { @@ -114,12 +114,12 @@ impl GpuStuff { let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Vertex Buffer"), - contents: bytemuck::cast_slice(&layout.geometry.vertices), + contents: bytemuck::cast_slice(&buffers.vertices), usage: wgpu::BufferUsages::VERTEX, }); let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("Index Buffer"), - contents: bytemuck::cast_slice(&layout.geometry.indices), + contents: bytemuck::cast_slice(&buffers.indices), usage: wgpu::BufferUsages::INDEX, }); @@ -145,7 +145,7 @@ impl GpuStuff { } /// Render the current frame - pub fn render(&self, max_index: u32) -> Result<(), wgpu::SurfaceError> { + pub fn render(&self, buffers: &Buffers) -> Result<(), wgpu::SurfaceError> { let output = self.surface.get_current_texture()?; let view = output .texture @@ -180,9 +180,9 @@ impl GpuStuff { render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); render_pass.draw_indexed( - 0..max_index, // indices - 0, // base_vertex - 0..1, // instances + 0..buffers.indices.len() as u32, // indices + 0, // base_vertex + 0..1, // instances ); } diff --git a/layout21wgpu/src/layout.rs b/layout21wgpu/src/layout.rs index 6f7fc2f..faafca3 100644 --- a/layout21wgpu/src/layout.rs +++ b/layout21wgpu/src/layout.rs @@ -1,31 +1,167 @@ +use std::collections::HashMap; + use lyon::{ geom::Box2D, - math::point, + math::{point, Transform}, path::polygon::Polygon, tessellation::{ geometry_builder::{BuffersBuilder, VertexBuffers}, FillOptions, FillTessellator, FillVertex, FillVertexConstructor, }, }; -use std::collections::HashMap; use layout21protos::{self, conv as proto_converters}; use layout21raw as raw; use layout21utils::Ptr; -use log::error; + // Local imports use crate::{Color, ColorWheel, Vertex}; +/// +/// # Tessellate `layout_display` to triangles ready to render on the GPU. +/// +pub fn tessellate(layout_display: &LayoutDisplay, size: &Size) -> VertexBuffers { + let cell1 = layout_display.cell.read().unwrap(); + let layout = cell1.layout.as_ref().unwrap(); + let bbox = &layout_display.bbox; + let layer_colors = &layout_display.layer_colors; + + let screen_transform = fit(&bbox, size); + let transform = &screen_transform.transform; + + let mut tessellator = FillTessellator::new(); + let mut buffers: VertexBuffers = VertexBuffers::new(); + + // Closures to convert between lyon and layout21 shapes, applying the transform + let get_lyon_point = |p: &raw::Point| transform.transform_point(point(p.x as f32, p.y as f32)); + let get_lyon_rect = |r: &raw::Rect| Box2D::new(get_lyon_point(&r.p0), get_lyon_point(&r.p1)); + + for elem in &layout.elems { + let color = layer_colors.get(&elem.layer).unwrap().clone(); + + let shape = &elem.inner; + match shape { + raw::Shape::Rect(r) => { + tessellator + .tessellate_rectangle( + &get_lyon_rect(r), + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut buffers, WithColor::new(color)), + ) + .unwrap(); + } + raw::Shape::Path(p) => { + // Operate on manhattan paths only, for now + // If the path is non-manhattan, we'll just skip it + let path_rects = p.rects(); + if path_rects.is_none() { + continue; + } + let path_rects = path_rects.unwrap(); + + // Tessellate each rectangle in the path + for r in path_rects.iter() { + let lyon_rect = get_lyon_rect(r); + tessellator + .tessellate_rectangle( + &lyon_rect, + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut buffers, WithColor::new(color)), + ) + .unwrap(); + } + } + raw::Shape::Polygon(p) => { + let points: Vec<_> = p.points.iter().map(|p| get_lyon_point(p)).collect(); + let lyon_polygon: Polygon<_> = Polygon { + points: &points, + closed: true, + }; + tessellator + .tessellate_polygon( + lyon_polygon, + &FillOptions::DEFAULT, + &mut BuffersBuilder::new(&mut buffers, WithColor::new(color)), + ) + .unwrap(); + } + } + } + + buffers +} + +/// Screen/ window size +#[derive(Debug)] +pub struct Size { + pub width: T, + pub height: T, +} +/// Get the transform to fit the bounding box in the screen +fn fit(bbox: &raw::BoundBox, size: &Size) -> ScreenTransformState { + let xspan = (bbox.p1.x - bbox.p0.x) as f64; + let yspan = (bbox.p1.y - bbox.p0.y) as f64; + + // Sort out which dimension to scale to + let zoom = if yspan * size.width as f64 > xspan * size.height as f64 { + 2.0 / yspan // scale to height + } else { + 2.0 / xspan // scale to width + }; + let zoom = (0.9 * zoom) as f32; // leave a bit of padding + + // Get the center of the bounding box + let xmid = (bbox.p1.x + bbox.p0.x) as f32 / 2.0; + let ymid = (bbox.p1.y + bbox.p0.y) as f32 / 2.0; + + // Provide a panning coordinate which scales/ zooms it into GPU coordinates + let pan = (-zoom * xmid, -zoom * ymid); + + ScreenTransformState::new(zoom, pan) +} + +/// The state of zooming and panning the screen +#[derive(Debug)] +pub struct ScreenTransformState { + pub zoom: f32, + pub pan: (f32, f32), + pub transform: Transform, +} +impl ScreenTransformState { + pub fn new(zoom: f32, pan: (f32, f32)) -> Self { + Self { + zoom, + pan, + transform: Transform::identity() + .pre_translate((pan.0, pan.1).into()) + .pre_scale(zoom, zoom), + } + } + pub fn identity() -> Self { + Self { + zoom: 1.0, + pan: (0.0, 0.0), + transform: Transform::identity(), + } + } + pub fn update(&mut self, zoom: f32, pan: (f32, f32)) { + self.zoom = zoom; + self.pan = pan; + self.transform = Transform::identity() + .pre_translate((pan.0, pan.1).into()) + .pre_scale(zoom, zoom); + } +} + #[derive(Debug)] pub struct LayoutDisplay { - // Data elements + // Source layout data pub lib: raw::Library, pub cell: Ptr, - // Rendering elements + // Derived at load time pub bbox: raw::BoundBox, pub layer_colors: HashMap, - pub geometry: VertexBuffers, } impl LayoutDisplay { pub fn from_proto() -> Self { @@ -39,102 +175,21 @@ impl LayoutDisplay { let cell1 = cell.read().unwrap(); let layout = cell1.layout.as_ref().unwrap(); - let mut layer_colors: HashMap = HashMap::new(); - - // let mut rects: Vec = Vec::with_capacity(layout.elems.len()); let bbox: raw::BoundBox = layout.bbox(); - error!("bbox: {:?}", bbox); + let mut layer_colors: HashMap = HashMap::new(); let mut color_wheel = ColorWheel::new(); - let mut tessellator = FillTessellator::new(); - let mut geometry: VertexBuffers = VertexBuffers::new(); - for elem in &layout.elems { - let color = layer_colors + layer_colors .entry(elem.layer) - .or_insert_with(|| color_wheel.next()) - .clone(); - - let shape = &elem.inner; - match shape { - raw::Shape::Rect(r) => { - let lyon_rect = Box2D::new( - point( - r.p0.x as f32 / bbox.p1.x as f32, - r.p0.y as f32 / bbox.p1.y as f32, - ), - point( - r.p1.x as f32 / bbox.p1.x as f32, - r.p1.y as f32 / bbox.p1.y as f32, - ), - ); - tessellator - .tessellate_rectangle( - &lyon_rect, - &FillOptions::DEFAULT, - &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), - ) - .unwrap(); - } - raw::Shape::Path(p) => { - let path_rects = p.rects(); - if path_rects.is_none() { - continue; - } - let path_rects = path_rects.unwrap(); - for r in path_rects.iter() { - let lyon_rect = Box2D::new( - point( - r.p0.x as f32 / bbox.p1.x as f32, - r.p0.y as f32 / bbox.p1.y as f32, - ), - point( - r.p1.x as f32 / bbox.p1.x as f32, - r.p1.y as f32 / bbox.p1.y as f32, - ), - ); - tessellator - .tessellate_rectangle( - &lyon_rect, - &FillOptions::DEFAULT, - &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), - ) - .unwrap(); - } - } - raw::Shape::Polygon(p) => { - let points: Vec<_> = p - .points - .iter() - .map(|p| { - lyon::math::Point::new( - p.x as f32 / bbox.p1.x as f32, - p.y as f32 / bbox.p1.y as f32, - ) - }) - .collect(); - let lyon_polygon: Polygon<_> = Polygon { - points: &points, - closed: true, - }; - tessellator - .tessellate_polygon( - lyon_polygon, - &FillOptions::DEFAULT, - &mut BuffersBuilder::new(&mut geometry, WithColor::new(color)), - ) - .unwrap(); - } - } + .or_insert_with(|| color_wheel.next()); } Self { lib: rawlib, cell: cell.clone(), bbox, - layer_colors, - geometry, } } } diff --git a/layout21wgpu/src/lib.rs b/layout21wgpu/src/lib.rs index 4522549..49abf67 100644 --- a/layout21wgpu/src/lib.rs +++ b/layout21wgpu/src/lib.rs @@ -1,3 +1,8 @@ +//! +//! # Layout21 WGPU +//! + +// Internal modules mod color; use crate::color::{Color, ColorWheel}; @@ -7,8 +12,11 @@ use crate::gpu::GpuStuff; mod vertex; use crate::vertex::Vertex; +mod buffers; +use crate::buffers::Buffers; + mod layout; -use crate::layout::LayoutDisplay; +use crate::layout::{tessellate, LayoutDisplay, Size}; // Primary public export: the run function mod run; diff --git a/layout21wgpu/src/run.rs b/layout21wgpu/src/run.rs index 8fe90c3..fea2ca2 100644 --- a/layout21wgpu/src/run.rs +++ b/layout21wgpu/src/run.rs @@ -6,17 +6,29 @@ use winit::{ }; // Local imports -use crate::{GpuStuff, LayoutDisplay}; +use crate::{tessellate, GpuStuff, LayoutDisplay, Size, Buffers}; +/// # Application State pub struct State { gpu: GpuStuff, layout: LayoutDisplay, + buffers: Buffers, } impl State { async fn new(window: &Window) -> Self { let layout = LayoutDisplay::from_proto(); - let gpu = GpuStuff::new(window, &layout).await; - Self { gpu, layout } + let size = window.inner_size(); + let size: Size = Size { + width: size.width, + height: size.height, + }; + let buffers = tessellate(&layout, &size); + let gpu = GpuStuff::new(window, &buffers).await; + Self { + gpu, + layout, + buffers, + } } } @@ -65,8 +77,7 @@ pub fn run() { } Event::RedrawRequested(_) => { error!("REDRAW!!!"); - let max_index = state.layout.geometry.indices.len(); - match state.gpu.render(max_index as u32) { + match state.gpu.render(&state.buffers) { Ok(_) => {} Err(wgpu::SurfaceError::Lost) => state.gpu.resize(state.gpu.size), Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,