Skip to content

Commit 302f28c

Browse files
committed
[add] high level instance type to use for configuring render targets and building the GPU.
1 parent c60c667 commit 302f28c

File tree

4 files changed

+376
-10
lines changed

4 files changed

+376
-10
lines changed

crates/lambda-rs/src/render/gpu.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
2424
use lambda_platform::wgpu as platform;
2525

26-
use super::texture::{
27-
DepthFormat,
28-
TextureFormat,
26+
use super::{
27+
instance::Instance,
28+
texture::{
29+
DepthFormat,
30+
TextureFormat,
31+
},
2932
};
3033

3134
// ---------------------------------------------------------------------------
@@ -222,12 +225,12 @@ impl GpuBuilder {
222225
/// presentation. Pass `None` for headless/compute-only contexts.
223226
pub fn build(
224227
self,
225-
instance: &platform::instance::Instance,
228+
instance: &Instance,
226229
surface: Option<&platform::surface::Surface<'_>>,
227230
) -> Result<Gpu, GpuBuildError> {
228231
let platform_gpu = self
229232
.inner
230-
.build(instance, surface)
233+
.build(instance.platform(), surface)
231234
.map_err(GpuBuildError::from_platform)?;
232235
return Ok(Gpu::from_platform(platform_gpu));
233236
}
Lines changed: 361 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,361 @@
1+
//! High-level graphics instance abstraction.
2+
//!
3+
//! The `Instance` type wraps the platform instance, providing a stable
4+
//! engine-facing API for instance creation and configuration.
5+
//!
6+
//! # Usage
7+
//!
8+
//! Create an instance using the builder pattern:
9+
//!
10+
//! ```ignore
11+
//! let instance = InstanceBuilder::new()
12+
//! .with_label("My Application")
13+
//! .with_backends(Backends::PRIMARY)
14+
//! .build();
15+
//! ```
16+
//!
17+
//! The instance is then used to create surfaces and GPUs:
18+
//!
19+
//! ```ignore
20+
//! let surface = WindowSurface::new(&instance, &window)?;
21+
//! let gpu = GpuBuilder::new().build(&instance, Some(&surface))?;
22+
//! ```
23+
24+
use lambda_platform::wgpu as platform;
25+
26+
// ---------------------------------------------------------------------------
27+
// Backends
28+
// ---------------------------------------------------------------------------
29+
30+
/// Graphics API backends available for rendering.
31+
///
32+
/// This type mirrors the platform `Backends` bitset, exposing the same
33+
/// options without leaking `wgpu` types to the engine layer.
34+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
35+
pub struct Backends(platform::instance::Backends);
36+
37+
impl Backends {
38+
/// Primary desktop backends (Vulkan/Metal/DX12).
39+
///
40+
/// This is the recommended default for cross-platform desktop applications.
41+
pub const PRIMARY: Backends = Backends(platform::instance::Backends::PRIMARY);
42+
43+
/// Vulkan backend (Linux, Windows, Android).
44+
pub const VULKAN: Backends = Backends(platform::instance::Backends::VULKAN);
45+
46+
/// Metal backend (macOS, iOS).
47+
pub const METAL: Backends = Backends(platform::instance::Backends::METAL);
48+
49+
/// DirectX 12 backend (Windows).
50+
pub const DX12: Backends = Backends(platform::instance::Backends::DX12);
51+
52+
/// OpenGL / WebGL backend.
53+
pub const GL: Backends = Backends(platform::instance::Backends::GL);
54+
55+
/// Browser WebGPU backend.
56+
pub const BROWSER_WEBGPU: Backends =
57+
Backends(platform::instance::Backends::BROWSER_WEBGPU);
58+
59+
/// Convert to the platform representation for internal use.
60+
#[inline]
61+
pub(crate) fn to_platform(self) -> platform::instance::Backends {
62+
return self.0;
63+
}
64+
}
65+
66+
impl Default for Backends {
67+
fn default() -> Self {
68+
return Backends::PRIMARY;
69+
}
70+
}
71+
72+
impl std::ops::BitOr for Backends {
73+
type Output = Backends;
74+
75+
fn bitor(self, rhs: Backends) -> Backends {
76+
return Backends(self.0 | rhs.0);
77+
}
78+
}
79+
80+
// ---------------------------------------------------------------------------
81+
// InstanceFlags
82+
// ---------------------------------------------------------------------------
83+
84+
/// Configuration flags for instance creation.
85+
///
86+
/// These flags control debugging and validation behavior at the instance
87+
/// level.
88+
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
89+
pub struct InstanceFlags(platform::instance::InstanceFlags);
90+
91+
impl InstanceFlags {
92+
/// Enable validation layers for debugging.
93+
///
94+
/// This enables GPU validation which can help catch errors but has
95+
/// performance overhead. Recommended for development builds.
96+
pub const VALIDATION: InstanceFlags =
97+
InstanceFlags(platform::instance::InstanceFlags::VALIDATION);
98+
99+
/// Enable additional debugging features.
100+
///
101+
/// This enables extra debugging information where supported by the
102+
/// graphics backend.
103+
pub const DEBUG: InstanceFlags =
104+
InstanceFlags(platform::instance::InstanceFlags::DEBUG);
105+
106+
/// Convert to the platform representation for internal use.
107+
#[inline]
108+
pub(crate) fn to_platform(self) -> platform::instance::InstanceFlags {
109+
return self.0;
110+
}
111+
}
112+
113+
impl Default for InstanceFlags {
114+
fn default() -> Self {
115+
return InstanceFlags(platform::instance::InstanceFlags::default());
116+
}
117+
}
118+
119+
impl std::ops::BitOr for InstanceFlags {
120+
type Output = InstanceFlags;
121+
122+
fn bitor(self, rhs: InstanceFlags) -> InstanceFlags {
123+
return InstanceFlags(self.0 | rhs.0);
124+
}
125+
}
126+
127+
// ---------------------------------------------------------------------------
128+
// Dx12Compiler
129+
// ---------------------------------------------------------------------------
130+
131+
/// DirectX 12 shader compiler selection (Windows only).
132+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
133+
pub enum Dx12Compiler {
134+
/// Use the FXC compiler (legacy, broadly compatible).
135+
#[default]
136+
Fxc,
137+
}
138+
139+
impl Dx12Compiler {
140+
/// Convert to the platform representation for internal use.
141+
#[inline]
142+
pub(crate) fn to_platform(self) -> platform::instance::Dx12Compiler {
143+
return match self {
144+
Dx12Compiler::Fxc => platform::instance::Dx12Compiler::Fxc,
145+
};
146+
}
147+
}
148+
149+
// ---------------------------------------------------------------------------
150+
// Gles3MinorVersion
151+
// ---------------------------------------------------------------------------
152+
153+
/// OpenGL ES 3.x minor version selection.
154+
///
155+
/// Used for WebGL and OpenGL ES targets to specify the required minor
156+
/// version of the OpenGL ES 3.x API.
157+
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
158+
pub enum Gles3MinorVersion {
159+
/// Let the platform select an appropriate version.
160+
#[default]
161+
Automatic,
162+
/// OpenGL ES 3.0
163+
Version0,
164+
/// OpenGL ES 3.1
165+
Version1,
166+
/// OpenGL ES 3.2
167+
Version2,
168+
}
169+
170+
impl Gles3MinorVersion {
171+
/// Convert to the platform representation for internal use.
172+
#[inline]
173+
pub(crate) fn to_platform(self) -> platform::instance::Gles3MinorVersion {
174+
return match self {
175+
Gles3MinorVersion::Automatic => {
176+
platform::instance::Gles3MinorVersion::Automatic
177+
}
178+
Gles3MinorVersion::Version0 => {
179+
platform::instance::Gles3MinorVersion::Version0
180+
}
181+
Gles3MinorVersion::Version1 => {
182+
platform::instance::Gles3MinorVersion::Version1
183+
}
184+
Gles3MinorVersion::Version2 => {
185+
platform::instance::Gles3MinorVersion::Version2
186+
}
187+
};
188+
}
189+
}
190+
191+
// ---------------------------------------------------------------------------
192+
// Instance
193+
// ---------------------------------------------------------------------------
194+
195+
/// High-level graphics instance.
196+
///
197+
/// The instance is the root object for the graphics subsystem. It manages
198+
/// the connection to the graphics backend and is used to create surfaces
199+
/// and enumerate adapters.
200+
///
201+
/// Create an instance using `InstanceBuilder`:
202+
///
203+
/// ```ignore
204+
/// let instance = InstanceBuilder::new()
205+
/// .with_label("My Application")
206+
/// .build();
207+
/// ```
208+
pub struct Instance {
209+
inner: platform::instance::Instance,
210+
}
211+
212+
impl Instance {
213+
/// Return the optional label attached at construction time.
214+
#[inline]
215+
pub fn label(&self) -> Option<&str> {
216+
return self.inner.label();
217+
}
218+
219+
/// Borrow the underlying platform instance for internal use.
220+
///
221+
/// This is crate-visible to allow surfaces and GPUs to access the
222+
/// platform instance without exposing it publicly.
223+
#[inline]
224+
pub(crate) fn platform(&self) -> &platform::instance::Instance {
225+
return &self.inner;
226+
}
227+
}
228+
229+
impl std::fmt::Debug for Instance {
230+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
231+
return f
232+
.debug_struct("Instance")
233+
.field("label", &self.inner.label())
234+
.finish_non_exhaustive();
235+
}
236+
}
237+
238+
// ---------------------------------------------------------------------------
239+
// InstanceBuilder
240+
// ---------------------------------------------------------------------------
241+
242+
/// Builder for creating a graphics `Instance` with configurable options.
243+
///
244+
/// The builder provides a fluent interface for configuring instance
245+
/// creation parameters. All options have sensible defaults for desktop
246+
/// applications.
247+
///
248+
/// # Defaults
249+
///
250+
/// - **Backends**: `Backends::PRIMARY` (Vulkan/Metal/DX12)
251+
/// - **Flags**: Platform defaults (no validation)
252+
///
253+
/// # Example
254+
///
255+
/// ```ignore
256+
/// let instance = InstanceBuilder::new()
257+
/// .with_label("My Application Instance")
258+
/// .with_backends(Backends::PRIMARY)
259+
/// .with_flags(InstanceFlags::VALIDATION)
260+
/// .build();
261+
/// ```
262+
pub struct InstanceBuilder {
263+
inner: platform::instance::InstanceBuilder,
264+
}
265+
266+
impl InstanceBuilder {
267+
/// Create a new builder with default settings.
268+
pub fn new() -> Self {
269+
return InstanceBuilder {
270+
inner: platform::instance::InstanceBuilder::new(),
271+
};
272+
}
273+
274+
/// Attach a debug label to the instance.
275+
///
276+
/// Labels appear in debug output and profiling tools, making it easier
277+
/// to identify resources during development.
278+
pub fn with_label(mut self, label: &str) -> Self {
279+
self.inner = self.inner.with_label(label);
280+
return self;
281+
}
282+
283+
/// Select which graphics backends to enable.
284+
///
285+
/// Multiple backends can be combined using the `|` operator. The runtime
286+
/// will select the best available backend from the enabled set.
287+
///
288+
/// # Example
289+
///
290+
/// ```ignore
291+
/// // Enable Vulkan and Metal
292+
/// builder.with_backends(Backends::VULKAN | Backends::METAL)
293+
/// ```
294+
pub fn with_backends(mut self, backends: Backends) -> Self {
295+
self.inner = self.inner.with_backends(backends.to_platform());
296+
return self;
297+
}
298+
299+
/// Set instance flags for debugging and validation.
300+
///
301+
/// Validation is recommended during development but has performance
302+
/// overhead and should be disabled in release builds.
303+
pub fn with_flags(mut self, flags: InstanceFlags) -> Self {
304+
self.inner = self.inner.with_flags(flags.to_platform());
305+
return self;
306+
}
307+
308+
/// Choose a DX12 shader compiler variant (Windows only).
309+
///
310+
/// This option only affects DirectX 12 backends on Windows.
311+
pub fn with_dx12_shader_compiler(mut self, compiler: Dx12Compiler) -> Self {
312+
self.inner = self.inner.with_dx12_shader_compiler(compiler.to_platform());
313+
return self;
314+
}
315+
316+
/// Configure the GLES minor version for WebGL/OpenGL ES targets.
317+
///
318+
/// This option only affects OpenGL and WebGL backends.
319+
pub fn with_gles_minor_version(mut self, version: Gles3MinorVersion) -> Self {
320+
self.inner = self.inner.with_gles_minor_version(version.to_platform());
321+
return self;
322+
}
323+
324+
/// Build the `Instance` from the accumulated options.
325+
pub fn build(self) -> Instance {
326+
return Instance {
327+
inner: self.inner.build(),
328+
};
329+
}
330+
}
331+
332+
impl Default for InstanceBuilder {
333+
fn default() -> Self {
334+
return Self::new();
335+
}
336+
}
337+
338+
#[cfg(test)]
339+
mod tests {
340+
use super::*;
341+
342+
#[test]
343+
fn instance_builder_sets_label() {
344+
let instance = InstanceBuilder::new().with_label("Test Instance").build();
345+
assert_eq!(instance.label(), Some("Test Instance"));
346+
}
347+
348+
#[test]
349+
fn instance_builder_default_backends() {
350+
// Just ensure we can build with defaults without panicking
351+
let _instance = InstanceBuilder::new().build();
352+
}
353+
354+
#[test]
355+
fn backends_bitor() {
356+
let combined = Backends::VULKAN | Backends::METAL;
357+
// Verify the operation doesn't panic and produces a valid result
358+
assert_ne!(combined, Backends::VULKAN);
359+
assert_ne!(combined, Backends::METAL);
360+
}
361+
}

0 commit comments

Comments
 (0)