anchor_kit_wgpu/
lib.rs

1use std::collections::HashMap;
2
3use anchor_kit_core::{
4    primitives::rectangle::Rectangle,
5    render::RenderList,
6    style::{FontFamily, FontStyle, FontWeight},
7};
8use glyphon::{
9    Attrs, Cache, FontSystem, Metrics, Shaping, SwashCache, TextArea, TextAtlas, TextBounds,
10    TextRenderer, Viewport,
11};
12use image::GenericImageView;
13use uuid::Uuid;
14use wgpu::include_wgsl;
15
16pub struct ScreenInfo {
17    pub size_px: [u32; 2], // w, h
18    pub scale_factor: f32, // used by glyphon to scale text
19}
20
21#[repr(C)]
22#[derive(Copy, Clone, Debug, bytemuck::NoUninit)] // TODO: do we need to go back to bytemuck POD/ zeroable here instead?
23struct Vertex {
24    position: [f32; 2],         // x, y (normalized)
25    local_uv: [f32; 2],         // uv in local units (inside the object)
26    background_color: [f32; 4], // r, g, b, a
27    border_radius: [f32; 4],    // top-left, top-right, bottom-right, bottom-left (clockwise)
28    border_width: f32,
29    border_color: [f32; 4], // r, g, b, a
30    scale: [f32; 2],        // scale x,y to w,h
31}
32
33impl Vertex {
34    const ATTRIBS: [wgpu::VertexAttribute; 7] = wgpu::vertex_attr_array![
35        0 => Float32x2, // location 0 is normalized position
36        1 => Float32x2, // location 1 is uv in local units within the object
37        2 => Float32x4, // location 2 is colour
38        3 => Float32x4, // location 3 is border radius (also in local units)
39        4 => Float32, // location 4 is border width (also in local units)
40        5 => Float32x4, // location 5 is boder colour
41        6 => Float32x2, // location 6 is the scale
42    ];
43
44    fn capacity_to_bytes(capacity: usize) -> wgpu::BufferAddress {
45        (capacity * std::mem::size_of::<Self>()) as wgpu::BufferAddress
46    }
47
48    fn desc() -> wgpu::VertexBufferLayout<'static> {
49        wgpu::VertexBufferLayout {
50            array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress,
51            step_mode: wgpu::VertexStepMode::Vertex,
52            attributes: &Self::ATTRIBS,
53        }
54    }
55}
56
57fn get_vertex_buffer(device: &wgpu::Device, capacity_bytes: wgpu::BufferAddress) -> wgpu::Buffer {
58    device.create_buffer(&wgpu::BufferDescriptor {
59        label: Some("anchor-kit vertex buffer"),
60        size: capacity_bytes,
61        usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
62        mapped_at_creation: false,
63    })
64}
65
66fn get_index_buffer(device: &wgpu::Device, capacity_bytes: wgpu::BufferAddress) -> wgpu::Buffer {
67    device.create_buffer(&wgpu::BufferDescriptor {
68        label: Some("anchor-kit index buffer"),
69        size: capacity_bytes,
70        usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
71        mapped_at_creation: false,
72    })
73}
74
75// rectangles are made up of 4 vertices and 6 indices
76// v0--v1
77// |  /|
78// | / |
79// |/  |
80// v3--v2
81fn get_vertices_and_indices_for_rectangle(
82    rect: &Rectangle,
83    screen_info: &ScreenInfo,
84    vertex_offset: u32,
85) -> ([Vertex; 4], [u32; 6]) {
86    let [x, y] = rect.position;
87    let [w, h] = rect.size;
88    let scale_axis = w.min(h) as f32;
89    let scale = if scale_axis <= 0.0 {
90        [1.0, 1.0]
91    } else {
92        [(w as f32) / scale_axis, (h as f32) / scale_axis]
93    };
94    let [screen_w, screen_h] = screen_info.size_px;
95
96    // normalize pixel values
97    let x0 = x as f32 / screen_w as f32;
98    let x1 = (x + w) as f32 / screen_w as f32;
99    let y0 = y as f32 / screen_h as f32;
100    let y1 = (y + h) as f32 / screen_h as f32;
101
102    let background_color = rect.style.background_color.to_rgba_f32();
103    let border_color = rect.style.border_color.to_rgba_f32();
104
105    // convert radius to local units
106    let mut local_radius = rect.style.border_radius;
107    for r in local_radius.iter_mut() {
108        *r = (*r / w.min(h) as f32).min(0.5) // we don't want the radius exceeding 0.5 to avoid impossible rounded corners
109    }
110    let local_border_width = rect.style.border_width / w.min(h) as f32;
111
112    // for the vertices the local uv values are just the corners
113    let v0 = Vertex {
114        position: [x0, y0],
115        local_uv: [0.0, 0.0],
116        background_color,
117        border_radius: local_radius,
118        border_width: local_border_width,
119        border_color,
120        scale,
121    };
122    let v1 = Vertex {
123        position: [x1, y0],
124        local_uv: [1.0, 0.0],
125        background_color,
126        border_radius: local_radius,
127        border_width: local_border_width,
128        border_color,
129        scale,
130    };
131    let v2 = Vertex {
132        position: [x1, y1],
133        local_uv: [1.0, 1.0],
134        background_color,
135        border_radius: local_radius,
136        border_width: local_border_width,
137        border_color,
138        scale,
139    };
140    let v3 = Vertex {
141        position: [x0, y1],
142        local_uv: [0.0, 1.0],
143        background_color,
144        border_radius: local_radius,
145        border_width: local_border_width,
146        border_color,
147        scale,
148    };
149
150    let vertices = [v0, v1, v2, v3];
151
152    // triangles are v0 -> v2 -> v1, and v0 -> v3 -> v2. (have to go in ccw order)
153    let indices = [
154        vertex_offset,
155        vertex_offset + 2,
156        vertex_offset + 1,
157        vertex_offset,
158        vertex_offset + 3,
159        vertex_offset + 2,
160    ];
161
162    (vertices, indices)
163}
164
165struct GlyphonRenderer {
166    font_system: FontSystem,
167    swash_cache: SwashCache,
168    viewport: Viewport,
169    atlas: TextAtlas,
170    text_renderer: TextRenderer,
171}
172
173impl GlyphonRenderer {
174    pub fn new(
175        device: &wgpu::Device,
176        queue: &wgpu::Queue,
177        texture_format: wgpu::TextureFormat,
178    ) -> Self {
179        // set up glyphon text rendering (inspired by: https://github.com/grovesNL/glyphon/blob/main/examples/hello-world.rs)
180        let font_system = FontSystem::new();
181        let swash_cache = SwashCache::new();
182        let glyphon_cache = Cache::new(device);
183        let viewport = Viewport::new(device, &glyphon_cache);
184        let mut atlas = TextAtlas::new(device, queue, &glyphon_cache, texture_format);
185        let text_renderer =
186            TextRenderer::new(&mut atlas, device, wgpu::MultisampleState::default(), None);
187
188        GlyphonRenderer {
189            font_system,
190            swash_cache,
191            viewport,
192            atlas,
193            text_renderer,
194        }
195    }
196
197    pub fn render_text(
198        &mut self,
199        device: &wgpu::Device,
200        queue: &wgpu::Queue,
201        render_pass: &mut wgpu::RenderPass<'_>,
202        screen_info: &ScreenInfo,
203        render_list: &RenderList,
204    ) {
205        // skip rendering if no text is requested
206        if render_list.text.is_empty() {
207            return;
208        }
209
210        let [screen_w, screen_h] = screen_info.size_px;
211
212        self.viewport.update(
213            queue,
214            glyphon::Resolution {
215                width: screen_w,
216                height: screen_h,
217            },
218        );
219
220        let physical_width = screen_w as f32 * screen_info.scale_factor;
221        let physical_height = screen_h as f32 * screen_info.scale_factor;
222
223        // each text item is rendered to its own `TextArea` in glyphon
224        let mut text_areas: Vec<TextArea> = Vec::with_capacity(render_list.text.len());
225
226        // we need to store the buffers so they have the same lifetime as the areas (areas use the buffers)
227        let mut text_buffers: Vec<glyphon::Buffer> = Vec::with_capacity(render_list.text.len());
228
229        for text_item in &render_list.text {
230            let text_style = &text_item.text_style;
231
232            // TODO: metrics should be set by text style passed in by user
233            let mut text_buffer = glyphon::Buffer::new(
234                &mut self.font_system,
235                Metrics::new(text_style.font_size, text_style.line_height),
236            );
237
238            text_buffer.set_size(
239                &mut self.font_system,
240                Some(physical_width),
241                Some(physical_height),
242            );
243
244            let text_color = glyphon::Color::rgba(
245                text_item.text_style.text_color.r,
246                text_item.text_style.text_color.g,
247                text_item.text_style.text_color.b,
248                text_item.text_style.text_color.a,
249            );
250
251            let text_attrs = Attrs::new()
252                .family(Self::anchor_kit_font_family_to_glyphon(
253                    &text_style.font_family,
254                ))
255                .style(Self::anchor_kit_font_style_to_glyphon(
256                    &text_style.font_style,
257                ))
258                .weight(Self::anchor_kit_font_weight_to_glyphon(
259                    &text_style.font_weight,
260                ))
261                .color(text_color);
262
263            text_buffer.set_text(
264                &mut self.font_system,
265                &text_item.text,
266                &text_attrs,
267                Shaping::Advanced,
268            );
269
270            // TODO: should we also set things like text wrap (should this be set in style)?
271            text_buffer.shape_until_scroll(&mut self.font_system, false);
272
273            text_buffers.push(text_buffer);
274        }
275
276        for (i, text_buffer) in text_buffers.iter().enumerate() {
277            let text_item = &render_list.text[i];
278
279            let [x, y] = text_item.position;
280            let [w, h] = text_item.size;
281            let text_bounds = TextBounds {
282                left: x as i32,
283                top: y as i32,
284                right: (x + w) as i32,
285                bottom: (y + h) as i32,
286            };
287
288            let text_color = glyphon::Color::rgba(
289                text_item.text_style.text_color.r,
290                text_item.text_style.text_color.g,
291                text_item.text_style.text_color.b,
292                text_item.text_style.text_color.a,
293            );
294
295            text_areas.push(TextArea {
296                buffer: &text_buffer,
297                left: x as f32,
298                top: y as f32,
299                scale: 1.0, // ignore screen scale factor (TODO: investigate if we want to include this later)
300                bounds: text_bounds,
301                default_color: text_color,
302                custom_glyphs: &[],
303            });
304        }
305
306        if let Err(err) = self.text_renderer.prepare(
307            device,
308            queue,
309            &mut self.font_system,
310            &mut self.atlas,
311            &self.viewport,
312            text_areas,
313            &mut self.swash_cache,
314        ) {
315            // TODO: add better error handling
316            println!("error with glyphon text prepare: {:?}", err);
317            return;
318        }
319
320        if let Err(err) = self
321            .text_renderer
322            .render(&mut self.atlas, &self.viewport, render_pass)
323        {
324            // TODO: add better error handling
325            println!("error with glyphon text render: {:?}", err);
326            return;
327        }
328
329        self.atlas.trim();
330    }
331
332    fn anchor_kit_font_family_to_glyphon(font_family: &FontFamily) -> glyphon::Family<'_> {
333        match font_family {
334            FontFamily::Name(name) => glyphon::Family::Name(name),
335            FontFamily::Serif => glyphon::Family::Serif,
336            FontFamily::SansSerif => glyphon::Family::SansSerif,
337            FontFamily::Cursive => glyphon::Family::Cursive,
338            FontFamily::Fantasy => glyphon::Family::Fantasy,
339            FontFamily::Monospace => glyphon::Family::Monospace,
340        }
341    }
342
343    fn anchor_kit_font_weight_to_glyphon(font_weight: &FontWeight) -> glyphon::Weight {
344        match font_weight {
345            FontWeight::Thin => glyphon::Weight::THIN,
346            FontWeight::ExtraLight => glyphon::Weight::EXTRA_LIGHT,
347            FontWeight::Light => glyphon::Weight::LIGHT,
348            FontWeight::Normal => glyphon::Weight::NORMAL,
349            FontWeight::Medium => glyphon::Weight::MEDIUM,
350            FontWeight::SemiBold => glyphon::Weight::SEMIBOLD,
351            FontWeight::Bold => glyphon::Weight::BOLD,
352            FontWeight::ExtraBold => glyphon::Weight::EXTRA_BOLD,
353            FontWeight::Black => glyphon::Weight::BLACK,
354        }
355    }
356
357    fn anchor_kit_font_style_to_glyphon(font_style: &FontStyle) -> glyphon::Style {
358        match font_style {
359            FontStyle::Normal => glyphon::Style::Normal,
360            FontStyle::Italic => glyphon::Style::Italic,
361            FontStyle::Oblique => glyphon::Style::Oblique,
362        }
363    }
364}
365
366pub struct Renderer {
367    main_pipeline: wgpu::RenderPipeline,
368    image_pipeline: wgpu::RenderPipeline, // we need a new pipeline for iamges because we have to pass bind groups to the fragment shader
369    vertex_buffer: wgpu::Buffer,
370    vertex_buffer_capacity: usize,
371    index_buffer: wgpu::Buffer,
372    index_buffer_capacity: usize,
373    glyphon_renderer: GlyphonRenderer,
374    bind_groups: HashMap<Uuid, wgpu::BindGroup>, // we want to store a map of ids to texture bind groups so we don't have to regenerate them each frame
375    texture_bind_group_layout: wgpu::BindGroupLayout, // we only need one bind group layout for all textures
376}
377
378impl Renderer {
379    pub fn new(
380        device: &wgpu::Device,
381        queue: &wgpu::Queue,
382        texture_format: wgpu::TextureFormat,
383    ) -> Self {
384        // inspired by: https://sotrh.github.io/learn-wgpu/beginner/tutorial3-pipeline/#how-do-we-use-the-shaders
385        let shader = device.create_shader_module(include_wgsl!("shader.wgsl"));
386
387        let initial_vertex_buffer_capacity = 1024; // reasonable estiamte for small scale applications so we don't have to resize right away
388        let vertex_buffer = get_vertex_buffer(
389            device,
390            Vertex::capacity_to_bytes(initial_vertex_buffer_capacity),
391        );
392
393        // we want to use indexed rendering for better performance
394        let initial_index_buffer_capacity = 2048;
395        let index_buffer = get_index_buffer(
396            device,
397            (initial_index_buffer_capacity * std::mem::size_of::<u32>()) as wgpu::BufferAddress, // use u32 for size of index
398        );
399
400        let main_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
401            label: Some("anchor-kit main layout"),
402            bind_group_layouts: &[],
403            push_constant_ranges: &[],
404        });
405
406        let main_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
407            label: Some("anchor-kit render pipeline"),
408            layout: Some(&main_pipeline_layout),
409            vertex: wgpu::VertexState {
410                module: &shader,
411                entry_point: Some("vs_main"),
412                buffers: &[Vertex::desc()], // get the buffer layout description from the vertex impl
413                compilation_options: wgpu::PipelineCompilationOptions::default(),
414            },
415            fragment: Some(wgpu::FragmentState {
416                module: &shader,
417                entry_point: Some("fs_main"),
418                targets: &[Some(wgpu::ColorTargetState {
419                    format: texture_format,
420                    blend: Some(wgpu::BlendState::ALPHA_BLENDING),
421                    write_mask: wgpu::ColorWrites::ALL,
422                })],
423                compilation_options: wgpu::PipelineCompilationOptions::default(),
424            }),
425            primitive: wgpu::PrimitiveState {
426                topology: wgpu::PrimitiveTopology::TriangleList,
427                strip_index_format: None,
428                front_face: wgpu::FrontFace::Ccw,
429                cull_mode: Some(wgpu::Face::Back),
430                polygon_mode: wgpu::PolygonMode::Fill,
431                unclipped_depth: false,
432                conservative: false,
433            },
434            depth_stencil: None,
435            multisample: wgpu::MultisampleState {
436                count: 1,
437                mask: !0,
438                alpha_to_coverage_enabled: false,
439            },
440            multiview: None,
441            cache: None,
442        });
443
444        let texture_bind_group_layout =
445            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
446                entries: &[
447                    wgpu::BindGroupLayoutEntry {
448                        binding: 0,
449                        visibility: wgpu::ShaderStages::FRAGMENT,
450                        ty: wgpu::BindingType::Texture {
451                            multisampled: false,
452                            view_dimension: wgpu::TextureViewDimension::D2,
453                            sample_type: wgpu::TextureSampleType::Float { filterable: true },
454                        },
455                        count: None,
456                    },
457                    wgpu::BindGroupLayoutEntry {
458                        binding: 1,
459                        visibility: wgpu::ShaderStages::FRAGMENT,
460                        ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
461                        count: None,
462                    },
463                ],
464                label: Some("anchor-kit bindgroup layout"),
465            });
466
467        let image_pipeline_layout =
468            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
469                label: Some("anchor-kit image pipeline layout"),
470                bind_group_layouts: &[&texture_bind_group_layout], // image pipeline layout needs the texture bind group layout
471                push_constant_ranges: &[],
472            });
473
474        // create seperate image pipeline for rendering images (using textures)
475        let image_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
476            label: Some("anchor-kit render pipeline"),
477            layout: Some(&image_pipeline_layout),
478            vertex: wgpu::VertexState {
479                module: &shader,
480                entry_point: Some("vs_main"),
481                buffers: &[Vertex::desc()], // get the buffer layout description from the vertex impl
482                compilation_options: wgpu::PipelineCompilationOptions::default(),
483            },
484            fragment: Some(wgpu::FragmentState {
485                module: &shader,
486                entry_point: Some("fs_image"), // use the image fragment shader
487                targets: &[Some(wgpu::ColorTargetState {
488                    format: texture_format,
489                    blend: Some(wgpu::BlendState::ALPHA_BLENDING),
490                    write_mask: wgpu::ColorWrites::ALL,
491                })],
492                compilation_options: wgpu::PipelineCompilationOptions::default(),
493            }),
494            primitive: wgpu::PrimitiveState {
495                topology: wgpu::PrimitiveTopology::TriangleList,
496                strip_index_format: None,
497                front_face: wgpu::FrontFace::Ccw,
498                cull_mode: Some(wgpu::Face::Back),
499                polygon_mode: wgpu::PolygonMode::Fill,
500                unclipped_depth: false,
501                conservative: false,
502            },
503            depth_stencil: None,
504            multisample: wgpu::MultisampleState {
505                count: 1,
506                mask: !0,
507                alpha_to_coverage_enabled: false,
508            },
509            multiview: None,
510            cache: None,
511        });
512
513        Renderer {
514            main_pipeline,
515            image_pipeline,
516            vertex_buffer,
517            vertex_buffer_capacity: initial_vertex_buffer_capacity as usize,
518            index_buffer,
519            index_buffer_capacity: initial_index_buffer_capacity as usize,
520            glyphon_renderer: GlyphonRenderer::new(device, queue, texture_format),
521            bind_groups: HashMap::new(),
522            texture_bind_group_layout,
523        }
524    }
525
526    pub fn render(
527        &mut self,
528        device: &wgpu::Device,
529        queue: &wgpu::Queue,
530        render_pass: &mut wgpu::RenderPass<'_>,
531        screen_info: &ScreenInfo,
532        render_list: &RenderList,
533    ) {
534        // main pipeline rendering
535        let mut vertices: Vec<Vertex> = vec![];
536        let mut indices: Vec<u32> = vec![];
537
538        // convert all primatives to vertices
539        let main_pipeline_index_offset = indices.len();
540        for rect in &render_list.rectangles {
541            // offset will increment as new vertices are added
542            let (new_vertices, new_indices) =
543                get_vertices_and_indices_for_rectangle(rect, screen_info, vertices.len() as u32);
544            vertices.extend_from_slice(&new_vertices);
545            indices.extend_from_slice(&new_indices);
546        }
547        let main_pipeline_index_count = indices.len();
548
549        // we will keep track of image draws seperatly so that we can use the correct bind gorups later for the texture rendering
550        struct ImageDraw {
551            texture_id: Uuid,
552            index_offset: usize, // where this image starts in the list of shared indices
553            index_count: usize,
554        }
555        let mut image_draws: Vec<ImageDraw> = vec![];
556
557        for image in &render_list.images {
558            let image_index_offset = indices.len();
559
560            let (new_vertices, new_indices) = get_vertices_and_indices_for_rectangle(
561                &image.rectangle,
562                screen_info,
563                vertices.len() as u32,
564            );
565            vertices.extend_from_slice(&new_vertices);
566            indices.extend_from_slice(&new_indices);
567
568            image_draws.push(ImageDraw {
569                texture_id: image.texture_id,
570                index_offset: image_index_offset,
571                index_count: new_indices.len(),
572            })
573        }
574
575        // make sure there is enough capcity on the gpu
576        self.resize_vertex_buffer_if_required(device, vertices.len());
577        self.resize_index_buffer_if_required(device, indices.len());
578
579        // write data to the queue
580        queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&vertices));
581        queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&indices));
582
583        render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
584        render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
585
586        // set data to be rendered (for main pipeline (for rectangles))
587        render_pass.set_pipeline(&self.main_pipeline);
588        render_pass.draw_indexed(
589            main_pipeline_index_offset as u32..main_pipeline_index_count as u32, // use the main pipeline index count/ offset
590            0,
591            0..1,
592        );
593
594        // draw the images using the image pipeline (individual draws for each since they could have different textures)
595        render_pass.set_pipeline(&self.image_pipeline);
596        for image_draw in image_draws.iter() {
597            if let Some(bind_group) = self.bind_groups.get(&image_draw.texture_id) {
598                render_pass.set_bind_group(0, bind_group, &[]);
599                render_pass.draw_indexed(
600                    image_draw.index_offset as u32
601                        ..(image_draw.index_offset + image_draw.index_count) as u32, // use the specific image index count/ offset
602                    0,
603                    0..1,
604                );
605            }
606        }
607
608        // handle text rendering with glyphon
609        self.glyphon_renderer
610            .render_text(device, queue, render_pass, screen_info, render_list);
611    }
612
613    // image/ texture rendering inspired by: https://sotrh.github.io/learn-wgpu/beginner/tutorial5-textures/#the-bindgroup
614    pub fn get_image_id_from_bytes(
615        &mut self,
616        device: &wgpu::Device,
617        queue: &wgpu::Queue,
618        diffuse_bytes: &[u8],
619    ) -> Uuid {
620        let diffuse_image = image::load_from_memory(diffuse_bytes).unwrap();
621        let diffuse_rgba = diffuse_image.to_rgba8();
622        let dimensions = diffuse_image.dimensions();
623
624        // create the texture
625        let texture_size = wgpu::Extent3d {
626            width: dimensions.0,
627            height: dimensions.1,
628            depth_or_array_layers: 1, // we keep depth as 1 for 2d images
629        };
630
631        let diffuse_texture = device.create_texture(&wgpu::TextureDescriptor {
632            size: texture_size,
633            mip_level_count: 1,
634            sample_count: 1,
635            dimension: wgpu::TextureDimension::D2,
636            format: wgpu::TextureFormat::Rgba8UnormSrgb,
637            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
638            label: Some("anchor-kit diffuse texture"),
639            view_formats: &[],
640        });
641
642        queue.write_texture(
643            wgpu::TexelCopyTextureInfo {
644                texture: &diffuse_texture,
645                mip_level: 0,
646                origin: wgpu::Origin3d::ZERO,
647                aspect: wgpu::TextureAspect::All,
648            },
649            &diffuse_rgba,
650            wgpu::TexelCopyBufferLayout {
651                offset: 0,
652                bytes_per_row: Some(4 * dimensions.0),
653                rows_per_image: Some(dimensions.1),
654            },
655            texture_size,
656        );
657
658        let diffuse_texture_view =
659            diffuse_texture.create_view(&wgpu::TextureViewDescriptor::default());
660
661        let diffuse_sampler = device.create_sampler(&wgpu::SamplerDescriptor {
662            address_mode_u: wgpu::AddressMode::ClampToEdge,
663            address_mode_v: wgpu::AddressMode::ClampToEdge,
664            address_mode_w: wgpu::AddressMode::ClampToEdge,
665            mag_filter: wgpu::FilterMode::Linear,
666            min_filter: wgpu::FilterMode::Linear,
667            mipmap_filter: wgpu::FilterMode::Linear,
668            ..Default::default()
669        });
670
671        let diffuse_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
672            layout: &self.texture_bind_group_layout, // the layout is created in the new function (single layout for all texture bind groups)
673            entries: &[
674                wgpu::BindGroupEntry {
675                    binding: 0,
676                    resource: wgpu::BindingResource::TextureView(&diffuse_texture_view),
677                },
678                wgpu::BindGroupEntry {
679                    binding: 1,
680                    resource: wgpu::BindingResource::Sampler(&diffuse_sampler),
681                },
682            ],
683            label: Some("anchor-kit diffuse bind group"),
684        });
685
686        // generate the unique texture id so we can access it later without regenerating
687        let id = Uuid::new_v4();
688        self.bind_groups.insert(id, diffuse_bind_group);
689        id // return the id so the user can use it to render images in the generate frame pass
690    }
691
692    fn resize_vertex_buffer_if_required(
693        &mut self,
694        device: &wgpu::Device,
695        num_requested_vertices: usize,
696    ) {
697        if num_requested_vertices <= self.vertex_buffer_capacity {
698            return;
699        }
700        let new_size = num_requested_vertices.next_power_of_two(); // we should grow exponentially in this case to avoid more resizes in the future
701        self.vertex_buffer = get_vertex_buffer(device, Vertex::capacity_to_bytes(new_size));
702        self.vertex_buffer_capacity = new_size;
703    }
704
705    fn resize_index_buffer_if_required(
706        &mut self,
707        device: &wgpu::Device,
708        num_requested_indices: usize,
709    ) {
710        if num_requested_indices <= self.index_buffer_capacity {
711            return;
712        }
713        let new_size = num_requested_indices.next_power_of_two();
714        self.index_buffer = get_index_buffer(
715            device,
716            (new_size * std::mem::size_of::<u32>()) as wgpu::BufferAddress, // u32 for size of index
717        );
718        self.index_buffer_capacity = new_size;
719    }
720}