diff options
author | 2020-02-09 05:32:56 +0100 | |
---|---|---|
committer | 2020-02-09 05:32:56 +0100 | |
commit | 4d7979aa77e481a5f8ff2f22bc2665912617fc04 (patch) | |
tree | 767519bfaf64a8abf3b091d3ff7d66cc44c4d4ad /examples/integration | |
parent | a244f9324305a2393da95be76085cba6a29b4b27 (diff) | |
download | iced-4d7979aa77e481a5f8ff2f22bc2665912617fc04.tar.gz iced-4d7979aa77e481a5f8ff2f22bc2665912617fc04.tar.bz2 iced-4d7979aa77e481a5f8ff2f22bc2665912617fc04.zip |
Add `integration` example
It showcases how to integrate iced in an existing graphical application.
Diffstat (limited to 'examples/integration')
-rw-r--r-- | examples/integration/Cargo.toml | 11 | ||||
-rw-r--r-- | examples/integration/src/controls.rs | 102 | ||||
-rw-r--r-- | examples/integration/src/main.rs | 204 | ||||
-rw-r--r-- | examples/integration/src/scene.rs | 119 | ||||
-rw-r--r-- | examples/integration/src/shader/frag.spv | bin | 0 -> 352 bytes | |||
-rw-r--r-- | examples/integration/src/shader/vert.spv | bin | 0 -> 904 bytes |
6 files changed, 436 insertions, 0 deletions
diff --git a/examples/integration/Cargo.toml b/examples/integration/Cargo.toml new file mode 100644 index 00000000..afc2c791 --- /dev/null +++ b/examples/integration/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "integration" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"] +edition = "2018" +publish = false + +[dependencies] +iced_winit = { path = "../../winit" } +iced_wgpu = { path = "../../wgpu" } +env_logger = "0.7" diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs new file mode 100644 index 00000000..0457a058 --- /dev/null +++ b/examples/integration/src/controls.rs @@ -0,0 +1,102 @@ +use crate::Scene; + +use iced_wgpu::Renderer; +use iced_winit::{ + slider, Align, Color, Column, Element, Length, Row, Slider, Text, +}; + +pub struct Controls { + sliders: [slider::State; 3], +} + +#[derive(Debug)] +pub enum Message { + BackgroundColorChanged(Color), +} + +impl Controls { + pub fn new() -> Controls { + Controls { + sliders: Default::default(), + } + } + + pub fn update(&self, message: Message, scene: &mut Scene) { + match message { + Message::BackgroundColorChanged(color) => { + scene.background_color = color; + } + } + } + + pub fn view<'a>( + &'a mut self, + scene: &Scene, + ) -> Element<'a, Message, Renderer> { + let [r, g, b] = &mut self.sliders; + let background_color = scene.background_color; + + let sliders = Row::new() + .width(Length::Units(500)) + .spacing(20) + .push(Slider::new( + r, + 0.0..=1.0, + scene.background_color.r, + move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color + }) + }, + )) + .push(Slider::new( + g, + 0.0..=1.0, + scene.background_color.g, + move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color + }) + }, + )) + .push(Slider::new( + b, + 0.0..=1.0, + scene.background_color.b, + move |b| { + Message::BackgroundColorChanged(Color { + b, + ..background_color + }) + }, + )); + + Row::new() + .width(Length::Fill) + .height(Length::Fill) + .align_items(Align::End) + .push( + Column::new() + .width(Length::Fill) + .align_items(Align::End) + .push( + Column::new() + .padding(10) + .spacing(10) + .push( + Text::new("Background color") + .color(Color::WHITE), + ) + .push(sliders) + .push( + Text::new(format!("{:?}", background_color)) + .size(14) + .color(Color::WHITE), + ), + ), + ) + .into() + } +} diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs new file mode 100644 index 00000000..ed36f736 --- /dev/null +++ b/examples/integration/src/main.rs @@ -0,0 +1,204 @@ +mod controls; +mod scene; + +use controls::Controls; +use scene::Scene; + +use iced_wgpu::{ + wgpu, window::SwapChain, Primitive, Renderer, Settings, Target, +}; +use iced_winit::{winit, Cache, Clipboard, MouseCursor, Size, UserInterface}; + +use winit::{ + event::{DeviceEvent, Event, ModifiersState, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, +}; + +pub fn main() { + env_logger::init(); + + // Initialize winit + let event_loop = EventLoop::new(); + let window = winit::window::Window::new(&event_loop).unwrap(); + let mut logical_size = + window.inner_size().to_logical(window.scale_factor()); + let mut modifiers = ModifiersState::default(); + + // Initialize WGPU + let adapter = wgpu::Adapter::request(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::Default, + backends: wgpu::BackendBit::PRIMARY, + }) + .expect("Request adapter"); + + let (mut device, mut queue) = + adapter.request_device(&wgpu::DeviceDescriptor { + extensions: wgpu::Extensions { + anisotropic_filtering: false, + }, + limits: wgpu::Limits::default(), + }); + + let surface = wgpu::Surface::create(&window); + + let mut swap_chain = { + let size = window.inner_size(); + + SwapChain::new(&device, &surface, size.width, size.height) + }; + let mut resized = false; + + // Initialize iced + let mut events = Vec::new(); + let mut cache = Some(Cache::default()); + let mut renderer = Renderer::new(&mut device, Settings::default()); + let mut output = (Primitive::None, MouseCursor::OutOfBounds); + let clipboard = Clipboard::new(&window); + + // Initialize scene and GUI controls + let mut scene = Scene::new(&device); + let mut controls = Controls::new(); + + // Run event loop + event_loop.run(move |event, _, control_flow| { + // You should change this if you want to render continuosly + *control_flow = ControlFlow::Wait; + + match event { + Event::DeviceEvent { + event: DeviceEvent::ModifiersChanged(new_modifiers), + .. + } => { + modifiers = new_modifiers; + } + Event::WindowEvent { event, .. } => { + match event { + WindowEvent::Resized(new_size) => { + logical_size = + new_size.to_logical(window.scale_factor()); + resized = true; + } + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + } + _ => {} + } + + // Map window event to iced event + if let Some(event) = iced_winit::conversion::window_event( + event, + window.scale_factor(), + modifiers, + ) { + events.push(event); + } + } + Event::MainEventsCleared => { + // If no relevant events happened, we can simply skip this + if events.is_empty() { + return; + } + + // We need to: + // 1. Process events of our user interface. + // 2. Update state as a result of any interaction. + // 3. Generate a new output for our renderer. + + // First, we build our user interface. + let mut user_interface = UserInterface::build( + controls.view(&scene), + Size::new(logical_size.width, logical_size.height), + cache.take().unwrap(), + &mut renderer, + ); + + // Then, we process the events, obtaining messages in return. + let messages = user_interface.update( + events.drain(..), + clipboard.as_ref().map(|c| c as _), + &renderer, + ); + + let user_interface = if messages.is_empty() { + // If there are no messages, no interactions we care about have + // happened. We can simply leave our user interface as it is. + user_interface + } else { + // If there are messages, we need to update our state + // accordingly and rebuild our user interface. + // We can only do this if we drop our user interface first + // by turning it into its cache. + cache = Some(user_interface.into_cache()); + + // In this example, `Controls` is the only part that cares + // about messages, so updating our state is pretty + // straightforward. + for message in messages { + controls.update(message, &mut scene); + } + + // Once the state has been changed, we rebuild our updated + // user interface. + UserInterface::build( + controls.view(&scene), + Size::new(logical_size.width, logical_size.height), + cache.take().unwrap(), + &mut renderer, + ) + }; + + // Finally, we just need to draw a new output for our renderer, + output = user_interface.draw(&mut renderer); + + // update our cache, + cache = Some(user_interface.into_cache()); + + // and request a redraw + window.request_redraw(); + } + Event::RedrawRequested(_) => { + if resized { + let size = window.inner_size(); + + swap_chain = SwapChain::new( + &device, + &surface, + size.width, + size.height, + ); + } + + let (frame, viewport) = swap_chain.next_frame(); + + let mut encoder = device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { todo: 0 }, + ); + + // We draw the scene first + scene.draw(&mut encoder, &frame.view); + + // And then iced on top + let mouse_cursor = renderer.draw( + &mut device, + &mut encoder, + Target { + texture: &frame.view, + viewport, + }, + &output, + window.scale_factor(), + &["Some debug information!"], + ); + + // Then we submit the work + queue.submit(&[encoder.finish()]); + + // And update the mouse cursor + window.set_cursor_icon(iced_winit::conversion::mouse_cursor( + mouse_cursor, + )); + } + _ => {} + } + }) +} diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs new file mode 100644 index 00000000..efb1921b --- /dev/null +++ b/examples/integration/src/scene.rs @@ -0,0 +1,119 @@ +use iced_wgpu::wgpu; +use iced_winit::Color; + +pub struct Scene { + pub background_color: Color, + pipeline: wgpu::RenderPipeline, + bind_group: wgpu::BindGroup, +} + +impl Scene { + pub fn new(device: &wgpu::Device) -> Scene { + let (pipeline, bind_group) = build_pipeline(device); + + Scene { + background_color: Color::BLACK, + pipeline, + bind_group, + } + } + + pub fn draw( + &self, + encoder: &mut wgpu::CommandEncoder, + target: &wgpu::TextureView, + ) { + let mut rpass = + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[ + wgpu::RenderPassColorAttachmentDescriptor { + attachment: target, + resolve_target: None, + load_op: wgpu::LoadOp::Clear, + store_op: wgpu::StoreOp::Store, + clear_color: { + let [r, g, b, a] = + self.background_color.into_linear(); + + wgpu::Color { + r: r as f64, + g: g as f64, + b: b as f64, + a: a as f64, + } + }, + }, + ], + depth_stencil_attachment: None, + }); + + rpass.set_pipeline(&self.pipeline); + rpass.set_bind_group(0, &self.bind_group, &[]); + rpass.draw(0..3, 0..1); + } +} + +fn build_pipeline( + device: &wgpu::Device, +) -> (wgpu::RenderPipeline, wgpu::BindGroup) { + let vs = include_bytes!("shader/vert.spv"); + let fs = include_bytes!("shader/frag.spv"); + + let vs_module = device.create_shader_module( + &wgpu::read_spirv(std::io::Cursor::new(&vs[..])).unwrap(), + ); + + let fs_module = device.create_shader_module( + &wgpu::read_spirv(std::io::Cursor::new(&fs[..])).unwrap(), + ); + + let bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + bindings: &[], + }); + + let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + layout: &bind_group_layout, + bindings: &[], + }); + + let pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + bind_group_layouts: &[&bind_group_layout], + }); + + let pipeline = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + layout: &pipeline_layout, + vertex_stage: wgpu::ProgrammableStageDescriptor { + module: &vs_module, + entry_point: "main", + }, + fragment_stage: Some(wgpu::ProgrammableStageDescriptor { + module: &fs_module, + entry_point: "main", + }), + rasterization_state: Some(wgpu::RasterizationStateDescriptor { + front_face: wgpu::FrontFace::Ccw, + cull_mode: wgpu::CullMode::None, + depth_bias: 0, + depth_bias_slope_scale: 0.0, + depth_bias_clamp: 0.0, + }), + primitive_topology: wgpu::PrimitiveTopology::TriangleList, + color_states: &[wgpu::ColorStateDescriptor { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + color_blend: wgpu::BlendDescriptor::REPLACE, + alpha_blend: wgpu::BlendDescriptor::REPLACE, + write_mask: wgpu::ColorWrite::ALL, + }], + depth_stencil_state: None, + index_format: wgpu::IndexFormat::Uint16, + vertex_buffers: &[], + sample_count: 1, + sample_mask: !0, + alpha_to_coverage_enabled: false, + }); + + (pipeline, bind_group) +} diff --git a/examples/integration/src/shader/frag.spv b/examples/integration/src/shader/frag.spv Binary files differnew file mode 100644 index 00000000..9d6807c9 --- /dev/null +++ b/examples/integration/src/shader/frag.spv diff --git a/examples/integration/src/shader/vert.spv b/examples/integration/src/shader/vert.spv Binary files differnew file mode 100644 index 00000000..0cabc9c0 --- /dev/null +++ b/examples/integration/src/shader/vert.spv |