diff options
Diffstat (limited to 'examples/integration/src')
-rw-r--r-- | examples/integration/src/controls.rs | 112 | ||||
-rw-r--r-- | examples/integration/src/main.rs | 289 | ||||
-rw-r--r-- | examples/integration/src/scene.rs | 102 | ||||
-rw-r--r-- | examples/integration/src/shader/frag.wgsl | 4 | ||||
-rw-r--r-- | examples/integration/src/shader/vert.wgsl | 6 |
5 files changed, 513 insertions, 0 deletions
diff --git a/examples/integration/src/controls.rs b/examples/integration/src/controls.rs new file mode 100644 index 00000000..533cb6e2 --- /dev/null +++ b/examples/integration/src/controls.rs @@ -0,0 +1,112 @@ +use iced_wgpu::Renderer; +use iced_winit::widget::{slider, text_input, Column, Row, Text}; +use iced_winit::{Alignment, Color, Command, Element, Length, Program}; + +pub struct Controls { + background_color: Color, + text: String, +} + +#[derive(Debug, Clone)] +pub enum Message { + BackgroundColorChanged(Color), + TextChanged(String), +} + +impl Controls { + pub fn new() -> Controls { + Controls { + background_color: Color::BLACK, + text: Default::default(), + } + } + + pub fn background_color(&self) -> Color { + self.background_color + } +} + +impl Program for Controls { + type Renderer = Renderer; + type Message = Message; + + fn update(&mut self, message: Message) -> Command<Message> { + match message { + Message::BackgroundColorChanged(color) => { + self.background_color = color; + } + Message::TextChanged(text) => { + self.text = text; + } + } + + Command::none() + } + + fn view(&self) -> Element<Message, Renderer> { + let background_color = self.background_color; + let text = &self.text; + + let sliders = Row::new() + .width(500) + .spacing(20) + .push( + slider(0.0..=1.0, background_color.r, move |r| { + Message::BackgroundColorChanged(Color { + r, + ..background_color + }) + }) + .step(0.01), + ) + .push( + slider(0.0..=1.0, background_color.g, move |g| { + Message::BackgroundColorChanged(Color { + g, + ..background_color + }) + }) + .step(0.01), + ) + .push( + slider(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(Alignment::End) + .push( + Column::new() + .width(Length::Fill) + .align_items(Alignment::End) + .push( + Column::new() + .padding(10) + .spacing(10) + .push( + Text::new("Background color") + .style(Color::WHITE), + ) + .push(sliders) + .push( + Text::new(format!("{background_color:?}")) + .size(14) + .style(Color::WHITE), + ) + .push(text_input( + "Placeholder", + text, + Message::TextChanged, + )), + ), + ) + .into() + } +} diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs new file mode 100644 index 00000000..2a56b6fa --- /dev/null +++ b/examples/integration/src/main.rs @@ -0,0 +1,289 @@ +mod controls; +mod scene; + +use controls::Controls; +use scene::Scene; + +use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport}; +use iced_winit::{ + conversion, futures, program, renderer, winit, Clipboard, Color, Debug, + Size, +}; + +use winit::{ + dpi::PhysicalPosition, + event::{Event, ModifiersState, WindowEvent}, + event_loop::{ControlFlow, EventLoop}, +}; + +#[cfg(target_arch = "wasm32")] +use wasm_bindgen::JsCast; +#[cfg(target_arch = "wasm32")] +use web_sys::HtmlCanvasElement; +#[cfg(target_arch = "wasm32")] +use winit::platform::web::WindowBuilderExtWebSys; + +pub fn main() { + #[cfg(target_arch = "wasm32")] + let canvas_element = { + console_log::init_with_level(log::Level::Debug) + .expect("could not initialize logger"); + std::panic::set_hook(Box::new(console_error_panic_hook::hook)); + + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| doc.get_element_by_id("iced_canvas")) + .and_then(|element| element.dyn_into::<HtmlCanvasElement>().ok()) + .expect("Canvas with id `iced_canvas` is missing") + }; + #[cfg(not(target_arch = "wasm32"))] + env_logger::init(); + + // Initialize winit + let event_loop = EventLoop::new(); + + #[cfg(target_arch = "wasm32")] + let window = winit::window::WindowBuilder::new() + .with_canvas(Some(canvas_element)) + .build(&event_loop) + .expect("Failed to build winit window"); + + #[cfg(not(target_arch = "wasm32"))] + 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 + + #[cfg(target_arch = "wasm32")] + let default_backend = wgpu::Backends::GL; + #[cfg(not(target_arch = "wasm32"))] + let default_backend = wgpu::Backends::PRIMARY; + + let backend = + wgpu::util::backend_bits_from_env().unwrap_or(default_backend); + + let instance = wgpu::Instance::new(backend); + let surface = unsafe { instance.create_surface(&window) }; + + let (format, (device, queue)) = futures::executor::block_on(async { + let adapter = wgpu::util::initialize_adapter_from_env_or_default( + &instance, + backend, + Some(&surface), + ) + .await + .expect("No suitable GPU adapters found on the system!"); + + let adapter_features = adapter.features(); + + #[cfg(target_arch = "wasm32")] + let needed_limits = wgpu::Limits::downlevel_webgl2_defaults() + .using_resolution(adapter.limits()); + + #[cfg(not(target_arch = "wasm32"))] + let needed_limits = wgpu::Limits::default(); + + ( + surface + .get_supported_formats(&adapter) + .first() + .copied() + .expect("Get preferred format"), + adapter + .request_device( + &wgpu::DeviceDescriptor { + label: None, + features: adapter_features & wgpu::Features::default(), + limits: needed_limits, + }, + None, + ) + .await + .expect("Request device"), + ) + }); + + surface.configure( + &device, + &wgpu::SurfaceConfiguration { + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + format, + width: physical_size.width, + height: physical_size.height, + present_mode: wgpu::PresentMode::AutoVsync, + alpha_mode: wgpu::CompositeAlphaMode::Auto, + }, + ); + + let mut resized = false; + + // Initialize staging belt + let mut staging_belt = wgpu::util::StagingBelt::new(5 * 1024); + + // Initialize scene and GUI controls + let scene = Scene::new(&device, format); + let controls = Controls::new(); + + // Initialize iced + let mut debug = Debug::new(); + let mut renderer = + Renderer::new(Backend::new(&device, Settings::default(), format)); + + let mut state = program::State::new( + controls, + viewport.logical_size(), + &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(_) => { + 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, + &iced_wgpu::Theme::Dark, + &renderer::Style { text_color: Color::WHITE }, + &mut clipboard, + &mut debug, + ); + + // and request a redraw + window.request_redraw(); + } + } + Event::RedrawRequested(_) => { + if resized { + let size = window.inner_size(); + + viewport = Viewport::with_physical_size( + Size::new(size.width, size.height), + window.scale_factor(), + ); + + surface.configure( + &device, + &wgpu::SurfaceConfiguration { + format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + width: size.width, + height: size.height, + present_mode: wgpu::PresentMode::AutoVsync, + alpha_mode: wgpu::CompositeAlphaMode::Auto + }, + ); + + resized = false; + } + + match surface.get_current_texture() { + Ok(frame) => { + let mut encoder = device.create_command_encoder( + &wgpu::CommandEncoderDescriptor { label: None }, + ); + + let program = state.program(); + + let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default()); + + { + // We clear the frame + let mut render_pass = scene.clear( + &view, + &mut encoder, + program.background_color(), + ); + + // Draw the scene + scene.draw(&mut render_pass); + } + + // And then iced on top + renderer.with_primitives(|backend, primitive| { + backend.present( + &device, + &mut staging_belt, + &mut encoder, + &view, + primitive, + &viewport, + &debug.overlay(), + ); + }); + + // Then we submit the work + staging_belt.finish(); + queue.submit(Some(encoder.finish())); + frame.present(); + + // Update the mouse cursor + window.set_cursor_icon( + iced_winit::conversion::mouse_interaction( + state.mouse_interaction(), + ), + ); + + // And recall staging buffers + staging_belt.recall(); + + } + Err(error) => match error { + wgpu::SurfaceError::OutOfMemory => { + panic!("Swapchain error: {error}. Rendering cannot continue.") + } + _ => { + // Try rendering again next frame. + window.request_redraw(); + } + }, + } + } + _ => {} + } + }) +} diff --git a/examples/integration/src/scene.rs b/examples/integration/src/scene.rs new file mode 100644 index 00000000..3e41fbda --- /dev/null +++ b/examples/integration/src/scene.rs @@ -0,0 +1,102 @@ +use iced_wgpu::wgpu; +use iced_winit::Color; + +pub struct Scene { + pipeline: wgpu::RenderPipeline, +} + +impl Scene { + pub fn new( + device: &wgpu::Device, + texture_format: wgpu::TextureFormat, + ) -> Scene { + let pipeline = build_pipeline(device, texture_format); + + 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: &[Some(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, + texture_format: wgpu::TextureFormat, +) -> wgpu::RenderPipeline { + let (vs_module, fs_module) = ( + device.create_shader_module(wgpu::include_wgsl!("shader/vert.wgsl")), + device.create_shader_module(wgpu::include_wgsl!("shader/frag.wgsl")), + ); + + let pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + push_constant_ranges: &[], + bind_group_layouts: &[], + }); + + 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: &[Some(wgpu::ColorTargetState { + format: texture_format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent::REPLACE, + alpha: wgpu::BlendComponent::REPLACE, + }), + write_mask: wgpu::ColorWrites::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, + }, + multiview: None, + }) +} diff --git a/examples/integration/src/shader/frag.wgsl b/examples/integration/src/shader/frag.wgsl new file mode 100644 index 00000000..cf27bb56 --- /dev/null +++ b/examples/integration/src/shader/frag.wgsl @@ -0,0 +1,4 @@ +@fragment +fn main() -> @location(0) vec4<f32> { + return vec4<f32>(1.0, 0.0, 0.0, 1.0); +} diff --git a/examples/integration/src/shader/vert.wgsl b/examples/integration/src/shader/vert.wgsl new file mode 100644 index 00000000..e353e6ba --- /dev/null +++ b/examples/integration/src/shader/vert.wgsl @@ -0,0 +1,6 @@ +@vertex +fn main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4<f32> { + let x = f32(1 - i32(in_vertex_index)) * 0.5; + let y = f32(1 - i32(in_vertex_index & 1u) * 2) * 0.5; + return vec4<f32>(x, y, 0.0, 1.0); +} |