@bornengine/engine 0.4.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -0
  3. package/native/android/Cargo.lock +1848 -0
  4. package/native/android/Cargo.toml +24 -0
  5. package/native/android/src/lib.rs +702 -0
  6. package/native/ios/Cargo.lock +1690 -0
  7. package/native/ios/Cargo.toml +32 -0
  8. package/native/ios/src/lib.rs +1267 -0
  9. package/native/linux/Cargo.lock +3279 -0
  10. package/native/linux/Cargo.toml +29 -0
  11. package/native/linux/src/lib.rs +1331 -0
  12. package/native/macos/Cargo.lock +3310 -0
  13. package/native/macos/Cargo.toml +46 -0
  14. package/native/macos/src/lib.rs +1302 -0
  15. package/native/shared/Cargo.lock +1899 -0
  16. package/native/shared/Cargo.toml +62 -0
  17. package/native/shared/assets/default_font.ttf +0 -0
  18. package/native/shared/build.rs +270 -0
  19. package/native/shared/shaders/common/clouds.wgsl +122 -0
  20. package/native/shared/shaders/common/fog.wgsl +16 -0
  21. package/native/shared/shaders/common/foliage_wind.wgsl +98 -0
  22. package/native/shared/shaders/common/imposter.wgsl +112 -0
  23. package/native/shared/shaders/common/pbr.wgsl +186 -0
  24. package/native/shared/shaders/common/shadows.wgsl +186 -0
  25. package/native/shared/shaders/common/sky.wgsl +8 -0
  26. package/native/shared/shaders/common/tonemap.wgsl +25 -0
  27. package/native/shared/shaders/impulse_field.wgsl +57 -0
  28. package/native/shared/shaders/material_abi.wgsl +383 -0
  29. package/native/shared/shaders/materials/test_minimal.wgsl +42 -0
  30. package/native/shared/src/anim_mixer.rs +61 -0
  31. package/native/shared/src/attach.rs +263 -0
  32. package/native/shared/src/audio/decode.rs +123 -0
  33. package/native/shared/src/audio/mod.rs +863 -0
  34. package/native/shared/src/audio/render.rs +892 -0
  35. package/native/shared/src/audio/spsc.rs +156 -0
  36. package/native/shared/src/audio/stream.rs +226 -0
  37. package/native/shared/src/custom_shaders.rs +104 -0
  38. package/native/shared/src/decals.rs +245 -0
  39. package/native/shared/src/drs.rs +211 -0
  40. package/native/shared/src/engine.rs +261 -0
  41. package/native/shared/src/ffi.rs +116 -0
  42. package/native/shared/src/ffi_core/assets.rs +388 -0
  43. package/native/shared/src/ffi_core/audio_ffi.rs +184 -0
  44. package/native/shared/src/ffi_core/draw.rs +334 -0
  45. package/native/shared/src/ffi_core/game_loop.rs +577 -0
  46. package/native/shared/src/ffi_core/input.rs +234 -0
  47. package/native/shared/src/ffi_core/mod.rs +127 -0
  48. package/native/shared/src/ffi_core/models.rs +1154 -0
  49. package/native/shared/src/ffi_core/ragdoll_ffi.rs +261 -0
  50. package/native/shared/src/ffi_core/scene.rs +626 -0
  51. package/native/shared/src/ffi_core/vfx.rs +212 -0
  52. package/native/shared/src/ffi_core/visual.rs +691 -0
  53. package/native/shared/src/frame_callbacks.rs +122 -0
  54. package/native/shared/src/geometry.rs +236 -0
  55. package/native/shared/src/handles.rs +182 -0
  56. package/native/shared/src/input.rs +448 -0
  57. package/native/shared/src/jolt_sys.rs +822 -0
  58. package/native/shared/src/lib.rs +55 -0
  59. package/native/shared/src/models.rs +1093 -0
  60. package/native/shared/src/models_gltf.rs +1280 -0
  61. package/native/shared/src/particles.rs +391 -0
  62. package/native/shared/src/physics_jolt.rs +1908 -0
  63. package/native/shared/src/picking.rs +298 -0
  64. package/native/shared/src/postfx.rs +345 -0
  65. package/native/shared/src/profiler.rs +492 -0
  66. package/native/shared/src/ragdoll.rs +474 -0
  67. package/native/shared/src/renderer/atmosphere_lut.rs +573 -0
  68. package/native/shared/src/renderer/brdf_lut.rs +154 -0
  69. package/native/shared/src/renderer/draw2d.rs +143 -0
  70. package/native/shared/src/renderer/formats.rs +822 -0
  71. package/native/shared/src/renderer/froxel.rs +421 -0
  72. package/native/shared/src/renderer/gi_bake.rs +653 -0
  73. package/native/shared/src/renderer/graph.rs +462 -0
  74. package/native/shared/src/renderer/hiz.rs +269 -0
  75. package/native/shared/src/renderer/hot_reload.rs +390 -0
  76. package/native/shared/src/renderer/impulse_field.rs +456 -0
  77. package/native/shared/src/renderer/lighting.rs +154 -0
  78. package/native/shared/src/renderer/material_instancing.rs +171 -0
  79. package/native/shared/src/renderer/material_pipeline.rs +700 -0
  80. package/native/shared/src/renderer/material_system.rs +1996 -0
  81. package/native/shared/src/renderer/material_system_tests.rs +601 -0
  82. package/native/shared/src/renderer/material_system_wasm.rs +41 -0
  83. package/native/shared/src/renderer/mod.rs +12556 -0
  84. package/native/shared/src/renderer/model_draw.rs +641 -0
  85. package/native/shared/src/renderer/occlusion.rs +429 -0
  86. package/native/shared/src/renderer/planar_pass.rs +593 -0
  87. package/native/shared/src/renderer/planar_reflection.rs +499 -0
  88. package/native/shared/src/renderer/post_pass.rs +249 -0
  89. package/native/shared/src/renderer/postfx_chain.rs +728 -0
  90. package/native/shared/src/renderer/pt_pass.rs +577 -0
  91. package/native/shared/src/renderer/scene_pass.rs +607 -0
  92. package/native/shared/src/renderer/shader_include.rs +205 -0
  93. package/native/shared/src/renderer/shader_library.rs +135 -0
  94. package/native/shared/src/renderer/shaders/ao.rs +570 -0
  95. package/native/shared/src/renderer/shaders/core.rs +1243 -0
  96. package/native/shared/src/renderer/shaders/env.rs +907 -0
  97. package/native/shared/src/renderer/shaders/gi.rs +810 -0
  98. package/native/shared/src/renderer/shaders/mod.rs +19 -0
  99. package/native/shared/src/renderer/shaders/post.rs +1558 -0
  100. package/native/shared/src/renderer/shaders/pt.rs +1859 -0
  101. package/native/shared/src/renderer/shaders/ssgi.rs +1586 -0
  102. package/native/shared/src/renderer/shadow_pass.rs +731 -0
  103. package/native/shared/src/renderer/ssgi_pass.rs +392 -0
  104. package/native/shared/src/renderer/ssr_pass.rs +188 -0
  105. package/native/shared/src/renderer/texture_store.rs +473 -0
  106. package/native/shared/src/renderer/transient.rs +591 -0
  107. package/native/shared/src/renderer/types.rs +941 -0
  108. package/native/shared/src/renderer/util.rs +152 -0
  109. package/native/shared/src/scene.rs +1362 -0
  110. package/native/shared/src/sdf_cache.rs +274 -0
  111. package/native/shared/src/shadows.rs +1036 -0
  112. package/native/shared/src/staging.rs +102 -0
  113. package/native/shared/src/string_header.rs +266 -0
  114. package/native/shared/src/text_renderer.rs +502 -0
  115. package/native/shared/src/textures.rs +197 -0
  116. package/native/tvos/Cargo.lock +1693 -0
  117. package/native/tvos/Cargo.toml +36 -0
  118. package/native/tvos/metal-patched/Cargo.toml +178 -0
  119. package/native/tvos/metal-patched/LICENSE-APACHE +201 -0
  120. package/native/tvos/metal-patched/LICENSE-MIT +25 -0
  121. package/native/tvos/metal-patched/src/acceleration_structure.rs +667 -0
  122. package/native/tvos/metal-patched/src/acceleration_structure_pass.rs +108 -0
  123. package/native/tvos/metal-patched/src/argument.rs +366 -0
  124. package/native/tvos/metal-patched/src/blitpass.rs +102 -0
  125. package/native/tvos/metal-patched/src/buffer.rs +71 -0
  126. package/native/tvos/metal-patched/src/capturedescriptor.rs +76 -0
  127. package/native/tvos/metal-patched/src/capturemanager.rs +113 -0
  128. package/native/tvos/metal-patched/src/commandbuffer.rs +192 -0
  129. package/native/tvos/metal-patched/src/commandqueue.rs +44 -0
  130. package/native/tvos/metal-patched/src/computepass.rs +107 -0
  131. package/native/tvos/metal-patched/src/constants.rs +152 -0
  132. package/native/tvos/metal-patched/src/counters.rs +119 -0
  133. package/native/tvos/metal-patched/src/depthstencil.rs +190 -0
  134. package/native/tvos/metal-patched/src/device.rs +2134 -0
  135. package/native/tvos/metal-patched/src/drawable.rs +39 -0
  136. package/native/tvos/metal-patched/src/encoder.rs +2041 -0
  137. package/native/tvos/metal-patched/src/heap.rs +281 -0
  138. package/native/tvos/metal-patched/src/indirect_encoder.rs +344 -0
  139. package/native/tvos/metal-patched/src/lib.rs +657 -0
  140. package/native/tvos/metal-patched/src/library.rs +902 -0
  141. package/native/tvos/metal-patched/src/mps.rs +575 -0
  142. package/native/tvos/metal-patched/src/pipeline/compute.rs +475 -0
  143. package/native/tvos/metal-patched/src/pipeline/mod.rs +71 -0
  144. package/native/tvos/metal-patched/src/pipeline/render.rs +762 -0
  145. package/native/tvos/metal-patched/src/renderpass.rs +443 -0
  146. package/native/tvos/metal-patched/src/resource.rs +182 -0
  147. package/native/tvos/metal-patched/src/sampler.rs +165 -0
  148. package/native/tvos/metal-patched/src/sync.rs +178 -0
  149. package/native/tvos/metal-patched/src/texture.rs +352 -0
  150. package/native/tvos/metal-patched/src/types.rs +90 -0
  151. package/native/tvos/metal-patched/src/vertexdescriptor.rs +250 -0
  152. package/native/tvos/src/audio_backend.rs +197 -0
  153. package/native/tvos/src/lib.rs +1891 -0
  154. package/native/visionos/Cargo.lock +1693 -0
  155. package/native/visionos/Cargo.toml +40 -0
  156. package/native/visionos/src/audio_backend.rs +197 -0
  157. package/native/visionos/src/lib.rs +1887 -0
  158. package/native/watchos/Cargo.lock +16 -0
  159. package/native/watchos/Cargo.toml +19 -0
  160. package/native/watchos/shaders/bloom_postfx.metal +99 -0
  161. package/native/watchos/src/BloomWatchApp.swift +1267 -0
  162. package/native/watchos/src/BloomWatchAudio.swift +179 -0
  163. package/native/watchos/src/audio.rs +55 -0
  164. package/native/watchos/src/draw_list.rs +229 -0
  165. package/native/watchos/src/ffi_stubs.rs +915 -0
  166. package/native/watchos/src/ffi_stubs_manual.rs +35 -0
  167. package/native/watchos/src/lib.rs +1124 -0
  168. package/native/watchos/src/models.rs +746 -0
  169. package/native/watchos/src/postfx.rs +95 -0
  170. package/native/watchos/src/scene.rs +534 -0
  171. package/native/watchos/src/textures.rs +184 -0
  172. package/native/web/Cargo.lock +1657 -0
  173. package/native/web/Cargo.toml +43 -0
  174. package/native/web/bloom_glue.js +695 -0
  175. package/native/web/build.sh +131 -0
  176. package/native/web/index.html +35 -0
  177. package/native/web/jolt_bridge.js +1519 -0
  178. package/native/web/src/input_ffi.rs +286 -0
  179. package/native/web/src/lib.rs +1796 -0
  180. package/native/web/src/material_ffi.rs +710 -0
  181. package/native/web/src/parity_ffi.rs +343 -0
  182. package/native/web/src/physics_ffi.rs +643 -0
  183. package/native/web/src/ragdoll_ffi.rs +250 -0
  184. package/native/web/src/render_settings.rs +98 -0
  185. package/native/windows/Cargo.lock +1815 -0
  186. package/native/windows/Cargo.toml +68 -0
  187. package/native/windows/src/lib.rs +1486 -0
  188. package/package.json +4279 -0
  189. package/src/audio/index.ts +315 -0
  190. package/src/core/colors.ts +63 -0
  191. package/src/core/index.ts +1206 -0
  192. package/src/core/keys.ts +63 -0
  193. package/src/core/types.ts +104 -0
  194. package/src/index.ts +171 -0
  195. package/src/math/index.ts +516 -0
  196. package/src/mobile/index.ts +294 -0
  197. package/src/models/index.ts +1258 -0
  198. package/src/physics/index.ts +1134 -0
  199. package/src/scene/index.ts +698 -0
  200. package/src/shapes/index.ts +120 -0
  201. package/src/text/index.ts +48 -0
  202. package/src/textures/index.ts +187 -0
  203. package/src/vfx/index.ts +191 -0
  204. package/src/world/index.ts +24 -0
  205. package/src/world/loader.ts +423 -0
  206. package/src/world/prefab.ts +217 -0
  207. package/src/world/render.ts +172 -0
  208. package/src/world/saver.ts +108 -0
  209. package/src/world/serialize.ts +301 -0
  210. package/src/world/terrain.ts +355 -0
  211. package/src/world/types.ts +160 -0
  212. package/src/world/validate.ts +319 -0
  213. package/src/world/version.ts +114 -0
