diff options
author | 2021-08-13 20:01:28 +0700 | |
---|---|---|
committer | 2021-08-13 20:12:07 +0700 | |
commit | 77a0b68aa176782e95048795aec15c20a3e60ec6 (patch) | |
tree | e4e7c2a34995a91b99a657539f92c8637f52c513 /examples/integration_wgpu | |
parent | a646b11109aa451614d3fdf688e600aa48b87a56 (diff) | |
download | iced-77a0b68aa176782e95048795aec15c20a3e60ec6.tar.gz iced-77a0b68aa176782e95048795aec15c20a3e60ec6.tar.bz2 iced-77a0b68aa176782e95048795aec15c20a3e60ec6.zip |
Rename `integration` examples
Diffstat (limited to 'examples/integration_wgpu')
-rw-r--r-- | examples/integration_wgpu/Cargo.toml | 11 | ||||
-rw-r--r-- | examples/integration_wgpu/README.md | 20 | ||||
-rw-r--r-- | examples/integration_wgpu/src/controls.rs | 110 | ||||
-rw-r--r-- | examples/integration_wgpu/src/main.rs | 239 | ||||
-rw-r--r-- | examples/integration_wgpu/src/scene.rs | 99 | ||||
-rw-r--r-- | examples/integration_wgpu/src/shader/frag.spv | bin | 0 -> 352 bytes | |||
-rw-r--r-- | examples/integration_wgpu/src/shader/vert.spv | bin | 0 -> 904 bytes |
7 files changed, 479 insertions, 0 deletions
diff --git a/examples/integration_wgpu/Cargo.toml b/examples/integration_wgpu/Cargo.toml new file mode 100644 index 00000000..743c655e --- /dev/null +++ b/examples/integration_wgpu/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "integration_wgpu" +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.8" diff --git a/examples/integration_wgpu/README.md b/examples/integration_wgpu/README.md new file mode 100644 index 00000000..53ffd3e5 --- /dev/null +++ b/examples/integration_wgpu/README.md @@ -0,0 +1,20 @@ +## `wgpu` integration + +A demonstration of how to integrate Iced in an existing graphical application +that leverages [`wgpu`]. + +The __[`main`]__ file contains all the code of the example. + +<div align="center"> + <a href="https://gfycat.com/nicemediocrekodiakbear"> + <img src="https://thumbs.gfycat.com/NiceMediocreKodiakbear-small.gif"> + </a> +</div> + +You can run it with `cargo run`: +``` +cargo run --package integration +``` + +[`main`]: src/main.rs +[`wgpu`]: https://github.com/gfx-rs/wgpu diff --git a/examples/integration_wgpu/src/controls.rs b/examples/integration_wgpu/src/controls.rs new file mode 100644 index 00000000..36ee9b7e --- /dev/null +++ b/examples/integration_wgpu/src/controls.rs @@ -0,0 +1,110 @@ +use iced_wgpu::Renderer; +use iced_winit::{ + slider, Align, Clipboard, Color, Column, Command, Element, Length, Program, + Row, Slider, Text, +}; + +pub struct Controls { + background_color: Color, + sliders: [slider::State; 3], +} + +#[derive(Debug, Clone)] +pub enum Message { + BackgroundColorChanged(Color), +} + +impl Controls { + pub fn new() -> Controls { + Controls { + background_color: Color::BLACK, + sliders: Default::default(), + } + } + + pub fn background_color(&self) -> Color { + self.background_color + } +} + +impl Program for Controls { + type Renderer = Renderer; + type Message = Message; + type Clipboard = Clipboard; + + fn update( + &mut self, + message: Message, + _clipboard: &mut Clipboard, + ) -> Command<Message> { + match message { + Message::BackgroundColorChanged(color) => { + self.background_color = color; + } + } + + Command::none() + } + + fn view(&mut self) -> Element<Message, Renderer> { + let [r, g, b] = &mut self.sliders; + let background_color = self.background_color; + + let sliders = Row::new() + .width(Length::Units(500)) + .spacing(20) + .push( + Slider::new(r, 0.0..=1.0, background_color.r, move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(g, 0.0..=1.0, background_color.g, move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color + }) + }) + .step(0.01), + ) + .push( + Slider::new(b, 0.0..=1.0, background_color.b, move |b| { + Message::BackgroundColorChanged(Color { + b, + ..background_color + }) + }) + .step(0.01), + ); + + 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_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs new file mode 100644 index 00000000..6f319466 --- /dev/null +++ b/examples/integration_wgpu/src/main.rs @@ -0,0 +1,239 @@ +mod controls; +mod scene; + +use controls::Controls; +use scene::Scene; + +use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; +use iced_winit::{conversion, futures, program, winit, Clipboard, Debug, Size}; + +use futures::task::SpawnExt; +use winit::{ + dpi::PhysicalPosition, + event::{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 physical_size = window.inner_size(); + let mut viewport = Viewport::with_physical_size( + Size::new(physical_size.width, physical_size.height), + window.scale_factor(), + ); + let mut cursor_position = PhysicalPosition::new(-1.0, -1.0); + let mut modifiers = ModifiersState::default(); + let mut clipboard = Clipboard::connect(&window); + + // Initialize wgpu + let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY); + let surface = unsafe { instance.create_surface(&window) }; + + let (format, (mut device, queue)) = futures::executor::block_on(async { + let adapter = instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::HighPerformance, + compatible_surface: Some(&surface), + }) + .await + .expect("Request adapter"); + + ( + adapter + .get_swap_chain_preferred_format(&surface) + .expect("Get preferred format"), + adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: wgpu::Features::empty(), + limits: wgpu::Limits::default(), + }, + None, + ) + .await + .expect("Request device"), + ) + }); + + let mut swap_chain = { + let size = window.inner_size(); + + device.create_swap_chain( + &surface, + &wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::RENDER_ATTACHMENT, + format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Mailbox, + }, + ) + }; + let mut resized = false; + + // Initialize staging belt and local pool + let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); + let mut local_pool = futures::executor::LocalPool::new(); + + // Initialize scene and GUI controls + let scene = Scene::new(&mut device); + let controls = Controls::new(); + + // Initialize iced + let mut debug = Debug::new(); + let mut renderer = + Renderer::new(Backend::new(&mut device, Settings::default(), format)); + + let mut state = program::State::new( + controls, + viewport.logical_size(), + conversion::cursor_position(cursor_position, viewport.scale_factor()), + &mut renderer, + &mut debug, + ); + + // 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::WindowEvent { event, .. } => { + match event { + WindowEvent::CursorMoved { position, .. } => { + cursor_position = position; + } + WindowEvent::ModifiersChanged(new_modifiers) => { + modifiers = new_modifiers; + } + WindowEvent::Resized(new_size) => { + viewport = Viewport::with_physical_size( + Size::new(new_size.width, new_size.height), + 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, + ) { + state.queue_event(event); + } + } + Event::MainEventsCleared => { + // If there are events pending + if !state.is_queue_empty() { + // We update iced + let _ = state.update( + viewport.logical_size(), + conversion::cursor_position( + cursor_position, + viewport.scale_factor(), + ), + &mut renderer, + &mut clipboard, + &mut debug, + ); + + // and request a redraw + window.request_redraw(); + } + } + Event::RedrawRequested(_) => { + if resized { + let size = window.inner_size(); + + swap_chain = device.create_swap_chain( + &surface, + &wgpu::SwapChainDescriptor { + usage: wgpu::TextureUsage::RENDER_ATTACHMENT, + format: format, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::Mailbox, + }, + ); + + resized = false; + } + + match swap_chain.get_current_frame() { + Ok(frame) => { + let mut encoder = device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { label: None }, + ); + + let program = state.program(); + + { + // We clear the frame + let mut render_pass = scene.clear( + &frame.output.view, + &mut encoder, + program.background_color(), + ); + + // Draw the scene + scene.draw(&mut render_pass); + } + + // And then iced on top + let mouse_interaction = renderer.backend_mut().draw( + &mut device, + &mut staging_belt, + &mut encoder, + &frame.output.view, + &viewport, + state.primitive(), + &debug.overlay(), + ); + + // Then we submit the work + staging_belt.finish(); + queue.submit(Some(encoder.finish())); + + // Update the mouse cursor + window.set_cursor_icon( + iced_winit::conversion::mouse_interaction( + mouse_interaction, + ), + ); + + // And recall staging buffers + local_pool + .spawner() + .spawn(staging_belt.recall()) + .expect("Recall staging buffers"); + + local_pool.run_until_stalled(); + } + Err(error) => match error { + wgpu::SwapChainError::OutOfMemory => { + panic!("Swapchain error: {}. Rendering cannot continue.", error) + } + _ => { + // Try rendering again next frame. + window.request_redraw(); + } + }, + } + } + _ => {} + } + }) +} diff --git a/examples/integration_wgpu/src/scene.rs b/examples/integration_wgpu/src/scene.rs new file mode 100644 index 00000000..3e8277c8 --- /dev/null +++ b/examples/integration_wgpu/src/scene.rs @@ -0,0 +1,99 @@ +use iced_wgpu::wgpu; +use iced_winit::Color; + +pub struct Scene { + pipeline: wgpu::RenderPipeline, +} + +impl Scene { + pub fn new(device: &wgpu::Device) -> Scene { + let pipeline = build_pipeline(device); + + Scene { pipeline } + } + + pub fn clear<'a>( + &self, + target: &'a wgpu::TextureView, + encoder: &'a mut wgpu::CommandEncoder, + background_color: Color, + ) -> wgpu::RenderPass<'a> { + encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[wgpu::RenderPassColorAttachment { + view: target, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear({ + let [r, g, b, a] = background_color.into_linear(); + + wgpu::Color { + r: r as f64, + g: g as f64, + b: b as f64, + a: a as f64, + } + }), + store: true, + }, + }], + depth_stencil_attachment: None, + }) + } + + pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { + render_pass.set_pipeline(&self.pipeline); + render_pass.draw(0..3, 0..1); + } +} + +fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline { + let vs_module = + device.create_shader_module(&wgpu::include_spirv!("shader/vert.spv")); + + let fs_module = + device.create_shader_module(&wgpu::include_spirv!("shader/frag.spv")); + + let pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + push_constant_ranges: &[], + bind_group_layouts: &[], + }); + + let pipeline = + device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &vs_module, + entry_point: "main", + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &fs_module, + entry_point: "main", + targets: &[wgpu::ColorTargetState { + format: wgpu::TextureFormat::Bgra8UnormSrgb, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent::REPLACE, + alpha: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrite::ALL, + }], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + front_face: wgpu::FrontFace::Ccw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + }); + + pipeline +} diff --git a/examples/integration_wgpu/src/shader/frag.spv b/examples/integration_wgpu/src/shader/frag.spv Binary files differnew file mode 100644 index 00000000..9d6807c9 --- /dev/null +++ b/examples/integration_wgpu/src/shader/frag.spv diff --git a/examples/integration_wgpu/src/shader/vert.spv b/examples/integration_wgpu/src/shader/vert.spv Binary files differnew file mode 100644 index 00000000..0cabc9c0 --- /dev/null +++ b/examples/integration_wgpu/src/shader/vert.spv |