Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_sprite_render/src/mesh2d/mesh.rs
6849 views
1
use bevy_app::Plugin;
2
use bevy_asset::{embedded_asset, load_embedded_asset, AssetId, AssetServer, Handle};
3
use bevy_camera::{visibility::ViewVisibility, Camera2d};
4
use bevy_render::RenderStartup;
5
use bevy_shader::{load_shader_library, Shader, ShaderDefVal, ShaderSettings};
6
7
use crate::{tonemapping_pipeline_key, Material2dBindGroupId};
8
use bevy_core_pipeline::{
9
core_2d::{AlphaMask2d, Opaque2d, Transparent2d, CORE_2D_DEPTH_FORMAT},
10
tonemapping::{
11
get_lut_bind_group_layout_entries, get_lut_bindings, DebandDither, Tonemapping,
12
TonemappingLuts,
13
},
14
};
15
use bevy_derive::{Deref, DerefMut};
16
use bevy_ecs::component::Tick;
17
use bevy_ecs::system::SystemChangeTick;
18
use bevy_ecs::{
19
prelude::*,
20
query::ROQueryItem,
21
system::{lifetimeless::*, SystemParamItem},
22
};
23
use bevy_image::{BevyDefault, Image, ImageSampler, TextureFormatPixelInfo};
24
use bevy_math::{Affine3, Vec4};
25
use bevy_mesh::{Mesh, Mesh2d, MeshTag, MeshVertexBufferLayoutRef};
26
use bevy_render::prelude::Msaa;
27
use bevy_render::RenderSystems::PrepareAssets;
28
use bevy_render::{
29
batching::{
30
gpu_preprocessing::IndirectParametersCpuMetadata,
31
no_gpu_preprocessing::{
32
self, batch_and_prepare_binned_render_phase, batch_and_prepare_sorted_render_phase,
33
write_batched_instance_buffer, BatchedInstanceBuffer,
34
},
35
GetBatchData, GetFullBatchData, NoAutomaticBatching,
36
},
37
globals::{GlobalsBuffer, GlobalsUniform},
38
mesh::{allocator::MeshAllocator, RenderMesh, RenderMeshBufferInfo},
39
render_asset::RenderAssets,
40
render_phase::{
41
sweep_old_entities, PhaseItem, PhaseItemExtraIndex, RenderCommand, RenderCommandResult,
42
TrackedRenderPass,
43
},
44
render_resource::{binding_types::uniform_buffer, *},
45
renderer::{RenderDevice, RenderQueue},
46
sync_world::{MainEntity, MainEntityHashMap},
47
texture::{DefaultImageSampler, FallbackImage, GpuImage},
48
view::{ExtractedView, ViewTarget, ViewUniform, ViewUniformOffset, ViewUniforms},
49
Extract, ExtractSchedule, Render, RenderApp, RenderSystems,
50
};
51
use bevy_transform::components::GlobalTransform;
52
use bevy_utils::default;
53
use nonmax::NonMaxU32;
54
use tracing::error;
55
56
#[derive(Default)]
57
pub struct Mesh2dRenderPlugin;
58
59
impl Plugin for Mesh2dRenderPlugin {
60
fn build(&self, app: &mut bevy_app::App) {
61
load_shader_library!(app, "mesh2d_vertex_output.wgsl");
62
load_shader_library!(app, "mesh2d_view_types.wgsl");
63
load_shader_library!(app, "mesh2d_view_bindings.wgsl");
64
load_shader_library!(app, "mesh2d_types.wgsl");
65
load_shader_library!(app, "mesh2d_functions.wgsl");
66
67
embedded_asset!(app, "mesh2d.wgsl");
68
69
// These bindings should be loaded as a shader library, but it depends on runtime
70
// information, so we will load it in a system.
71
embedded_asset!(app, "mesh2d_bindings.wgsl");
72
73
if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
74
render_app
75
.init_resource::<ViewKeyCache>()
76
.init_resource::<RenderMesh2dInstances>()
77
.init_resource::<SpecializedMeshPipelines<Mesh2dPipeline>>()
78
.init_resource::<ViewSpecializationTicks>()
79
.add_systems(
80
RenderStartup,
81
(
82
init_mesh_2d_pipeline,
83
init_batched_instance_buffer,
84
load_mesh2d_bindings,
85
),
86
)
87
.add_systems(ExtractSchedule, extract_mesh2d)
88
.add_systems(
89
Render,
90
(
91
check_views_need_specialization.in_set(PrepareAssets),
92
(
93
sweep_old_entities::<Opaque2d>,
94
sweep_old_entities::<AlphaMask2d>,
95
)
96
.in_set(RenderSystems::QueueSweep),
97
batch_and_prepare_binned_render_phase::<Opaque2d, Mesh2dPipeline>
98
.in_set(RenderSystems::PrepareResources),
99
batch_and_prepare_binned_render_phase::<AlphaMask2d, Mesh2dPipeline>
100
.in_set(RenderSystems::PrepareResources),
101
batch_and_prepare_sorted_render_phase::<Transparent2d, Mesh2dPipeline>
102
.in_set(RenderSystems::PrepareResources),
103
write_batched_instance_buffer::<Mesh2dPipeline>
104
.in_set(RenderSystems::PrepareResourcesFlush),
105
prepare_mesh2d_bind_group.in_set(RenderSystems::PrepareBindGroups),
106
prepare_mesh2d_view_bind_groups.in_set(RenderSystems::PrepareBindGroups),
107
no_gpu_preprocessing::clear_batched_cpu_instance_buffers::<Mesh2dPipeline>
108
.in_set(RenderSystems::Cleanup)
109
.after(RenderSystems::Render),
110
),
111
);
112
}
113
}
114
}
115
116
#[derive(Resource, Deref, DerefMut, Default, Debug, Clone)]
117
pub struct ViewKeyCache(MainEntityHashMap<Mesh2dPipelineKey>);
118
119
#[derive(Resource, Deref, DerefMut, Default, Debug, Clone)]
120
pub struct ViewSpecializationTicks(MainEntityHashMap<Tick>);
121
122
pub fn check_views_need_specialization(
123
mut view_key_cache: ResMut<ViewKeyCache>,
124
mut view_specialization_ticks: ResMut<ViewSpecializationTicks>,
125
views: Query<(
126
&MainEntity,
127
&ExtractedView,
128
&Msaa,
129
Option<&Tonemapping>,
130
Option<&DebandDither>,
131
)>,
132
ticks: SystemChangeTick,
133
) {
134
for (view_entity, view, msaa, tonemapping, dither) in &views {
135
let mut view_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())
136
| Mesh2dPipelineKey::from_hdr(view.hdr);
137
138
if !view.hdr {
139
if let Some(tonemapping) = tonemapping {
140
view_key |= Mesh2dPipelineKey::TONEMAP_IN_SHADER;
141
view_key |= tonemapping_pipeline_key(*tonemapping);
142
}
143
if let Some(DebandDither::Enabled) = dither {
144
view_key |= Mesh2dPipelineKey::DEBAND_DITHER;
145
}
146
}
147
148
if !view_key_cache
149
.get_mut(view_entity)
150
.is_some_and(|current_key| *current_key == view_key)
151
{
152
view_key_cache.insert(*view_entity, view_key);
153
view_specialization_ticks.insert(*view_entity, ticks.this_run());
154
}
155
}
156
}
157
158
pub fn init_batched_instance_buffer(mut commands: Commands, render_device: Res<RenderDevice>) {
159
commands.insert_resource(BatchedInstanceBuffer::<Mesh2dUniform>::new(&render_device));
160
}
161
162
fn load_mesh2d_bindings(render_device: Res<RenderDevice>, asset_server: Res<AssetServer>) {
163
let mut mesh_bindings_shader_defs = Vec::with_capacity(1);
164
165
if let Some(per_object_buffer_batch_size) =
166
GpuArrayBuffer::<Mesh2dUniform>::batch_size(&render_device)
167
{
168
mesh_bindings_shader_defs.push(ShaderDefVal::UInt(
169
"PER_OBJECT_BUFFER_BATCH_SIZE".into(),
170
per_object_buffer_batch_size,
171
));
172
}
173
174
// Load the mesh_bindings shader module here as it depends on runtime information about
175
// whether storage buffers are supported, or the maximum uniform buffer binding size.
176
let handle: Handle<Shader> = load_embedded_asset!(
177
asset_server.as_ref(),
178
"mesh2d_bindings.wgsl",
179
move |settings| {
180
*settings = ShaderSettings {
181
shader_defs: mesh_bindings_shader_defs.clone(),
182
}
183
}
184
);
185
// Forget the handle so we don't have to store it anywhere, and we keep the embedded asset
186
// loaded. Note: This is what happens in `load_shader_library` internally.
187
core::mem::forget(handle);
188
}
189
190
#[derive(Component)]
191
pub struct Mesh2dTransforms {
192
pub world_from_local: Affine3,
193
pub flags: u32,
194
}
195
196
#[derive(ShaderType, Clone, Copy)]
197
pub struct Mesh2dUniform {
198
// Affine 4x3 matrix transposed to 3x4
199
pub world_from_local: [Vec4; 3],
200
// 3x3 matrix packed in mat2x4 and f32 as:
201
// [0].xyz, [1].x,
202
// [1].yz, [2].xy
203
// [2].z
204
pub local_from_world_transpose_a: [Vec4; 2],
205
pub local_from_world_transpose_b: f32,
206
pub flags: u32,
207
pub tag: u32,
208
}
209
210
impl Mesh2dUniform {
211
fn from_components(mesh_transforms: &Mesh2dTransforms, tag: u32) -> Self {
212
let (local_from_world_transpose_a, local_from_world_transpose_b) =
213
mesh_transforms.world_from_local.inverse_transpose_3x3();
214
Self {
215
world_from_local: mesh_transforms.world_from_local.to_transpose(),
216
local_from_world_transpose_a,
217
local_from_world_transpose_b,
218
flags: mesh_transforms.flags,
219
tag,
220
}
221
}
222
}
223
224
// NOTE: These must match the bit flags in bevy_sprite_render/src/mesh2d/mesh2d.wgsl!
225
bitflags::bitflags! {
226
#[repr(transparent)]
227
pub struct MeshFlags: u32 {
228
const NONE = 0;
229
const UNINITIALIZED = 0xFFFF;
230
}
231
}
232
233
pub struct RenderMesh2dInstance {
234
pub transforms: Mesh2dTransforms,
235
pub mesh_asset_id: AssetId<Mesh>,
236
pub material_bind_group_id: Material2dBindGroupId,
237
pub automatic_batching: bool,
238
pub tag: u32,
239
}
240
241
#[derive(Default, Resource, Deref, DerefMut)]
242
pub struct RenderMesh2dInstances(MainEntityHashMap<RenderMesh2dInstance>);
243
244
#[derive(Component, Default)]
245
pub struct Mesh2dMarker;
246
247
pub fn extract_mesh2d(
248
mut render_mesh_instances: ResMut<RenderMesh2dInstances>,
249
query: Extract<
250
Query<(
251
Entity,
252
&ViewVisibility,
253
&GlobalTransform,
254
&Mesh2d,
255
Option<&MeshTag>,
256
Has<NoAutomaticBatching>,
257
)>,
258
>,
259
) {
260
render_mesh_instances.clear();
261
262
for (entity, view_visibility, transform, handle, tag, no_automatic_batching) in &query {
263
if !view_visibility.get() {
264
continue;
265
}
266
render_mesh_instances.insert(
267
entity.into(),
268
RenderMesh2dInstance {
269
transforms: Mesh2dTransforms {
270
world_from_local: (&transform.affine()).into(),
271
flags: MeshFlags::empty().bits(),
272
},
273
mesh_asset_id: handle.0.id(),
274
material_bind_group_id: Material2dBindGroupId::default(),
275
automatic_batching: !no_automatic_batching,
276
tag: tag.map_or(0, |i| **i),
277
},
278
);
279
}
280
}
281
282
#[derive(Resource, Clone)]
283
pub struct Mesh2dPipeline {
284
pub view_layout: BindGroupLayout,
285
pub mesh_layout: BindGroupLayout,
286
pub shader: Handle<Shader>,
287
// This dummy white texture is to be used in place of optional textures
288
pub dummy_white_gpu_image: GpuImage,
289
pub per_object_buffer_batch_size: Option<u32>,
290
}
291
292
pub fn init_mesh_2d_pipeline(
293
mut commands: Commands,
294
render_device: Res<RenderDevice>,
295
render_queue: Res<RenderQueue>,
296
default_sampler: Res<DefaultImageSampler>,
297
asset_server: Res<AssetServer>,
298
) {
299
let tonemapping_lut_entries = get_lut_bind_group_layout_entries();
300
let view_layout = render_device.create_bind_group_layout(
301
"mesh2d_view_layout",
302
&BindGroupLayoutEntries::sequential(
303
ShaderStages::VERTEX_FRAGMENT,
304
(
305
uniform_buffer::<ViewUniform>(true),
306
uniform_buffer::<GlobalsUniform>(false),
307
tonemapping_lut_entries[0].visibility(ShaderStages::FRAGMENT),
308
tonemapping_lut_entries[1].visibility(ShaderStages::FRAGMENT),
309
),
310
),
311
);
312
313
let mesh_layout = render_device.create_bind_group_layout(
314
"mesh2d_layout",
315
&BindGroupLayoutEntries::single(
316
ShaderStages::VERTEX_FRAGMENT,
317
GpuArrayBuffer::<Mesh2dUniform>::binding_layout(&render_device),
318
),
319
);
320
// A 1x1x1 'all 1.0' texture to use as a dummy texture to use in place of optional StandardMaterial textures
321
let dummy_white_gpu_image = {
322
let image = Image::default();
323
let texture = render_device.create_texture(&image.texture_descriptor);
324
let sampler = match image.sampler {
325
ImageSampler::Default => (**default_sampler).clone(),
326
ImageSampler::Descriptor(ref descriptor) => {
327
render_device.create_sampler(&descriptor.as_wgpu())
328
}
329
};
330
331
if let Ok(format_size) = image.texture_descriptor.format.pixel_size() {
332
render_queue.write_texture(
333
texture.as_image_copy(),
334
image.data.as_ref().expect("Image has no data"),
335
TexelCopyBufferLayout {
336
offset: 0,
337
bytes_per_row: Some(image.width() * format_size as u32),
338
rows_per_image: None,
339
},
340
image.texture_descriptor.size,
341
);
342
}
343
344
let texture_view = texture.create_view(&TextureViewDescriptor::default());
345
GpuImage {
346
texture,
347
texture_view,
348
texture_format: image.texture_descriptor.format,
349
sampler,
350
size: image.texture_descriptor.size,
351
mip_level_count: image.texture_descriptor.mip_level_count,
352
}
353
};
354
commands.insert_resource(Mesh2dPipeline {
355
view_layout,
356
mesh_layout,
357
dummy_white_gpu_image,
358
per_object_buffer_batch_size: GpuArrayBuffer::<Mesh2dUniform>::batch_size(&render_device),
359
shader: load_embedded_asset!(asset_server.as_ref(), "mesh2d.wgsl"),
360
});
361
}
362
363
impl Mesh2dPipeline {
364
pub fn get_image_texture<'a>(
365
&'a self,
366
gpu_images: &'a RenderAssets<GpuImage>,
367
handle_option: &Option<Handle<Image>>,
368
) -> Option<(&'a TextureView, &'a Sampler)> {
369
if let Some(handle) = handle_option {
370
let gpu_image = gpu_images.get(handle)?;
371
Some((&gpu_image.texture_view, &gpu_image.sampler))
372
} else {
373
Some((
374
&self.dummy_white_gpu_image.texture_view,
375
&self.dummy_white_gpu_image.sampler,
376
))
377
}
378
}
379
}
380
381
impl GetBatchData for Mesh2dPipeline {
382
type Param = (
383
SRes<RenderMesh2dInstances>,
384
SRes<RenderAssets<RenderMesh>>,
385
SRes<MeshAllocator>,
386
);
387
type CompareData = (Material2dBindGroupId, AssetId<Mesh>);
388
type BufferData = Mesh2dUniform;
389
390
fn get_batch_data(
391
(mesh_instances, _, _): &SystemParamItem<Self::Param>,
392
(_entity, main_entity): (Entity, MainEntity),
393
) -> Option<(Self::BufferData, Option<Self::CompareData>)> {
394
let mesh_instance = mesh_instances.get(&main_entity)?;
395
Some((
396
Mesh2dUniform::from_components(&mesh_instance.transforms, mesh_instance.tag),
397
mesh_instance.automatic_batching.then_some((
398
mesh_instance.material_bind_group_id,
399
mesh_instance.mesh_asset_id,
400
)),
401
))
402
}
403
}
404
405
impl GetFullBatchData for Mesh2dPipeline {
406
type BufferInputData = ();
407
408
fn get_binned_batch_data(
409
(mesh_instances, _, _): &SystemParamItem<Self::Param>,
410
main_entity: MainEntity,
411
) -> Option<Self::BufferData> {
412
let mesh_instance = mesh_instances.get(&main_entity)?;
413
Some(Mesh2dUniform::from_components(
414
&mesh_instance.transforms,
415
mesh_instance.tag,
416
))
417
}
418
419
fn get_index_and_compare_data(
420
_: &SystemParamItem<Self::Param>,
421
_query_item: MainEntity,
422
) -> Option<(NonMaxU32, Option<Self::CompareData>)> {
423
error!(
424
"`get_index_and_compare_data` is only intended for GPU mesh uniform building, \
425
but this is not yet implemented for 2d meshes"
426
);
427
None
428
}
429
430
fn get_binned_index(
431
_: &SystemParamItem<Self::Param>,
432
_query_item: MainEntity,
433
) -> Option<NonMaxU32> {
434
error!(
435
"`get_binned_index` is only intended for GPU mesh uniform building, \
436
but this is not yet implemented for 2d meshes"
437
);
438
None
439
}
440
441
fn write_batch_indirect_parameters_metadata(
442
indexed: bool,
443
base_output_index: u32,
444
batch_set_index: Option<NonMaxU32>,
445
indirect_parameters_buffer: &mut bevy_render::batching::gpu_preprocessing::UntypedPhaseIndirectParametersBuffers,
446
indirect_parameters_offset: u32,
447
) {
448
// Note that `IndirectParameters` covers both of these structures, even
449
// though they actually have distinct layouts. See the comment above that
450
// type for more information.
451
let indirect_parameters = IndirectParametersCpuMetadata {
452
base_output_index,
453
batch_set_index: match batch_set_index {
454
None => !0,
455
Some(batch_set_index) => u32::from(batch_set_index),
456
},
457
};
458
459
if indexed {
460
indirect_parameters_buffer
461
.indexed
462
.set(indirect_parameters_offset, indirect_parameters);
463
} else {
464
indirect_parameters_buffer
465
.non_indexed
466
.set(indirect_parameters_offset, indirect_parameters);
467
}
468
}
469
}
470
471
bitflags::bitflags! {
472
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
473
#[repr(transparent)]
474
// NOTE: Apparently quadro drivers support up to 64x MSAA.
475
// MSAA uses the highest 3 bits for the MSAA log2(sample count) to support up to 128x MSAA.
476
// FIXME: make normals optional?
477
pub struct Mesh2dPipelineKey: u32 {
478
const NONE = 0;
479
const HDR = 1 << 0;
480
const TONEMAP_IN_SHADER = 1 << 1;
481
const DEBAND_DITHER = 1 << 2;
482
const BLEND_ALPHA = 1 << 3;
483
const MAY_DISCARD = 1 << 4;
484
const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS;
485
const PRIMITIVE_TOPOLOGY_RESERVED_BITS = Self::PRIMITIVE_TOPOLOGY_MASK_BITS << Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
486
const TONEMAP_METHOD_RESERVED_BITS = Self::TONEMAP_METHOD_MASK_BITS << Self::TONEMAP_METHOD_SHIFT_BITS;
487
const TONEMAP_METHOD_NONE = 0 << Self::TONEMAP_METHOD_SHIFT_BITS;
488
const TONEMAP_METHOD_REINHARD = 1 << Self::TONEMAP_METHOD_SHIFT_BITS;
489
const TONEMAP_METHOD_REINHARD_LUMINANCE = 2 << Self::TONEMAP_METHOD_SHIFT_BITS;
490
const TONEMAP_METHOD_ACES_FITTED = 3 << Self::TONEMAP_METHOD_SHIFT_BITS;
491
const TONEMAP_METHOD_AGX = 4 << Self::TONEMAP_METHOD_SHIFT_BITS;
492
const TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM = 5 << Self::TONEMAP_METHOD_SHIFT_BITS;
493
const TONEMAP_METHOD_TONY_MC_MAPFACE = 6 << Self::TONEMAP_METHOD_SHIFT_BITS;
494
const TONEMAP_METHOD_BLENDER_FILMIC = 7 << Self::TONEMAP_METHOD_SHIFT_BITS;
495
}
496
}
497
498
impl Mesh2dPipelineKey {
499
const MSAA_MASK_BITS: u32 = 0b111;
500
const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones();
501
const PRIMITIVE_TOPOLOGY_MASK_BITS: u32 = 0b111;
502
const PRIMITIVE_TOPOLOGY_SHIFT_BITS: u32 = Self::MSAA_SHIFT_BITS - 3;
503
const TONEMAP_METHOD_MASK_BITS: u32 = 0b111;
504
const TONEMAP_METHOD_SHIFT_BITS: u32 =
505
Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS - Self::TONEMAP_METHOD_MASK_BITS.count_ones();
506
507
pub fn from_msaa_samples(msaa_samples: u32) -> Self {
508
let msaa_bits =
509
(msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
510
Self::from_bits_retain(msaa_bits)
511
}
512
513
pub fn from_hdr(hdr: bool) -> Self {
514
if hdr {
515
Mesh2dPipelineKey::HDR
516
} else {
517
Mesh2dPipelineKey::NONE
518
}
519
}
520
521
pub fn msaa_samples(&self) -> u32 {
522
1 << ((self.bits() >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS)
523
}
524
525
pub fn from_primitive_topology(primitive_topology: PrimitiveTopology) -> Self {
526
let primitive_topology_bits = ((primitive_topology as u32)
527
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS)
528
<< Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS;
529
Self::from_bits_retain(primitive_topology_bits)
530
}
531
532
pub fn primitive_topology(&self) -> PrimitiveTopology {
533
let primitive_topology_bits = (self.bits() >> Self::PRIMITIVE_TOPOLOGY_SHIFT_BITS)
534
& Self::PRIMITIVE_TOPOLOGY_MASK_BITS;
535
match primitive_topology_bits {
536
x if x == PrimitiveTopology::PointList as u32 => PrimitiveTopology::PointList,
537
x if x == PrimitiveTopology::LineList as u32 => PrimitiveTopology::LineList,
538
x if x == PrimitiveTopology::LineStrip as u32 => PrimitiveTopology::LineStrip,
539
x if x == PrimitiveTopology::TriangleList as u32 => PrimitiveTopology::TriangleList,
540
x if x == PrimitiveTopology::TriangleStrip as u32 => PrimitiveTopology::TriangleStrip,
541
_ => PrimitiveTopology::default(),
542
}
543
}
544
}
545
546
impl SpecializedMeshPipeline for Mesh2dPipeline {
547
type Key = Mesh2dPipelineKey;
548
549
fn specialize(
550
&self,
551
key: Self::Key,
552
layout: &MeshVertexBufferLayoutRef,
553
) -> Result<RenderPipelineDescriptor, SpecializedMeshPipelineError> {
554
let mut shader_defs = Vec::new();
555
let mut vertex_attributes = Vec::new();
556
557
if layout.0.contains(Mesh::ATTRIBUTE_POSITION) {
558
shader_defs.push("VERTEX_POSITIONS".into());
559
vertex_attributes.push(Mesh::ATTRIBUTE_POSITION.at_shader_location(0));
560
}
561
562
if layout.0.contains(Mesh::ATTRIBUTE_NORMAL) {
563
shader_defs.push("VERTEX_NORMALS".into());
564
vertex_attributes.push(Mesh::ATTRIBUTE_NORMAL.at_shader_location(1));
565
}
566
567
if layout.0.contains(Mesh::ATTRIBUTE_UV_0) {
568
shader_defs.push("VERTEX_UVS".into());
569
vertex_attributes.push(Mesh::ATTRIBUTE_UV_0.at_shader_location(2));
570
}
571
572
if layout.0.contains(Mesh::ATTRIBUTE_TANGENT) {
573
shader_defs.push("VERTEX_TANGENTS".into());
574
vertex_attributes.push(Mesh::ATTRIBUTE_TANGENT.at_shader_location(3));
575
}
576
577
if layout.0.contains(Mesh::ATTRIBUTE_COLOR) {
578
shader_defs.push("VERTEX_COLORS".into());
579
vertex_attributes.push(Mesh::ATTRIBUTE_COLOR.at_shader_location(4));
580
}
581
582
if key.contains(Mesh2dPipelineKey::TONEMAP_IN_SHADER) {
583
shader_defs.push("TONEMAP_IN_SHADER".into());
584
shader_defs.push(ShaderDefVal::UInt(
585
"TONEMAPPING_LUT_TEXTURE_BINDING_INDEX".into(),
586
2,
587
));
588
shader_defs.push(ShaderDefVal::UInt(
589
"TONEMAPPING_LUT_SAMPLER_BINDING_INDEX".into(),
590
3,
591
));
592
593
let method = key.intersection(Mesh2dPipelineKey::TONEMAP_METHOD_RESERVED_BITS);
594
595
match method {
596
Mesh2dPipelineKey::TONEMAP_METHOD_NONE => {
597
shader_defs.push("TONEMAP_METHOD_NONE".into());
598
}
599
Mesh2dPipelineKey::TONEMAP_METHOD_REINHARD => {
600
shader_defs.push("TONEMAP_METHOD_REINHARD".into());
601
}
602
Mesh2dPipelineKey::TONEMAP_METHOD_REINHARD_LUMINANCE => {
603
shader_defs.push("TONEMAP_METHOD_REINHARD_LUMINANCE".into());
604
}
605
Mesh2dPipelineKey::TONEMAP_METHOD_ACES_FITTED => {
606
shader_defs.push("TONEMAP_METHOD_ACES_FITTED".into());
607
}
608
Mesh2dPipelineKey::TONEMAP_METHOD_AGX => {
609
shader_defs.push("TONEMAP_METHOD_AGX".into());
610
}
611
Mesh2dPipelineKey::TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM => {
612
shader_defs.push("TONEMAP_METHOD_SOMEWHAT_BORING_DISPLAY_TRANSFORM".into());
613
}
614
Mesh2dPipelineKey::TONEMAP_METHOD_BLENDER_FILMIC => {
615
shader_defs.push("TONEMAP_METHOD_BLENDER_FILMIC".into());
616
}
617
Mesh2dPipelineKey::TONEMAP_METHOD_TONY_MC_MAPFACE => {
618
shader_defs.push("TONEMAP_METHOD_TONY_MC_MAPFACE".into());
619
}
620
_ => {}
621
}
622
// Debanding is tied to tonemapping in the shader, cannot run without it.
623
if key.contains(Mesh2dPipelineKey::DEBAND_DITHER) {
624
shader_defs.push("DEBAND_DITHER".into());
625
}
626
}
627
628
if key.contains(Mesh2dPipelineKey::MAY_DISCARD) {
629
shader_defs.push("MAY_DISCARD".into());
630
}
631
632
let vertex_buffer_layout = layout.0.get_layout(&vertex_attributes)?;
633
634
let format = match key.contains(Mesh2dPipelineKey::HDR) {
635
true => ViewTarget::TEXTURE_FORMAT_HDR,
636
false => TextureFormat::bevy_default(),
637
};
638
639
let (depth_write_enabled, label, blend);
640
if key.contains(Mesh2dPipelineKey::BLEND_ALPHA) {
641
label = "transparent_mesh2d_pipeline";
642
blend = Some(BlendState::ALPHA_BLENDING);
643
depth_write_enabled = false;
644
} else {
645
label = "opaque_mesh2d_pipeline";
646
blend = None;
647
depth_write_enabled = true;
648
}
649
650
Ok(RenderPipelineDescriptor {
651
vertex: VertexState {
652
shader: self.shader.clone(),
653
shader_defs: shader_defs.clone(),
654
buffers: vec![vertex_buffer_layout],
655
..default()
656
},
657
fragment: Some(FragmentState {
658
shader: self.shader.clone(),
659
shader_defs,
660
targets: vec![Some(ColorTargetState {
661
format,
662
blend,
663
write_mask: ColorWrites::ALL,
664
})],
665
..default()
666
}),
667
layout: vec![self.view_layout.clone(), self.mesh_layout.clone()],
668
primitive: PrimitiveState {
669
front_face: FrontFace::Ccw,
670
cull_mode: None,
671
unclipped_depth: false,
672
polygon_mode: PolygonMode::Fill,
673
conservative: false,
674
topology: key.primitive_topology(),
675
strip_index_format: None,
676
},
677
depth_stencil: Some(DepthStencilState {
678
format: CORE_2D_DEPTH_FORMAT,
679
depth_write_enabled,
680
depth_compare: CompareFunction::GreaterEqual,
681
stencil: StencilState {
682
front: StencilFaceState::IGNORE,
683
back: StencilFaceState::IGNORE,
684
read_mask: 0,
685
write_mask: 0,
686
},
687
bias: DepthBiasState {
688
constant: 0,
689
slope_scale: 0.0,
690
clamp: 0.0,
691
},
692
}),
693
multisample: MultisampleState {
694
count: key.msaa_samples(),
695
mask: !0,
696
alpha_to_coverage_enabled: false,
697
},
698
label: Some(label.into()),
699
..default()
700
})
701
}
702
}
703
704
#[derive(Resource)]
705
pub struct Mesh2dBindGroup {
706
pub value: BindGroup,
707
}
708
709
pub fn prepare_mesh2d_bind_group(
710
mut commands: Commands,
711
mesh2d_pipeline: Res<Mesh2dPipeline>,
712
render_device: Res<RenderDevice>,
713
mesh2d_uniforms: Res<BatchedInstanceBuffer<Mesh2dUniform>>,
714
) {
715
if let Some(binding) = mesh2d_uniforms.instance_data_binding() {
716
commands.insert_resource(Mesh2dBindGroup {
717
value: render_device.create_bind_group(
718
"mesh2d_bind_group",
719
&mesh2d_pipeline.mesh_layout,
720
&BindGroupEntries::single(binding),
721
),
722
});
723
}
724
}
725
726
#[derive(Component)]
727
pub struct Mesh2dViewBindGroup {
728
pub value: BindGroup,
729
}
730
731
pub fn prepare_mesh2d_view_bind_groups(
732
mut commands: Commands,
733
render_device: Res<RenderDevice>,
734
mesh2d_pipeline: Res<Mesh2dPipeline>,
735
view_uniforms: Res<ViewUniforms>,
736
views: Query<(Entity, &Tonemapping), (With<ExtractedView>, With<Camera2d>)>,
737
globals_buffer: Res<GlobalsBuffer>,
738
tonemapping_luts: Res<TonemappingLuts>,
739
images: Res<RenderAssets<GpuImage>>,
740
fallback_image: Res<FallbackImage>,
741
) {
742
let (Some(view_binding), Some(globals)) = (
743
view_uniforms.uniforms.binding(),
744
globals_buffer.buffer.binding(),
745
) else {
746
return;
747
};
748
749
for (entity, tonemapping) in &views {
750
let lut_bindings =
751
get_lut_bindings(&images, &tonemapping_luts, tonemapping, &fallback_image);
752
let view_bind_group = render_device.create_bind_group(
753
"mesh2d_view_bind_group",
754
&mesh2d_pipeline.view_layout,
755
&BindGroupEntries::sequential((
756
view_binding.clone(),
757
globals.clone(),
758
lut_bindings.0,
759
lut_bindings.1,
760
)),
761
);
762
763
commands.entity(entity).insert(Mesh2dViewBindGroup {
764
value: view_bind_group,
765
});
766
}
767
}
768
769
pub struct SetMesh2dViewBindGroup<const I: usize>;
770
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMesh2dViewBindGroup<I> {
771
type Param = ();
772
type ViewQuery = (Read<ViewUniformOffset>, Read<Mesh2dViewBindGroup>);
773
type ItemQuery = ();
774
775
#[inline]
776
fn render<'w>(
777
_item: &P,
778
(view_uniform, mesh2d_view_bind_group): ROQueryItem<'w, '_, Self::ViewQuery>,
779
_view: Option<()>,
780
_param: SystemParamItem<'w, '_, Self::Param>,
781
pass: &mut TrackedRenderPass<'w>,
782
) -> RenderCommandResult {
783
pass.set_bind_group(I, &mesh2d_view_bind_group.value, &[view_uniform.offset]);
784
785
RenderCommandResult::Success
786
}
787
}
788
789
pub struct SetMesh2dBindGroup<const I: usize>;
790
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMesh2dBindGroup<I> {
791
type Param = SRes<Mesh2dBindGroup>;
792
type ViewQuery = ();
793
type ItemQuery = ();
794
795
#[inline]
796
fn render<'w>(
797
item: &P,
798
_view: (),
799
_item_query: Option<()>,
800
mesh2d_bind_group: SystemParamItem<'w, '_, Self::Param>,
801
pass: &mut TrackedRenderPass<'w>,
802
) -> RenderCommandResult {
803
let mut dynamic_offsets: [u32; 1] = Default::default();
804
let mut offset_count = 0;
805
if let PhaseItemExtraIndex::DynamicOffset(dynamic_offset) = item.extra_index() {
806
dynamic_offsets[offset_count] = dynamic_offset;
807
offset_count += 1;
808
}
809
pass.set_bind_group(
810
I,
811
&mesh2d_bind_group.into_inner().value,
812
&dynamic_offsets[..offset_count],
813
);
814
RenderCommandResult::Success
815
}
816
}
817
818
pub struct DrawMesh2d;
819
impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
820
type Param = (
821
SRes<RenderAssets<RenderMesh>>,
822
SRes<RenderMesh2dInstances>,
823
SRes<MeshAllocator>,
824
);
825
type ViewQuery = ();
826
type ItemQuery = ();
827
828
#[inline]
829
fn render<'w>(
830
item: &P,
831
_view: (),
832
_item_query: Option<()>,
833
(meshes, render_mesh2d_instances, mesh_allocator): SystemParamItem<'w, '_, Self::Param>,
834
pass: &mut TrackedRenderPass<'w>,
835
) -> RenderCommandResult {
836
let meshes = meshes.into_inner();
837
let render_mesh2d_instances = render_mesh2d_instances.into_inner();
838
let mesh_allocator = mesh_allocator.into_inner();
839
840
let Some(RenderMesh2dInstance { mesh_asset_id, .. }) =
841
render_mesh2d_instances.get(&item.main_entity())
842
else {
843
return RenderCommandResult::Skip;
844
};
845
let Some(gpu_mesh) = meshes.get(*mesh_asset_id) else {
846
return RenderCommandResult::Skip;
847
};
848
let Some(vertex_buffer_slice) = mesh_allocator.mesh_vertex_slice(mesh_asset_id) else {
849
return RenderCommandResult::Skip;
850
};
851
852
pass.set_vertex_buffer(0, vertex_buffer_slice.buffer.slice(..));
853
854
let batch_range = item.batch_range();
855
match &gpu_mesh.buffer_info {
856
RenderMeshBufferInfo::Indexed {
857
index_format,
858
count,
859
} => {
860
let Some(index_buffer_slice) = mesh_allocator.mesh_index_slice(mesh_asset_id)
861
else {
862
return RenderCommandResult::Skip;
863
};
864
865
pass.set_index_buffer(index_buffer_slice.buffer.slice(..), 0, *index_format);
866
867
pass.draw_indexed(
868
index_buffer_slice.range.start..(index_buffer_slice.range.start + count),
869
vertex_buffer_slice.range.start as i32,
870
batch_range.clone(),
871
);
872
}
873
RenderMeshBufferInfo::NonIndexed => {
874
pass.draw(vertex_buffer_slice.range, batch_range.clone());
875
}
876
}
877
RenderCommandResult::Success
878
}
879
}
880
881