@@ -0,0 +1,249 @@
1
+ //! EN-017 — Post-pass slot for game-supplied fullscreen WGSL FX.
2
+ //!
3
+ //! A single slot the game can install via `set_post_pass(wgsl)` and
4
+ //! remove via `clear_post_pass()`. When set, the engine's composite
5
+ //! output is redirected to an LDR intermediate render target, then
6
+ //! the user's fragment shader runs as a fullscreen draw — sampling
7
+ //! `scene_color_tex` (LDR, post-tonemap) and `scene_depth_tex` —
8
+ //! and writes the final image to the swapchain. The 2D overlay
9
+ //! still draws on top of the post-pass output, so the HUD stays
10
+ //! crisp.
11
+ //!
12
+ //! V1 ships with one slot (no stack), depth access on, and a
13
+ //! dedicated bind group layout — see EN-017 for rationale.
14
+
15
+ use wgpu;
16
+
17
+ /// Describes the format the post-pass fragment shader writes to
18
+ /// (swapchain) plus the format of the intermediate LDR RT it
19
+ /// samples. Both equal `surface_config.format` so the post-pass
20
+ /// composes the engine's normal LDR output 1:1.
21
+ pub struct PostPassPipeline {
22
+ pub pipeline: wgpu::RenderPipeline,
23
+ pub bind_group_layout: wgpu::BindGroupLayout,
24
+ }
25
+
26
+ #[derive(Debug)]
27
+ pub enum PostPassCompileError {
28
+ Naga(String),
29
+ Wgpu(String),
30
+ }
31
+
32
+ /// Auto-prepended ABI for every post-pass shader. Declares the
33
+ /// engine-provided bindings + a fullscreen-triangle vertex shader.
34
+ /// User WGSL is appended verbatim and must declare:
35
+ /// `@fragment fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32>`
36
+ pub const POST_PASS_PRELUDE: &str = r#"
37
+ // Auto-prepended by the engine for post-pass shaders (EN-017).
38
+ @group(0) @binding(0) var scene_color_tex: texture_2d<f32>;
39
+ @group(0) @binding(1) var scene_color_samp: sampler;
40
+ @group(0) @binding(2) var scene_depth_tex: texture_depth_2d;
41
+ @group(0) @binding(3) var scene_depth_samp: sampler;
42
+
43
+ struct VertexOutput {
44
+ @builtin(position) position: vec4<f32>,
45
+ @location(0) uv: vec2<f32>,
46
+ };
47
+
48
+ @vertex
49
+ fn vs_main(@builtin(vertex_index) vid: u32) -> VertexOutput {
50
+ // Standard fullscreen triangle.
51
+ var out: VertexOutput;
52
+ let x = f32((vid << 1u) & 2u) * 2.0 - 1.0;
53
+ let y = f32(vid & 2u) * 2.0 - 1.0;
54
+ out.position = vec4<f32>(x, -y, 0.0, 1.0);
55
+ out.uv = vec2<f32>((x + 1.0) * 0.5, (y + 1.0) * 0.5);
56
+ return out;
57
+ }
58
+
59
+ // User WGSL follows. Must declare `fs_main` with the signature:
60
+ // @fragment fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32>
61
+ "#;
62
+
63
+ /// Build the bind-group layout the post-pass uses. One layout per
64
+ /// pipeline (recreated on each `set_post_pass`) — cheap and avoids
65
+ /// having to thread a shared layout through the Renderer struct.
66
+ pub fn create_post_pass_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout {
67
+ device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
68
+ label: Some("post_pass_layout"),
69
+ entries: &[
70
+ // 0: scene color (LDR composite output).
71
+ wgpu::BindGroupLayoutEntry {
72
+ binding: 0,
73
+ visibility: wgpu::ShaderStages::FRAGMENT,
74
+ ty: wgpu::BindingType::Texture {
75
+ sample_type: wgpu::TextureSampleType::Float { filterable: true },
76
+ view_dimension: wgpu::TextureViewDimension::D2,
77
+ multisampled: false,
78
+ },
79
+ count: None,
80
+ },
81
+ // 1: filtering sampler for scene color.
82
+ wgpu::BindGroupLayoutEntry {
83
+ binding: 1,
84
+ visibility: wgpu::ShaderStages::FRAGMENT,
85
+ ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
86
+ count: None,
87
+ },
88
+ // 2: scene depth (Depth32Float — texture_depth_2d in WGSL).
89
+ wgpu::BindGroupLayoutEntry {
90
+ binding: 2,
91
+ visibility: wgpu::ShaderStages::FRAGMENT,
92
+ ty: wgpu::BindingType::Texture {
93
+ sample_type: wgpu::TextureSampleType::Depth,
94
+ view_dimension: wgpu::TextureViewDimension::D2,
95
+ multisampled: false,
96
+ },
97
+ count: None,
98
+ },
99
+ // 3: non-filtering sampler for scene depth.
100
+ wgpu::BindGroupLayoutEntry {
101
+ binding: 3,
102
+ visibility: wgpu::ShaderStages::FRAGMENT,
103
+ ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::NonFiltering),
104
+ count: None,
105
+ },
106
+ ],
107
+ })
108
+ }
109
+
110
+ /// Compile a post-pass pipeline from user WGSL.
111
+ ///
112
+ /// `swapchain_format` is the colour-target format the post-pass
113
+ /// writes to (the engine's surface format) — the LDR intermediate
114
+ /// the post-pass samples is allocated in the same format so the
115
+ /// user shader sees identical bits regardless of whether the
116
+ /// post-pass is active.
117
+ pub fn compile_post_pass(
118
+ device: &wgpu::Device,
119
+ user_wgsl: &str,
120
+ swapchain_format: wgpu::TextureFormat,
121
+ ) -> Result<PostPassPipeline, PostPassCompileError> {
122
+ let full_source = format!("{}\n{}", POST_PASS_PRELUDE, user_wgsl);
123
+
124
+ // wgpu 29 surfaces validation errors via the device's
125
+ // uncaptured-error handler — same pattern material_pipeline.rs
126
+ // uses. Pushing the scope keeps a syntax error from killing the
127
+ // renderer; the real error text reaches the platform's eprintln
128
+ // / console.error glue layer.
129
+ let _ = device.push_error_scope(wgpu::ErrorFilter::Validation);
130
+ let module = device.create_shader_module(wgpu::ShaderModuleDescriptor {
131
+ label: Some("post_pass_shader"),
132
+ source: wgpu::ShaderSource::Wgsl(full_source.into()),
133
+ });
134
+
135
+ let bind_group_layout = create_post_pass_layout(device);
136
+ let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
137
+ label: Some("post_pass_pl_layout"),
138
+ bind_group_layouts: &[Some(&bind_group_layout)],
139
+ immediate_size: 0,
140
+ });
141
+
142
+ let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
143
+ label: Some("post_pass_pipeline"),
144
+ layout: Some(&pipeline_layout),
145
+ vertex: wgpu::VertexState {
146
+ module: &module,
147
+ entry_point: Some("vs_main"),
148
+ buffers: &[],
149
+ compilation_options: Default::default(),
150
+ },
151
+ fragment: Some(wgpu::FragmentState {
152
+ module: &module,
153
+ entry_point: Some("fs_main"),
154
+ targets: &[Some(wgpu::ColorTargetState {
155
+ format: swapchain_format,
156
+ blend: None,
157
+ write_mask: wgpu::ColorWrites::ALL,
158
+ })],
159
+ compilation_options: Default::default(),
160
+ }),
161
+ primitive: wgpu::PrimitiveState {
162
+ topology: wgpu::PrimitiveTopology::TriangleList,
163
+ strip_index_format: None,
164
+ front_face: wgpu::FrontFace::Ccw,
165
+ cull_mode: None,
166
+ polygon_mode: wgpu::PolygonMode::Fill,
167
+ unclipped_depth: false,
168
+ conservative: false,
169
+ },
170
+ depth_stencil: None,
171
+ multisample: wgpu::MultisampleState::default(),
172
+ multiview_mask: None,
173
+ cache: None,
174
+ });
175
+
176
+ Ok(PostPassPipeline { pipeline, bind_group_layout })
177
+ }
178
+
179
+ /// Allocate the LDR intermediate render target the composite pass
180
+ /// writes into when a post-pass is installed. Format mirrors the
181
+ /// swapchain so the user shader sees identical bits to a normal
182
+ /// composite output.
183
+ pub fn create_composite_ldr_rt(
184
+ device: &wgpu::Device,
185
+ width: u32,
186
+ height: u32,
187
+ format: wgpu::TextureFormat,
188
+ ) -> (wgpu::Texture, wgpu::TextureView) {
189
+ let texture = device.create_texture(&wgpu::TextureDescriptor {
190
+ label: Some("composite_ldr_rt"),
191
+ size: wgpu::Extent3d { width: width.max(1), height: height.max(1), depth_or_array_layers: 1 },
192
+ mip_level_count: 1,
193
+ sample_count: 1,
194
+ dimension: wgpu::TextureDimension::D2,
195
+ format,
196
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT
197
+ | wgpu::TextureUsages::TEXTURE_BINDING,
198
+ view_formats: &[],
199
+ });
200
+ let view = texture.create_view(&wgpu::TextureViewDescriptor::default());
201
+ (texture, view)
202
+ }
203
+
204
+ #[cfg(test)]
205
+ mod tests {
206
+ use super::*;
207
+
208
+ /// Headless wgpu device for tests. Mirrors the pattern in
209
+ /// `transient.rs` so tests run on machines without a real GPU.
210
+ fn try_create_device() -> Option<(wgpu::Device, wgpu::Queue)> {
211
+ let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
212
+ backends: wgpu::Backends::all(),
213
+ ..wgpu::InstanceDescriptor::new_without_display_handle()
214
+ });
215
+ let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
216
+ power_preference: wgpu::PowerPreference::LowPower,
217
+ compatible_surface: None,
218
+ force_fallback_adapter: true,
219
+ })).ok()?;
220
+ let (device, queue) = pollster::block_on(adapter.request_device(
221
+ &wgpu::DeviceDescriptor {
222
+ label: Some("post-pass-test-device"),
223
+ required_features: wgpu::Features::empty(),
224
+ required_limits: wgpu::Limits::downlevel_defaults(),
225
+ ..Default::default()
226
+ },
227
+ )).ok()?;
228
+ Some((device, queue))
229
+ }
230
+
231
+ /// EN-017 — the underwater-tint example from the API docs must
232
+ /// compile against the post-pass prelude. Skipped on adapters
233
+ /// where `try_create_device` returns None (CI without a GPU).
234
+ #[test]
235
+ fn underwater_tint_compiles() {
236
+ let Some((device, _queue)) = try_create_device() else { return; };
237
+ let wgsl = r#"
238
+ @fragment
239
+ fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
240
+ let scene = textureSample(scene_color_tex, scene_color_samp, uv);
241
+ return vec4<f32>(scene.rgb * vec3<f32>(0.4, 0.7, 0.9), 1.0);
242
+ }
243
+ "#;
244
+ let result = compile_post_pass(
245
+ &device, wgsl, wgpu::TextureFormat::Bgra8UnormSrgb,
246
+ );
247
+ assert!(result.is_ok(), "underwater tint should compile: {:?}", result.err());
248
+ }
249
+ }