diff options
Diffstat (limited to 'wgpu/src/renderer.rs')
-rw-r--r-- | wgpu/src/renderer.rs | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/wgpu/src/renderer.rs b/wgpu/src/renderer.rs new file mode 100644 index 00000000..df8887e3 --- /dev/null +++ b/wgpu/src/renderer.rs @@ -0,0 +1,167 @@ +use crate::Primitive; +use iced_native::{renderer::Debugger, Color, Layout, Point, Widget}; + +use raw_window_handle::HasRawWindowHandle; +use wgpu::{ + Adapter, CommandEncoderDescriptor, Device, DeviceDescriptor, Extensions, + Instance, Limits, PowerPreference, RequestAdapterOptions, Surface, + SwapChain, SwapChainDescriptor, TextureFormat, TextureUsage, +}; +use wgpu_glyph::{GlyphBrush, GlyphBrushBuilder, Section}; + +use std::{cell::RefCell, rc::Rc}; + +mod button; +mod checkbox; +mod column; +mod image; +mod radio; +mod row; +mod slider; +mod text; + +pub struct Renderer { + instance: Instance, + surface: Surface, + adapter: Adapter, + device: Device, + glyph_brush: Rc<RefCell<GlyphBrush<'static, ()>>>, +} + +pub struct Target { + width: u16, + height: u16, + swap_chain: SwapChain, +} + +impl Renderer { + pub fn new<W: HasRawWindowHandle>(window: &W) -> Self { + let instance = Instance::new(); + + let adapter = instance.request_adapter(&RequestAdapterOptions { + power_preference: PowerPreference::LowPower, + }); + + let mut device = adapter.request_device(&DeviceDescriptor { + extensions: Extensions { + anisotropic_filtering: false, + }, + limits: Limits { max_bind_groups: 1 }, + }); + + let surface = instance.create_surface(window.raw_window_handle()); + + // TODO: Think about font loading strategy + // Loading system fonts with fallback may be a good idea + let font: &[u8] = + include_bytes!("../../examples/tour/resources/Roboto-Regular.ttf"); + + let glyph_brush = GlyphBrushBuilder::using_font_bytes(font) + .build(&mut device, TextureFormat::Bgra8UnormSrgb); + + Self { + instance, + surface, + adapter, + device, + glyph_brush: Rc::new(RefCell::new(glyph_brush)), + } + } + + pub fn target(&self, width: u16, height: u16) -> Target { + Target { + width, + height, + swap_chain: self.device.create_swap_chain( + &self.surface, + &SwapChainDescriptor { + usage: TextureUsage::OUTPUT_ATTACHMENT, + format: TextureFormat::Bgra8UnormSrgb, + width: u32::from(width), + height: u32::from(height), + present_mode: wgpu::PresentMode::Vsync, + }, + ), + } + } + + pub fn draw(&mut self, target: &mut Target, primitive: &Primitive) { + let frame = target.swap_chain.get_next_texture(); + + let mut encoder = self + .device + .create_command_encoder(&CommandEncoderDescriptor { todo: 0 }); + + let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { + attachment: &frame.view, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: wgpu::Color { + r: 1.0, + g: 1.0, + b: 1.0, + a: 1.0, + }, + }], + depth_stencil_attachment: None, + }); + + self.draw_primitive(primitive); + + self.glyph_brush + .borrow_mut() + .draw_queued( + &mut self.device, + &mut encoder, + &frame.view, + u32::from(target.width), + u32::from(target.height), + ) + .expect("Draw text"); + + self.device.get_queue().submit(&[encoder.finish()]); + } + + fn draw_primitive(&mut self, primitive: &Primitive) { + match primitive { + Primitive::None => {} + Primitive::Group { primitives } => { + // TODO: Inspect a bit and regroup (?) + for primitive in primitives { + self.draw_primitive(primitive) + } + } + Primitive::Text { + content, + bounds, + size, + } => self.glyph_brush.borrow_mut().queue(Section { + text: &content, + screen_position: (bounds.x, bounds.y), + bounds: (bounds.width, bounds.height), + scale: wgpu_glyph::Scale { x: *size, y: *size }, + ..Default::default() + }), + } + } +} + +impl iced_native::Renderer for Renderer { + // TODO: Add `MouseCursor` here (?) + type Primitive = Primitive; +} + +impl Debugger for Renderer { + fn explain<Message>( + &mut self, + widget: &dyn Widget<Message, Self>, + layout: Layout<'_>, + cursor_position: Point, + _color: Color, + ) -> Self::Primitive { + // TODO: Include a bordered box to display layout bounds + widget.draw(self, layout, cursor_position) + } +} |