demo_wgpu/
demo_wgpu.rs

1use std::sync::Arc;
2
3use winit::{event::{Event, WindowEvent}, event_loop::{EventLoop, ControlFlow}};
4use thyme::{bench};
5
6mod demo;
7
8/// A basic RPG character sheet, using the wgpu backend.
9/// This file contains the application setup code and wgpu specifics.
10/// the `demo.rs` file contains the Thyme UI code and logic.
11/// A simple party creator and character sheet for an RPG.
12fn main() -> Result<(), Box<dyn std::error::Error>> {
13    use winit::{ window::WindowBuilder };
14
15    // initialize our very basic logger so error messages go to stdout
16    thyme::log::init(log::Level::Warn).unwrap();
17
18    let window_size = [1280.0, 720.0];
19    let events_loop = EventLoop::new();
20
21    // create winit window
22    let window = WindowBuilder::new()
23        .with_title("Thyme WGPU Demo")
24        .with_inner_size(winit::dpi::LogicalSize::new(window_size[0], window_size[1]))
25        .build(&events_loop)?;
26
27    // setup WGPU
28    let instance_desc = wgpu::InstanceDescriptor {
29        backends: wgpu::Backends::PRIMARY,
30        dx12_shader_compiler: wgpu::Dx12Compiler::Fxc,
31    };
32    let instance = wgpu::Instance::new(instance_desc);
33    let surface = unsafe { instance.create_surface(&window).map_err(thyme::Error::WgpuSurface)? };
34    let (_adapter, device, queue) = futures::executor::block_on(setup_wgpu(&instance, &surface));
35    let surface_config = get_surface_config(window_size[0] as u32, window_size[1] as u32);
36    surface.configure(&device, &surface_config);
37
38    // create thyme backend
39    let mut renderer = thyme::WgpuRenderer::new(Arc::clone(&device), Arc::clone(&queue));
40    let mut io = thyme::WinitIo::new(&events_loop, window_size.into())?;
41    let mut context_builder = thyme::ContextBuilder::with_defaults();
42
43    demo::register_assets(&mut context_builder);
44
45    let mut context = context_builder.build(&mut renderer, &mut io)?;
46
47    let mut party = demo::Party::default();
48
49    let mut last_frame = std::time::Instant::now();
50    let frame_time = std::time::Duration::from_millis(16);
51
52    // run main loop
53    events_loop.run(move |event, _, control_flow| match event {
54        Event::MainEventsCleared => {
55            if std::time::Instant::now() > last_frame + frame_time {
56                window.request_redraw();
57            }
58            *control_flow = ControlFlow::WaitUntil(last_frame + frame_time);
59        },
60        Event::RedrawRequested(_) => {
61            last_frame = std::time::Instant::now();
62
63            party.check_context_changes(&mut context, &mut renderer);
64
65            let frame = surface.get_current_texture().unwrap();
66            let view = frame.texture.create_view(&wgpu::TextureViewDescriptor::default());
67            let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
68
69            bench::run("thyme", || {
70                window.set_cursor_visible(!party.theme_has_mouse_cursor());
71
72                let mut ui = context.create_frame();
73
74                bench::run("frame", || {
75                    demo::build_ui(&mut ui, &mut party);
76                });
77
78                bench::run("draw", || {
79                    {
80                        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
81                            label: None,
82                            color_attachments: &[Some(wgpu::RenderPassColorAttachment {
83                                view: &view,
84                                resolve_target: None,
85                                ops: wgpu::Operations {
86                                    load: wgpu::LoadOp::Clear(wgpu::Color { r: 0.5, g: 0.5, b: 0.5, a: 1.0 }),
87                                    store: true,
88                                },
89                            })],
90                            depth_stencil_attachment: None,
91                        });
92
93                        renderer.draw_frame(ui, &mut render_pass);
94                    }
95
96                    queue.submit(Some(encoder.finish()));
97                    frame.present();
98                });
99            });
100        },
101        Event::WindowEvent { event: WindowEvent::CloseRequested, .. } => *control_flow = ControlFlow::Exit,
102        event => {
103            // recreate swap chain on resize, but also still pass the event to thyme
104            if let Event::WindowEvent { event: WindowEvent::Resized(_), ..} = event {
105                let size: (u32, u32) = window.inner_size().into();
106
107                let surface_config = get_surface_config(size.0, size.1);
108                surface.configure(&device, &surface_config);
109            }
110
111            io.handle_event(&mut context, &event);
112        }
113    })
114}
115
116async fn setup_wgpu(
117    instance: &wgpu::Instance,
118    surface: &wgpu::Surface
119) -> (wgpu::Adapter, Arc<wgpu::Device>, Arc<wgpu::Queue>) {
120    let adapter = instance.request_adapter(&wgpu::RequestAdapterOptions {
121        power_preference: wgpu::PowerPreference::LowPower,
122        // Request an adapter which can render to our surface
123        compatible_surface: Some(surface),
124        force_fallback_adapter: false,
125    }).await.unwrap();
126
127    // Create the logical device and command queue
128    let (device, queue) = adapter.request_device(
129        &wgpu::DeviceDescriptor {
130            label: None,
131            features: wgpu::Features::empty(),
132            limits: wgpu::Limits::default(),
133        },
134        None,
135    ).await.expect("Failed to create WGPU device");
136
137    (adapter, Arc::new(device), Arc::new(queue))
138}
139
140fn get_surface_config(width: u32, height: u32) -> wgpu::SurfaceConfiguration {
141    wgpu::SurfaceConfiguration {
142        usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
143        format: wgpu::TextureFormat::Bgra8Unorm,
144        width,
145        height,
146        present_mode: wgpu::PresentMode::AutoVsync,
147        alpha_mode: wgpu::CompositeAlphaMode::Auto,
148        view_formats: vec![],
149    }
150}