Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_pbr/src/ssr/mod.rs
9413 views
1
//! Screen space reflections implemented via raymarching.
2
3
use core::ops::Range;
4
5
use bevy_app::{App, Plugin};
6
use bevy_asset::{load_embedded_asset, AssetServer, Handle};
7
use bevy_core_pipeline::{
8
core_3d::{main_opaque_pass_3d, DEPTH_TEXTURE_SAMPLING_SUPPORTED},
9
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
10
schedule::{Core3d, Core3dSystems},
11
FullscreenShader,
12
};
13
use bevy_derive::{Deref, DerefMut};
14
use bevy_ecs::{
15
component::Component,
16
entity::Entity,
17
query::{Has, QueryItem, With},
18
reflect::ReflectComponent,
19
resource::Resource,
20
schedule::IntoScheduleConfigs as _,
21
system::{lifetimeless::Read, Commands, Query, Res, ResMut},
22
};
23
use bevy_image::BevyDefault as _;
24
use bevy_light::EnvironmentMapLight;
25
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
26
use bevy_render::{
27
diagnostic::RecordDiagnostics,
28
extract_component::{ExtractComponent, ExtractComponentPlugin},
29
render_asset::RenderAssets,
30
render_resource::{
31
binding_types, AddressMode, BindGroupEntries, BindGroupLayoutDescriptor,
32
BindGroupLayoutEntries, CachedRenderPipelineId, ColorTargetState, ColorWrites,
33
DynamicUniformBuffer, FilterMode, FragmentState, Operations, PipelineCache,
34
RenderPassColorAttachment, RenderPassDescriptor, RenderPipelineDescriptor, Sampler,
35
SamplerBindingType, SamplerDescriptor, ShaderStages, ShaderType, SpecializedRenderPipeline,
36
SpecializedRenderPipelines, TextureFormat, TextureSampleType, TextureViewDescriptor,
37
TextureViewDimension,
38
},
39
renderer::{RenderAdapter, RenderContext, RenderDevice, RenderQueue, ViewQuery},
40
sync_component::SyncComponent,
41
texture::GpuImage,
42
view::{ExtractedView, Msaa, ViewTarget, ViewUniformOffset},
43
Render, RenderApp, RenderStartup, RenderSystems,
44
};
45
use bevy_shader::{load_shader_library, Shader};
46
use bevy_utils::{once, prelude::default};
47
use tracing::info;
48
49
use crate::{
50
binding_arrays_are_usable, contact_shadows::ViewContactShadowsUniformOffset,
51
deferred::deferred_lighting, Bluenoise, ExtractedAtmosphere, MeshPipelineViewLayoutKey,
52
MeshPipelineViewLayouts, MeshViewBindGroup, RenderViewLightProbes,
53
ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset,
54
ViewLightsUniformOffset,
55
};
56
57
/// Enables screen-space reflections for a camera.
58
///
59
/// Screen-space reflections are currently only supported with deferred rendering.
60
pub struct ScreenSpaceReflectionsPlugin;
61
62
/// Add this component to a camera to enable *screen-space reflections* (SSR).
63
///
64
/// Screen-space reflections currently require deferred rendering in order to
65
/// appear. Therefore, they also need the [`DepthPrepass`] and [`DeferredPrepass`]
66
/// components, which are inserted automatically,
67
/// but deferred rendering itself is not automatically enabled.
68
///
69
/// Enable the `bluenoise_texture` feature to improve the quality of noise on rough reflections.
70
///
71
/// As with all screen-space techniques, SSR can only reflect objects on screen.
72
/// When objects leave the camera, they will disappear from reflections.
73
/// An alternative that doesn't suffer from this problem is the combination of
74
/// a [`LightProbe`](bevy_light::LightProbe) and [`EnvironmentMapLight`]. The advantage of SSR is
75
/// that it can reflect all objects, not just static ones.
76
///
77
/// SSR is an approximation technique and produces artifacts in some situations.
78
/// Hand-tuning the settings in this component will likely be useful.
79
///
80
/// Screen-space reflections are presently unsupported on WebGL 2 because of a
81
/// bug whereby Naga doesn't generate correct GLSL when sampling depth buffers,
82
/// which is required for screen-space raymarching.
83
#[derive(Clone, Component, Reflect)]
84
#[reflect(Component, Default, Clone)]
85
#[require(DepthPrepass, DeferredPrepass)]
86
#[doc(alias = "Ssr")]
87
pub struct ScreenSpaceReflections {
88
/// The perceptual roughness range over which SSR begins to fade in.
89
///
90
/// The first value is the roughness at which SSR begins to appear; the
91
/// second value is the roughness at which SSR is fully active.
92
pub min_perceptual_roughness: Range<f32>,
93
94
/// The perceptual roughness range over which SSR begins to fade out.
95
///
96
/// The first value is the roughness at which SSR begins to fade out; the
97
/// second value is the roughness at which SSR is no longer active.
98
pub max_perceptual_roughness: Range<f32>,
99
100
/// When marching the depth buffer, we only have 2.5D information and don't
101
/// know how thick surfaces are. We shall assume that the depth buffer
102
/// fragments are cuboids with a constant thickness defined by this
103
/// parameter.
104
pub thickness: f32,
105
106
/// The number of steps to be taken at regular intervals to find an initial
107
/// intersection. Must not be zero.
108
///
109
/// Higher values result in higher-quality reflections, because the
110
/// raymarching shader is less likely to miss objects. However, they take
111
/// more GPU time.
112
pub linear_steps: u32,
113
114
/// Exponent to be applied in the linear part of the march.
115
///
116
/// A value of 1.0 will result in equidistant steps, and higher values will
117
/// compress the earlier steps, and expand the later ones. This might be
118
/// desirable in order to get more detail close to objects.
119
///
120
/// For optimal performance, this should be a small unsigned integer, such
121
/// as 1 or 2.
122
pub linear_march_exponent: f32,
123
124
/// The range over which SSR begins to fade out at the edges of the screen,
125
/// in terms of a percentage of the screen dimensions.
126
///
127
/// The first value is the percentage from the edge at which SSR is no
128
/// longer active; the second value is the percentage at which SSR is fully
129
/// active.
130
pub edge_fadeout: Range<f32>,
131
132
/// Number of steps in a bisection (binary search) to perform once the
133
/// linear search has found an intersection. Helps narrow down the hit,
134
/// increasing the chance of the secant method finding an accurate hit
135
/// point.
136
pub bisection_steps: u32,
137
138
/// Approximate the root position using the secant method—by solving for
139
/// line-line intersection between the ray approach rate and the surface
140
/// gradient.
141
pub use_secant: bool,
142
}
143
144
/// A version of [`ScreenSpaceReflections`] for upload to the GPU.
145
///
146
/// For more information on these fields, see the corresponding documentation in
147
/// [`ScreenSpaceReflections`].
148
#[derive(Clone, Copy, Component, ShaderType)]
149
pub struct ScreenSpaceReflectionsUniform {
150
min_perceptual_roughness: f32,
151
min_perceptual_roughness_fully_active: f32,
152
max_perceptual_roughness_starts_to_fade: f32,
153
max_perceptual_roughness: f32,
154
edge_fadeout_fully_active: f32,
155
edge_fadeout_no_longer_active: f32,
156
thickness: f32,
157
linear_steps: u32,
158
linear_march_exponent: f32,
159
bisection_steps: u32,
160
/// A boolean converted to a `u32`.
161
use_secant: u32,
162
}
163
164
/// Identifies which screen space reflections render pipeline a view needs.
165
#[derive(Component, Deref, DerefMut)]
166
pub struct ScreenSpaceReflectionsPipelineId(pub CachedRenderPipelineId);
167
168
/// Information relating to the render pipeline for the screen space reflections
169
/// shader.
170
#[derive(Resource)]
171
pub struct ScreenSpaceReflectionsPipeline {
172
mesh_view_layouts: MeshPipelineViewLayouts,
173
color_sampler: Sampler,
174
depth_linear_sampler: Sampler,
175
depth_nearest_sampler: Sampler,
176
bind_group_layout: BindGroupLayoutDescriptor,
177
binding_arrays_are_usable: bool,
178
fullscreen_shader: FullscreenShader,
179
fragment_shader: Handle<Shader>,
180
}
181
182
/// A GPU buffer that stores the screen space reflection settings for each view.
183
#[derive(Resource, Default, Deref, DerefMut)]
184
pub struct ScreenSpaceReflectionsBuffer(pub DynamicUniformBuffer<ScreenSpaceReflectionsUniform>);
185
186
/// A component that stores the offset within the
187
/// [`ScreenSpaceReflectionsBuffer`] for each view.
188
#[derive(Component, Default, Deref, DerefMut)]
189
pub struct ViewScreenSpaceReflectionsUniformOffset(u32);
190
191
/// Identifies a specific configuration of the SSR pipeline shader.
192
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
193
pub struct ScreenSpaceReflectionsPipelineKey {
194
mesh_pipeline_view_key: MeshPipelineViewLayoutKey,
195
is_hdr: bool,
196
has_environment_maps: bool,
197
has_atmosphere: bool,
198
}
199
200
impl Plugin for ScreenSpaceReflectionsPlugin {
201
fn build(&self, app: &mut App) {
202
load_shader_library!(app, "ssr.wgsl");
203
load_shader_library!(app, "raymarch.wgsl");
204
205
app.add_plugins(ExtractComponentPlugin::<ScreenSpaceReflections>::default());
206
207
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
208
return;
209
};
210
211
render_app
212
.init_resource::<ScreenSpaceReflectionsBuffer>()
213
.init_resource::<SpecializedRenderPipelines<ScreenSpaceReflectionsPipeline>>()
214
.add_systems(RenderStartup, init_screen_space_reflections_pipeline)
215
.add_systems(Render, prepare_ssr_pipelines.in_set(RenderSystems::Prepare))
216
.add_systems(
217
Render,
218
prepare_ssr_settings.in_set(RenderSystems::PrepareResources),
219
)
220
.add_systems(
221
Core3d,
222
screen_space_reflections
223
.after(deferred_lighting)
224
.before(main_opaque_pass_3d)
225
.in_set(Core3dSystems::MainPass),
226
);
227
}
228
}
229
230
impl Default for ScreenSpaceReflections {
231
// Reasonable default values.
232
//
233
// These are from
234
// <https://gist.github.com/h3r2tic/9c8356bdaefbe80b1a22ae0aaee192db?permalink_comment_id=4552149#gistcomment-4552149>.
235
fn default() -> Self {
236
Self {
237
min_perceptual_roughness: 0.08..0.12,
238
max_perceptual_roughness: 0.55..0.6,
239
linear_steps: 10,
240
bisection_steps: 5,
241
use_secant: true,
242
thickness: 0.25,
243
linear_march_exponent: 1.0,
244
edge_fadeout: 0.0..0.0,
245
}
246
}
247
}
248
249
pub fn screen_space_reflections(
250
view: ViewQuery<(
251
&ViewTarget,
252
&ViewUniformOffset,
253
&ViewLightsUniformOffset,
254
&ViewFogUniformOffset,
255
&ViewLightProbesUniformOffset,
256
&ViewScreenSpaceReflectionsUniformOffset,
257
&ViewContactShadowsUniformOffset,
258
&ViewEnvironmentMapUniformOffset,
259
&MeshViewBindGroup,
260
&ScreenSpaceReflectionsPipelineId,
261
)>,
262
pipeline_cache: Res<PipelineCache>,
263
ssr_pipeline: Res<ScreenSpaceReflectionsPipeline>,
264
bluenoise: Res<Bluenoise>,
265
render_images: Res<RenderAssets<GpuImage>>,
266
mut ctx: RenderContext,
267
) {
268
let (
269
view_target,
270
view_uniform_offset,
271
view_lights_offset,
272
view_fog_offset,
273
view_light_probes_offset,
274
view_ssr_offset,
275
view_contact_shadows_offset,
276
view_environment_map_offset,
277
view_bind_group,
278
ssr_pipeline_id,
279
) = view.into_inner();
280
281
// Grab the render pipeline.
282
let Some(render_pipeline) = pipeline_cache.get_render_pipeline(**ssr_pipeline_id) else {
283
return;
284
};
285
286
// Set up a standard pair of postprocessing textures.
287
let postprocess = view_target.post_process_write();
288
289
// Get blue noise texture for SSR.
290
let Some(stbn_texture) = render_images.get(&bluenoise.texture) else {
291
return;
292
};
293
let stbn_view = stbn_texture.texture.create_view(&TextureViewDescriptor {
294
label: Some("ssr_stbn_view"),
295
dimension: Some(TextureViewDimension::D2Array),
296
..default()
297
});
298
299
// Create the bind group for this view.
300
let ssr_bind_group = ctx.render_device().create_bind_group(
301
"SSR bind group",
302
&pipeline_cache.get_bind_group_layout(&ssr_pipeline.bind_group_layout),
303
&BindGroupEntries::sequential((
304
postprocess.source,
305
&ssr_pipeline.color_sampler,
306
&ssr_pipeline.depth_linear_sampler,
307
&ssr_pipeline.depth_nearest_sampler,
308
&stbn_view,
309
)),
310
);
311
312
let diagnostics = ctx.diagnostic_recorder();
313
let diagnostics = diagnostics.as_deref();
314
315
// Build the SSR render pass.
316
let mut render_pass = ctx.begin_tracked_render_pass(RenderPassDescriptor {
317
label: Some("ssr"),
318
color_attachments: &[Some(RenderPassColorAttachment {
319
view: postprocess.destination,
320
depth_slice: None,
321
resolve_target: None,
322
ops: Operations::default(),
323
})],
324
depth_stencil_attachment: None,
325
timestamp_writes: None,
326
occlusion_query_set: None,
327
multiview_mask: None,
328
});
329
let pass_span = diagnostics.pass_span(&mut render_pass, "ssr");
330
331
// Set bind groups.
332
render_pass.set_render_pipeline(render_pipeline);
333
render_pass.set_bind_group(
334
0,
335
&view_bind_group.main,
336
&[
337
view_uniform_offset.offset,
338
view_lights_offset.offset,
339
view_fog_offset.offset,
340
**view_light_probes_offset,
341
**view_ssr_offset,
342
**view_contact_shadows_offset,
343
**view_environment_map_offset,
344
],
345
);
346
render_pass.set_bind_group(1, &view_bind_group.binding_array, &[]);
347
348
// Perform the SSR render pass.
349
render_pass.set_bind_group(2, &ssr_bind_group, &[]);
350
render_pass.draw(0..3, 0..1);
351
352
pass_span.end(&mut render_pass);
353
}
354
355
pub fn init_screen_space_reflections_pipeline(
356
mut commands: Commands,
357
render_device: Res<RenderDevice>,
358
render_adapter: Res<RenderAdapter>,
359
mesh_view_layouts: Res<MeshPipelineViewLayouts>,
360
fullscreen_shader: Res<FullscreenShader>,
361
asset_server: Res<AssetServer>,
362
) {
363
// Create the bind group layout.
364
let bind_group_layout = BindGroupLayoutDescriptor::new(
365
"SSR bind group layout",
366
&BindGroupLayoutEntries::sequential(
367
ShaderStages::FRAGMENT,
368
(
369
binding_types::texture_2d(TextureSampleType::Float { filterable: true }),
370
binding_types::sampler(SamplerBindingType::Filtering),
371
binding_types::sampler(SamplerBindingType::Filtering),
372
binding_types::sampler(SamplerBindingType::NonFiltering),
373
binding_types::texture_2d_array(TextureSampleType::Float { filterable: false }),
374
),
375
),
376
);
377
378
// Create the samplers we need.
379
380
let color_sampler = render_device.create_sampler(&SamplerDescriptor {
381
label: "SSR color sampler".into(),
382
address_mode_u: AddressMode::ClampToEdge,
383
address_mode_v: AddressMode::ClampToEdge,
384
mag_filter: FilterMode::Linear,
385
min_filter: FilterMode::Linear,
386
..default()
387
});
388
389
let depth_linear_sampler = render_device.create_sampler(&SamplerDescriptor {
390
label: "SSR depth linear sampler".into(),
391
address_mode_u: AddressMode::ClampToEdge,
392
address_mode_v: AddressMode::ClampToEdge,
393
mag_filter: FilterMode::Linear,
394
min_filter: FilterMode::Linear,
395
..default()
396
});
397
398
let depth_nearest_sampler = render_device.create_sampler(&SamplerDescriptor {
399
label: "SSR depth nearest sampler".into(),
400
address_mode_u: AddressMode::ClampToEdge,
401
address_mode_v: AddressMode::ClampToEdge,
402
mag_filter: FilterMode::Nearest,
403
min_filter: FilterMode::Nearest,
404
..default()
405
});
406
407
commands.insert_resource(ScreenSpaceReflectionsPipeline {
408
mesh_view_layouts: mesh_view_layouts.clone(),
409
color_sampler,
410
depth_linear_sampler,
411
depth_nearest_sampler,
412
bind_group_layout,
413
binding_arrays_are_usable: binding_arrays_are_usable(&render_device, &render_adapter),
414
fullscreen_shader: fullscreen_shader.clone(),
415
// Even though ssr was loaded using load_shader_library, we can still access it like a
416
// normal embedded asset (so we can use it as both a library or a kernel).
417
fragment_shader: load_embedded_asset!(asset_server.as_ref(), "ssr.wgsl"),
418
});
419
}
420
421
/// Sets up screen space reflection pipelines for each applicable view.
422
pub fn prepare_ssr_pipelines(
423
mut commands: Commands,
424
pipeline_cache: Res<PipelineCache>,
425
mut pipelines: ResMut<SpecializedRenderPipelines<ScreenSpaceReflectionsPipeline>>,
426
ssr_pipeline: Res<ScreenSpaceReflectionsPipeline>,
427
views: Query<
428
(
429
Entity,
430
&ExtractedView,
431
Has<RenderViewLightProbes<EnvironmentMapLight>>,
432
Has<NormalPrepass>,
433
Has<MotionVectorPrepass>,
434
Has<ExtractedAtmosphere>,
435
),
436
(
437
With<ScreenSpaceReflectionsUniform>,
438
With<DepthPrepass>,
439
With<DeferredPrepass>,
440
),
441
>,
442
) {
443
for (
444
entity,
445
extracted_view,
446
has_environment_maps,
447
has_normal_prepass,
448
has_motion_vector_prepass,
449
has_atmosphere,
450
) in &views
451
{
452
// SSR is only supported in the deferred pipeline, which has no MSAA
453
// support. Thus we can assume MSAA is off.
454
let mut mesh_pipeline_view_key = MeshPipelineViewLayoutKey::from(Msaa::Off)
455
| MeshPipelineViewLayoutKey::DEPTH_PREPASS
456
| MeshPipelineViewLayoutKey::DEFERRED_PREPASS;
457
mesh_pipeline_view_key.set(
458
MeshPipelineViewLayoutKey::NORMAL_PREPASS,
459
has_normal_prepass,
460
);
461
mesh_pipeline_view_key.set(
462
MeshPipelineViewLayoutKey::MOTION_VECTOR_PREPASS,
463
has_motion_vector_prepass,
464
);
465
mesh_pipeline_view_key.set(MeshPipelineViewLayoutKey::ATMOSPHERE, has_atmosphere);
466
if cfg!(feature = "bluenoise_texture") {
467
mesh_pipeline_view_key |= MeshPipelineViewLayoutKey::STBN;
468
}
469
470
// Build the pipeline.
471
let pipeline_id = pipelines.specialize(
472
&pipeline_cache,
473
&ssr_pipeline,
474
ScreenSpaceReflectionsPipelineKey {
475
mesh_pipeline_view_key,
476
is_hdr: extracted_view.hdr,
477
has_environment_maps,
478
has_atmosphere,
479
},
480
);
481
482
// Note which pipeline ID was used.
483
commands
484
.entity(entity)
485
.insert(ScreenSpaceReflectionsPipelineId(pipeline_id));
486
}
487
}
488
489
/// Gathers up screen space reflection settings for each applicable view and
490
/// writes them into a GPU buffer.
491
pub fn prepare_ssr_settings(
492
mut commands: Commands,
493
views: Query<(Entity, Option<&ScreenSpaceReflectionsUniform>), With<ExtractedView>>,
494
mut ssr_settings_buffer: ResMut<ScreenSpaceReflectionsBuffer>,
495
render_device: Res<RenderDevice>,
496
render_queue: Res<RenderQueue>,
497
) {
498
let Some(mut writer) =
499
ssr_settings_buffer.get_writer(views.iter().len(), &render_device, &render_queue)
500
else {
501
return;
502
};
503
504
for (view, ssr_uniform) in views.iter() {
505
let uniform_offset = match ssr_uniform {
506
None => 0,
507
Some(ssr_uniform) => writer.write(ssr_uniform),
508
};
509
commands
510
.entity(view)
511
.insert(ViewScreenSpaceReflectionsUniformOffset(uniform_offset));
512
}
513
}
514
515
impl SyncComponent for ScreenSpaceReflections {
516
type Out = ScreenSpaceReflectionsUniform;
517
}
518
519
impl ExtractComponent for ScreenSpaceReflections {
520
type QueryData = Read<ScreenSpaceReflections>;
521
type QueryFilter = ();
522
523
fn extract_component(settings: QueryItem<'_, '_, Self::QueryData>) -> Option<Self::Out> {
524
if !DEPTH_TEXTURE_SAMPLING_SUPPORTED {
525
once!(info!(
526
"Disabling screen-space reflections on this platform because depth textures \
527
aren't supported correctly"
528
));
529
return None;
530
}
531
532
Some(settings.clone().into())
533
}
534
}
535
536
impl SpecializedRenderPipeline for ScreenSpaceReflectionsPipeline {
537
type Key = ScreenSpaceReflectionsPipelineKey;
538
539
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
540
let layout = self
541
.mesh_view_layouts
542
.get_view_layout(key.mesh_pipeline_view_key);
543
let layout = vec![
544
layout.main_layout.clone(),
545
layout.binding_array_layout.clone(),
546
self.bind_group_layout.clone(),
547
];
548
549
let mut shader_defs = vec![
550
"DEPTH_PREPASS".into(),
551
"DEFERRED_PREPASS".into(),
552
"SCREEN_SPACE_REFLECTIONS".into(),
553
];
554
555
if key.has_environment_maps {
556
shader_defs.push("ENVIRONMENT_MAP".into());
557
}
558
559
if self.binding_arrays_are_usable {
560
shader_defs.push("MULTIPLE_LIGHT_PROBES_IN_ARRAY".into());
561
}
562
563
if key.has_atmosphere {
564
shader_defs.push("ATMOSPHERE".into());
565
}
566
567
if cfg!(feature = "bluenoise_texture") {
568
shader_defs.push("BLUE_NOISE_TEXTURE".into());
569
}
570
571
#[cfg(not(target_arch = "wasm32"))]
572
shader_defs.push("USE_DEPTH_SAMPLERS".into());
573
574
RenderPipelineDescriptor {
575
label: Some("SSR pipeline".into()),
576
layout,
577
vertex: self.fullscreen_shader.to_vertex_state(),
578
fragment: Some(FragmentState {
579
shader: self.fragment_shader.clone(),
580
shader_defs,
581
targets: vec![Some(ColorTargetState {
582
format: if key.is_hdr {
583
ViewTarget::TEXTURE_FORMAT_HDR
584
} else {
585
TextureFormat::bevy_default()
586
},
587
blend: None,
588
write_mask: ColorWrites::ALL,
589
})],
590
..default()
591
}),
592
..default()
593
}
594
}
595
}
596
597
impl From<ScreenSpaceReflections> for ScreenSpaceReflectionsUniform {
598
fn from(settings: ScreenSpaceReflections) -> Self {
599
Self {
600
min_perceptual_roughness: settings.min_perceptual_roughness.start,
601
min_perceptual_roughness_fully_active: settings.min_perceptual_roughness.end,
602
max_perceptual_roughness_starts_to_fade: settings.max_perceptual_roughness.start,
603
max_perceptual_roughness: settings.max_perceptual_roughness.end,
604
edge_fadeout_no_longer_active: settings.edge_fadeout.start,
605
edge_fadeout_fully_active: settings.edge_fadeout.end,
606
thickness: settings.thickness,
607
linear_steps: settings.linear_steps,
608
linear_march_exponent: settings.linear_march_exponent,
609
bisection_steps: settings.bisection_steps,
610
use_secant: settings.use_secant as u32,
611
}
612
}
613
}
614
615