comfy_wgpu/
device.rs

1use crate::*;
2
3pub async fn create_graphics_context(
4    window: &'static Window,
5) -> GraphicsContext {
6    let size = window.inner_size();
7
8    let backends =
9        wgpu::util::backend_bits_from_env().unwrap_or(wgpu::Backends::all());
10
11    let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
12        // backends: wgpu::Backends::GL,
13        backends,
14        dx12_shader_compiler: Default::default(),
15        // TODO: make validation configurable?
16        flags: if cfg!(debug_assertions) {
17            wgpu::InstanceFlags::debugging()
18        } else {
19            wgpu::InstanceFlags::VALIDATION
20        },
21        gles_minor_version: wgpu::Gles3MinorVersion::Automatic,
22    });
23
24    let surface =
25        instance.create_surface(window).expect("surface config must be valid");
26
27    trace!("Requesting adapter");
28
29    let adapter = instance
30        .request_adapter(&wgpu::RequestAdapterOptions {
31            power_preference: power_preference_to_wgpu(
32                game_config().power_preference,
33            ),
34            compatible_surface: Some(&surface),
35            force_fallback_adapter: false,
36        })
37        .await
38        .expect("adapter config must be valid");
39
40    info!("Using adapter: {:?}", adapter.get_info().name);
41
42    trace!("Requesting device");
43
44    let max_texture_dim_2d =
45        game_config().max_texture_dimension_2d_override.unwrap_or(4096);
46
47    #[cfg(not(target_arch = "wasm32"))]
48    let limits = wgpu::Limits {
49        max_texture_dimension_2d: max_texture_dim_2d,
50        ..wgpu::Limits::downlevel_defaults()
51    };
52
53    #[cfg(target_arch = "wasm32")]
54    let limits = wgpu::Limits {
55        max_texture_dimension_2d: max_texture_dim_2d,
56        ..wgpu::Limits::downlevel_webgl2_defaults()
57    };
58
59    let (device, queue) = adapter
60        .request_device(
61            &wgpu::DeviceDescriptor {
62                required_features: wgpu::Features::empty(),
63                required_limits: limits,
64                label: None,
65            },
66            None,
67        )
68        .await
69        .expect("failed to create wgpu adapter");
70
71    #[cfg(fature = "ci-release")]
72    device.on_uncaptured_error(Box::new(|err| {
73        error!("WGPU ERROR: {:?}", err);
74        panic!("Exiting due to wgpu error: {:?}", err);
75    }));
76
77    let caps = surface.get_capabilities(&adapter);
78    let supported_formats = caps.formats;
79    info!("Supported formats: {:?}", supported_formats);
80
81    #[cfg(not(target_arch = "wasm32"))]
82    let preferred_format = wgpu::TextureFormat::Bgra8UnormSrgb;
83    #[cfg(target_arch = "wasm32")]
84    let preferred_format = wgpu::TextureFormat::Rgba8UnormSrgb;
85
86    let monitor_surface_format =
87        if supported_formats.contains(&preferred_format) {
88            preferred_format
89        } else {
90            let fallback = supported_formats[0];
91
92            error!(
93                "Unsupported preferred surface format: {:?}. Using first \
94                 supported format: {:?}",
95                preferred_format, fallback
96            );
97
98            fallback
99        };
100
101    #[cfg(not(target_arch = "wasm32"))]
102    let surface_usage =
103        wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC;
104    #[cfg(target_arch = "wasm32")]
105    let surface_usage = wgpu::TextureUsages::RENDER_ATTACHMENT;
106
107    let desired_present_mode =
108        match std::env::var("COMFY_VSYNC_OVERRIDE").as_deref() {
109            Ok("0") | Ok("f") | Ok("false") => {
110                info!("VSYNC OVERRIDE via env var, set to VSYNC=off");
111                wgpu::PresentMode::Immediate
112            }
113            Ok("1") | Ok("t") | Ok("true") => {
114                info!("VSYNC OVERRIDE via env var, set to VSYNC=on");
115                wgpu::PresentMode::AutoVsync
116            }
117            _ => {
118                if game_config().vsync_enabled {
119                    wgpu::PresentMode::AutoVsync
120                } else {
121                    wgpu::PresentMode::Immediate
122                }
123            }
124        };
125
126    let present_mode = if caps.present_modes.contains(&desired_present_mode) {
127        desired_present_mode
128    } else {
129        caps.present_modes[0]
130    };
131
132    info!("ACTUAL PRESENT MODE: {:?}", present_mode);
133
134    let config = wgpu::SurfaceConfiguration {
135        usage: surface_usage,
136        format: monitor_surface_format,
137        width: size.width,
138        height: size.height,
139        present_mode,
140        alpha_mode: caps.alpha_modes[0],
141        view_formats: vec![],
142        desired_maximum_frame_latency: game_config()
143            .desired_maximum_frame_latency,
144    };
145
146    trace!("Configuring surface");
147
148    surface.configure(&device, &config);
149
150    let texture_bind_group_layout =
151        device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
152            entries: &[
153                wgpu::BindGroupLayoutEntry {
154                    binding: 0,
155                    visibility: wgpu::ShaderStages::FRAGMENT,
156                    ty: wgpu::BindingType::Texture {
157                        multisampled: false,
158                        view_dimension: wgpu::TextureViewDimension::D2,
159                        sample_type: wgpu::TextureSampleType::Float {
160                            filterable: true,
161                        },
162                    },
163                    count: None,
164                },
165                wgpu::BindGroupLayoutEntry {
166                    binding: 1,
167                    visibility: wgpu::ShaderStages::FRAGMENT,
168                    ty: wgpu::BindingType::Sampler(
169                        wgpu::SamplerBindingType::Filtering,
170                    ),
171                    count: None,
172                },
173            ],
174            label: Some("texture_bind_group_layout"),
175        });
176
177    let textures = Arc::new(Mutex::new(HashMap::new()));
178
179    let device = Arc::new(device);
180    let queue = Arc::new(queue);
181    let texture_layout = Arc::new(texture_bind_group_layout);
182
183    let texture_creator = Arc::new(AtomicRefCell::new(WgpuTextureCreator {
184        textures: textures.clone(),
185        layout: texture_layout.clone(),
186        queue: queue.clone(),
187        device: device.clone(),
188    }));
189
190    GraphicsContext {
191        surface: Arc::new(surface),
192        instance: Arc::new(instance),
193        adapter: Arc::new(adapter),
194        device,
195        queue,
196        texture_layout,
197        config: Arc::new(AtomicRefCell::new(config)),
198        texture_creator,
199        textures,
200    }
201}