gust_render/
spritebatch.rs

1//  Rust file | 2018
2//  Author: Alexandre Fourcat
3//  spritebatch.rs
4//  module:
5//! spritebatch test system
6
7use super::Vector;
8use color::Color;
9use draw::*;
10use gl;
11use gl::types::*;
12use nalgebra::{Matrix4, Vector3};
13use nalgebra::{Scalar, Vector4};
14use rect::Rect;
15use shader::BATCH_SHADER;
16use std::mem;
17use std::ptr;
18use std::rc::Rc;
19use texture::Texture;
20use transform::*;
21use vertex::Vertex;
22
23pub enum BatchError {
24    BadTextureRect,
25}
26
27#[derive(Debug, Clone)]
28/// SpriteData is a structure representing transformation on a texture to become a sprite.
29pub struct SpriteData {
30    pos: Vector<f32>,
31    rotation: f32,
32    model: Matrix4<f32>,
33    need_update: bool,
34    text_coord: [Vector<f32>; 2],
35    color: Option<Color>,
36}
37
38impl SpriteData {
39    /// Create a new SpriteData needed by SpriteBatch
40    pub fn new(pos: Vector<f32>) -> Self {
41        SpriteData {
42            pos,
43            ..Self::default()
44        }
45    }
46
47    /// Set texture_coord Raw (gl like)
48    pub fn set_texture_raw(&mut self, text_coord: [Vector<f32>; 2]) {
49        self.text_coord = text_coord;
50    }
51
52    /// Set texture rect.
53    pub fn set_texture_rect(&mut self, text_rect: Rect<u32>, texture_size: u32) {
54        self.text_coord = [
55            Vector::new(
56                text_rect.left as f32 / texture_size as f32,
57                text_rect.top as f32 / texture_size as f32,
58            ),
59            Vector::new(
60                text_rect.width as f32 / texture_size as f32,
61                text_rect.height as f32 / texture_size as f32,
62            ),
63        ];
64    }
65
66    /// Get texture rect.
67    pub fn texture_rect<T>(&self, texture_size: T) -> Rect<u32>
68    where
69        T: Into<f32> + Copy,
70    {
71        Rect::new(
72            (self.text_coord[0].x * texture_size.into()) as u32,
73            (self.text_coord[0].y * texture_size.into()) as u32,
74            (self.text_coord[1].x * texture_size.into()) as u32,
75            (self.text_coord[1].y * texture_size.into()) as u32,
76        )
77    }
78}
79
80impl Default for SpriteData {
81    fn default() -> SpriteData {
82        SpriteData {
83            pos: Vector::new(0.0, 0.0),
84            rotation: 0.0,
85            model: Matrix4::identity(),
86            need_update: true,
87            text_coord: [Vector::new(0.0, 0.0), Vector::new(1.0, 1.0)],
88            color: None,
89        }
90    }
91}
92
93impl Transformable for SpriteData {
94    fn contain<T>(&self, _vec: ::Point<T>) -> bool
95    where
96        T: Scalar + Into<f32>,
97    {
98        true
99    }
100
101    fn set_origin<T>(&mut self, _origin: Vector<T>)
102    where
103        T: Scalar + Into<f32>,
104    {
105        unimplemented!("No origin for now in SpriteData.");
106    }
107
108    fn get_origin(&self) -> Vector<f32> {
109        unimplemented!("No origin for now in SpriteData.");
110    }
111}
112
113impl Scalable for SpriteData {
114    fn set_scale<T>(&mut self, _vec: Vector<T>)
115    where
116        T: Scalar + Into<f32>,
117    {
118        unimplemented!("For instance no scale to SpriteData.");
119    }
120
121    fn get_scale(&self) -> Vector<f32> {
122        unimplemented!("For instance no scale to SpriteData.");
123    }
124
125    fn scale<T>(&mut self, _factor: Vector<T>)
126    where
127        T: Scalar + Into<f32>,
128    {
129        unimplemented!("For instance no scale to SpriteData.");
130    }
131}
132
133impl Rotable for SpriteData {
134    fn rotate<T>(&mut self, angle: T)
135    where
136        T: Into<f32> + Scalar,
137    {
138        self.rotation += angle.into();
139        self.need_update = true;
140    }
141
142    fn set_rotation<T>(&mut self, angle: T)
143    where
144        T: Into<f32> + Scalar,
145    {
146        self.rotation = angle.into();
147        self.need_update = true;
148    }
149
150    fn get_rotation(&self) -> f32 {
151        self.rotation
152    }
153}
154
155impl Movable for SpriteData {
156    fn translate<T>(&mut self, vec: Vector<T>)
157    where
158        T: Into<f32> + Scalar,
159    {
160        self.pos.x += vec.x.into();
161        self.pos.y += vec.y.into();
162        self.need_update = true;
163    }
164
165    fn get_position(&self) -> Vector<f32> {
166        self.pos
167    }
168
169    fn set_position<T>(&mut self, vec: Vector<T>)
170    where
171        T: Into<f32> + Scalar,
172    {
173        self.pos.x = vec.x.into();
174        self.pos.y = vec.y.into();
175        self.need_update = true;
176    }
177}
178
179#[derive(Clone, Debug)]
180/// SpriteBatch is a datastructure that handle all sprites that have the same texture.
181/// And make only 1 drawCall to draw them all. this way you can highly optimise data sended to
182/// GPU.
183/// You should use it in system where there is a lot's of sprite that should be drawn with the same
184/// shaders and the same texture.
185/// SpriteBatch is a kind of collection that implement some iterator traits.
186/// ```no_run
187///     let texture = Rc::new(Texture::from_path("path_to_texture"));
188///     let mut batch = SpriteBatch::from(&texture);
189///
190///     SpriteData
191/// ```
192/// The idea behind SpriteBatch is to limit draw calls. Even if your sprites havn't the same texture
193/// can pack textures. And give your Vertex text_coord the actual texture coordinate that you want to be drawn.
194pub struct SpriteBatch {
195    texture: Option<Rc<Texture>>,
196    sprites: Vec<SpriteData>,
197    vertice: Vec<Vertex>,
198    gl_objects: (u32, u32),
199    glob_origin: Vector<f32>,
200    glob_pos: Vector<f32>,
201    glob_scale: Vector<f32>,
202    glob_rotation: f32,
203    len: usize,
204    need_update: bool,
205    model: Matrix4<f32>,
206}
207
208// For maximum efficiency we will not use the previously implemented abstraction of VertexBuffer
209impl SpriteBatch {
210    /// Create a new empty spriteBatch
211    pub fn new() -> SpriteBatch {
212        SpriteBatch::default()
213    }
214
215    pub fn extend_from_slice(&mut self, slice: &mut [SpriteData]) {
216        {
217            let vertice = &mut self.vertice;
218
219            let (w, h) = self
220                .texture
221                .as_ref()
222                .map_or((0.0, 0.0), |x| (x.width() as f32, x.height() as f32));
223
224            for x in slice.iter_mut() {
225                x.need_update = true;
226                vertice.extend_from_slice(&[
227                    Vertex::new(
228                        Vector::new(0.0, 0.0),
229                        Vector::new(x.text_coord[0].x, x.text_coord[0].y),
230                        x.color.unwrap_or(Color::white()),
231                    ),
232                    Vertex::new(
233                        Vector::new(0.0, h),
234                        Vector::new(x.text_coord[0].x, x.text_coord[1].y),
235                        x.color.unwrap_or(Color::white()),
236                    ),
237                    Vertex::new(
238                        Vector::new(w, 0.0),
239                        Vector::new(x.text_coord[1].x, x.text_coord[0].y),
240                        x.color.unwrap_or(Color::white()),
241                    ),
242                    Vertex::new(
243                        Vector::new(w, h),
244                        Vector::new(x.text_coord[1].x, x.text_coord[1].y),
245                        x.color.unwrap_or(Color::white()),
246                    ),
247                ]);
248            }
249        }
250        self.sprites.extend_from_slice(slice);
251        self.need_update = true;
252    }
253
254    /// Clear data.
255    pub fn clear(&mut self) {
256        self.sprites.clear();
257        self.vertice.clear();
258    }
259
260    /// Reserve data for vertice and sprites.
261    pub fn reserve(&mut self, len: usize) {
262        self.sprites.reserve(len);
263        self.vertice.reserve(len * 4);
264    }
265
266    /// Return spritedata slice
267    pub fn sprites(&self) -> &[SpriteData] {
268        &self.sprites
269    }
270
271    /// Return sprite mutable slice
272    pub fn sprites_mut(&mut self) -> &mut [SpriteData] {
273        &mut self.sprites
274    }
275
276    /// Return maybe a mutable Slice.
277    pub fn get_sprite_mut(&mut self, idx: usize) -> Option<&mut SpriteData> {
278        self.sprites.get_mut(idx)
279    }
280
281    /// Return maybe a SpriteData.
282    pub fn get_sprite(&self, idx: usize) -> Option<&SpriteData> {
283        self.sprites.get(idx)
284    }
285
286    pub fn push_sprite(&mut self, mut sprites: SpriteData) {
287        sprites.need_update = true;
288        let (w, h) = if let Some(ref texture) = self.texture {
289            (texture.width() as f32, texture.height() as f32)
290        } else {
291            (0.0, 0.0)
292        };
293
294        self.vertice.extend_from_slice(&[
295            Vertex::new(
296                Vector::new(0.0, 0.0),
297                Vector::new(sprites.text_coord[0].x, sprites.text_coord[0].y),
298                Color::white(),
299            ),
300            Vertex::new(
301                Vector::new(0.0, h),
302                Vector::new(sprites.text_coord[0].x, sprites.text_coord[1].y),
303                Color::white(),
304            ),
305            Vertex::new(
306                Vector::new(w, 0.0),
307                Vector::new(sprites.text_coord[1].x, sprites.text_coord[0].y),
308                Color::white(),
309            ),
310            Vertex::new(
311                Vector::new(w, h),
312                Vector::new(sprites.text_coord[1].x, sprites.text_coord[1].y),
313                Color::white(),
314            ),
315        ]);
316        self.sprites.push(sprites);
317    }
318
319    /// Pop a sprite and return it's data.
320    pub fn pop_sprite(&mut self) -> Option<SpriteData> {
321        self.need_update = true;
322        self.vertice.truncate(self.len - 4);
323        self.sprites.pop()
324    }
325
326    fn update_vbo(&mut self) {
327        unsafe {
328            gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_objects.1);
329
330            if self.len != self.vertice.len() / 4 {
331                gl::BufferData(
332                    gl::ARRAY_BUFFER,
333                    (std::mem::size_of::<GLfloat>() * self.vertice.len() * 8) as GLsizeiptr,
334                    self.vertice.as_ptr() as *const GLvoid,
335                    gl::STATIC_DRAW,
336                );
337                self.len = self.vertice.len() / 4;
338            }
339            gl::BufferSubData(
340                gl::ARRAY_BUFFER,
341                0,
342                (std::mem::size_of::<GLfloat>() * self.vertice.len() * 8) as GLsizeiptr,
343                self.vertice.as_ptr() as *const GLvoid,
344            );
345            self.update_vao();
346            gl::BindBuffer(gl::ARRAY_BUFFER, 0);
347        }
348    }
349
350    fn create_vbo() -> (u32, u32) {
351        let (mut vao, mut vbo) = (0, 0);
352        unsafe {
353            gl::GenVertexArrays(1, &mut vao);
354
355            gl::GenBuffers(1, &mut vbo);
356
357            gl::BindVertexArray(0);
358        }
359        (vao, vbo)
360    }
361
362    fn update_vao(&mut self) {
363        unsafe {
364            gl::BindVertexArray(self.gl_objects.0);
365            // position (of each vertex)
366            gl::VertexAttribPointer(
367                0,
368                2,
369                gl::FLOAT,
370                gl::FALSE,
371                (8 * mem::size_of::<GLfloat>()) as GLsizei,
372                ptr::null(),
373            );
374            gl::EnableVertexAttribArray(0);
375            // texture coord (of each vertex)
376            gl::VertexAttribPointer(
377                1,
378                2,
379                gl::FLOAT,
380                gl::FALSE,
381                (8 * mem::size_of::<GLfloat>()) as GLsizei,
382                (2 * mem::size_of::<GLfloat>()) as *const _,
383            );
384            gl::EnableVertexAttribArray(1);
385            // color (of each vertex)
386            gl::VertexAttribPointer(
387                2,
388                3,
389                gl::FLOAT,
390                gl::FALSE,
391                (8 * mem::size_of::<GLfloat>()) as GLsizei,
392                (4 * mem::size_of::<GLfloat>()) as *const _,
393            );
394            gl::EnableVertexAttribArray(2);
395
396            gl::BindVertexArray(0);
397        }
398    }
399
400    fn fill_vbo(&mut self) {
401        unsafe {
402            gl::BindVertexArray(self.gl_objects.0);
403            gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_objects.1);
404
405            gl::BufferData(
406                gl::ARRAY_BUFFER,
407                (std::mem::size_of::<GLfloat>() * self.vertice.len() * 8) as GLsizeiptr,
408                self.vertice.as_ptr() as *const GLvoid,
409                gl::STATIC_DRAW,
410            );
411            self.update_vao();
412
413            gl::BindBuffer(gl::ARRAY_BUFFER, 0);
414        }
415    }
416
417    fn update_model(&mut self) {
418        //translate to glob_glob_glob_position
419        self.model = Matrix4::<f32>::identity().append_translation(&Vector3::new(
420            self.glob_pos.x - self.glob_origin.x,
421            self.glob_pos.y - self.glob_origin.y,
422            0.0,
423        ));
424        if self.glob_origin.x != 0.0 && self.glob_origin.y != 0.0 {
425            self.model.append_translation_mut(&Vector3::new(
426                self.glob_origin.x,
427                self.glob_origin.y,
428                0.0,
429            ));
430            self.model *=
431                Matrix4::from_euler_angles(0.0, 0.0, self.glob_rotation * (3.14116 * 180.0));
432            self.model.prepend_translation_mut(&Vector3::new(
433                -self.glob_origin.x,
434                -self.glob_origin.y,
435                0.0,
436            ));
437        } else {
438            self.model *=
439                Matrix4::from_euler_angles(0.0, 0.0, self.glob_rotation * (3.14116 * 180.0));
440        }
441        self.model.append_nonuniform_scaling_mut(&Vector3::new(
442            self.glob_scale.x,
443            self.glob_scale.y,
444            0.0,
445        ));
446        if self.glob_rotation > 360.0 {
447            self.glob_rotation = 0.0;
448        }
449        self.need_update = false;
450    }
451
452    fn len(&self) -> usize {
453        self.len
454    }
455}
456
457impl Transformable for SpriteBatch {
458    fn contain<T: nalgebra::Scalar + Into<f32>>(&self, _vec: ::Point<T>) -> bool {
459        true
460    }
461
462    fn set_origin<T: nalgebra::Scalar + Into<f32>>(&mut self, origin: Vector<T>) {
463        self.glob_origin.x = origin.x.into();
464        self.glob_origin.y = origin.y.into();
465        self.need_update = true;
466    }
467
468    fn get_origin(&self) -> Vector<f32> {
469        self.glob_origin
470    }
471}
472
473impl Scalable for SpriteBatch {
474    fn set_scale<T>(&mut self, vec: Vector<T>)
475    where
476        T: Scalar + Into<f32>,
477    {
478        self.glob_scale.x = vec.x.into();
479        self.glob_scale.y = vec.y.into();
480        self.need_update = true;
481    }
482
483    fn get_scale(&self) -> Vector<f32> {
484        self.glob_scale
485    }
486
487    fn scale<T>(&mut self, factor: Vector<T>)
488    where
489        T: Scalar + Into<f32>,
490    {
491        self.glob_scale.x += factor.x.into();
492        self.glob_scale.y += factor.y.into();
493        self.need_update = true;
494    }
495}
496
497impl Rotable for SpriteBatch {
498    fn rotate<T>(&mut self, angle: T)
499    where
500        T: Scalar + Into<f32>,
501    {
502        self.glob_rotation += angle.into();
503        self.need_update = true;
504    }
505
506    fn set_rotation<T>(&mut self, angle: T)
507    where
508        T: Scalar + Into<f32>,
509    {
510        self.glob_rotation = angle.into();
511        self.need_update = true;
512    }
513
514    fn get_rotation(&self) -> f32 {
515        self.glob_rotation
516    }
517}
518
519impl Movable for SpriteBatch {
520    fn translate<T>(&mut self, vec: Vector<T>)
521    where
522        T: Scalar + Into<f32>,
523    {
524        self.glob_pos.x += vec.x.into();
525        self.glob_pos.y += vec.y.into();
526        self.need_update = true;
527    }
528
529    fn get_position(&self) -> Vector<f32> {
530        self.glob_pos
531    }
532
533    fn set_position<T>(&mut self, vec: Vector<T>)
534    where
535        T: Scalar + Into<f32>,
536    {
537        self.glob_pos.x = vec.x.into();
538        self.glob_pos.y = vec.y.into();
539        self.need_update = true;
540    }
541}
542
543impl DrawableMut for SpriteBatch {
544    fn draw_mut<T: Drawer>(&mut self, target: &mut T) {
545        self.update();
546        self.draw(target);
547    }
548}
549
550impl Drawable for SpriteBatch {
551    fn draw<T: Drawer>(&self, target: &mut T) {
552        let texture = if let Some(ref rc_texture) = self.texture {
553            Some(rc_texture.as_ref())
554        } else {
555            None
556        };
557
558        let mut context = Context::new(
559            texture,
560            &*BATCH_SHADER,
561            vec![
562                ("projection".to_string(), target.projection()),
563                ("glob_model".to_string(), &self.model),
564            ],
565            BlendMode::Alpha,
566        );
567
568        self.setup_draw(&mut context);
569        unsafe {
570            gl::BindVertexArray(self.gl_objects.0);
571            gl::BindBuffer(gl::ARRAY_BUFFER, self.gl_objects.1);
572
573            gl::DrawArrays(gl::TRIANGLE_STRIP, 0, self.vertice.len() as i32);
574
575            gl::BindVertexArray(0);
576            gl::BindBuffer(gl::ARRAY_BUFFER, 0);
577        }
578    }
579
580    fn draw_with_context(&self, _context: &mut Context) {
581        unimplemented!(
582        "Put an issue here please if I forgot to implement it https://github.com/Afourcat/Gust/issues");
583    }
584
585    fn update(&mut self) {
586        //use std::sync::mpsc;
587        //let (rec, sen) = mpsc::channel();
588        let mut sprite_mod = false;
589        {
590            //let rex = Mutex::new(rec);
591            let sprites = &mut self.sprites;
592            //let vertices = Mutex::new(&mut self.vertice);
593            let vertices = &mut self.vertice;
594
595            for (i, mut elem) in sprites.iter_mut().enumerate() {
596                if elem.need_update {
597                    let vert = &mut vertices[(i * 4)..(i * 4 + 4)];
598                    self::update_sprite(&mut elem, Vector::new(0.0, 0.0), vert);
599                    sprite_mod = true;
600                }
601            }
602        }
603
604        if sprite_mod {
605            self.update_vbo();
606        }
607        if self.need_update {
608            self.update_model();
609        }
610    }
611}
612
613fn update_sprite(data: &mut SpriteData, origin: Vector<f32>, vertice: &mut [Vertex]) {
614    data.model = Matrix4::identity().append_translation(&Vector3::new(
615        data.pos.x - origin.x,
616        data.pos.y - origin.y,
617        0.0,
618    ));
619
620    data.model *= Matrix4::from_euler_angles(0.0, 0.0, data.rotation * (3.14116 * 180.0));
621
622    for vertex in vertice {
623        let b = data.model * Vector4::new(vertex.pos.x, vertex.pos.y, 0.0, 1.0);
624        vertex.pos = Vector::new(b.x, b.y);
625    }
626
627    if data.rotation > 360.0 {
628        data.rotation = 0.0;
629    }
630
631    data.need_update = false;
632}
633
634impl Default for SpriteBatch {
635    fn default() -> Self {
636        SpriteBatch {
637            texture: None,
638            sprites: Vec::new(),
639            vertice: Vec::new(),
640            gl_objects: Self::create_vbo(),
641            glob_origin: Vector::new(0.0, 0.0),
642            glob_pos: Vector::new(0.0, 0.0),
643            glob_scale: Vector::new(0.0, 0.0),
644            glob_rotation: 0.0,
645            len: 0,
646            need_update: false,
647            model: Matrix4::identity(),
648        }
649    }
650}
651
652impl From<&Rc<Texture>> for SpriteBatch {
653    fn from(what: &Rc<Texture>) -> SpriteBatch {
654        let (width, height) = (what.width(), what.height());
655
656        SpriteBatch {
657            texture: Some(Rc::clone(what)),
658            sprites: Vec::new(),
659            vertice: Vec::new(),
660            gl_objects: Self::create_vbo(),
661            glob_origin: Vector::new((width / 2) as f32, (height / 2) as f32),
662            glob_pos: Vector::new(0.0, 0.0),
663            glob_scale: Vector::new(1.0, 1.0),
664            glob_rotation: 0.0,
665            len: 0,
666            need_update: false,
667            model: Matrix4::identity(),
668        }
669    }
670}
671
672#[cfg(test)]
673mod test {
674    extern crate test;
675
676    use self::test::Bencher;
677    use super::{SpriteBatch, SpriteData};
678    use draw::Drawable;
679    use std::rc::Rc;
680    use transform::Movable;
681    use window::Window;
682    use {texture::Texture, Vector};
683
684    #[bench]
685    fn sprite_batch_create(bencher: &mut Bencher) {
686        Window::new(100, 100, "Loader");
687
688        bencher.iter(|| {
689            SpriteBatch::new();
690        });
691    }
692
693    #[bench]
694    fn batch_create_with_data(bencher: &mut Bencher) {
695        Window::new(100, 100, "Loader");
696        let texture = Rc::new(Texture::from_path("examples/texture/test.jpg").unwrap());
697        let mut vec = Vec::with_capacity(1000);
698        (0..1000)
699            .into_iter()
700            .for_each(|i| vec.push(SpriteData::new(Vector::new((i * 10 + 10) as f32, 10.0))));
701
702        bencher.iter(|| {
703            let mut batch = SpriteBatch::from(&texture);
704            batch.extend_from_slice(&mut vec);
705        });
706    }
707
708    #[bench]
709    fn batch_update_create(bencher: &mut Bencher) {
710        Window::new(100, 100, "Loader");
711        let texture = Rc::new(Texture::from_path("examples/texture/test.jpg").unwrap());
712        let mut vec = Vec::with_capacity(1000);
713        (0..1000)
714            .into_iter()
715            .for_each(|i| vec.push(SpriteData::new(Vector::new((i * 10 + 10) as f32, 10.0))));
716        let mut batch = SpriteBatch::from(&texture);
717        batch.extend_from_slice(&mut vec);
718
719        bencher.iter(|| {
720            batch.update();
721        });
722    }
723
724    #[bench]
725    fn batch_update_translation_with_bad_update(bencher: &mut Bencher) {
726        Window::new(100, 100, "Loader");
727        let texture = Rc::new(Texture::from_path("examples/texture/test.jpg").unwrap());
728        let mut vec = Vec::with_capacity(1000);
729        (0..1000)
730            .into_iter()
731            .for_each(|i| vec.push(SpriteData::new(Vector::new((i * 10 + 10) as f32, 10.0))));
732        let mut batch = SpriteBatch::from(&texture);
733        batch.extend_from_slice(&mut vec);
734
735        bencher.iter(|| {
736            // Here this update is make the compute 2 times longer
737            // However it's useless because we will mut and update after it.
738            // /!\ Be careful when calling heavy function.
739            batch.update();
740            batch.translate(Vector::new(100.0, 0.0));
741            batch
742                .get_sprite_mut(0)
743                .unwrap()
744                .translate(Vector::new(100.0, 0.0));
745            batch.update();
746        });
747    }
748
749    #[bench]
750    fn batch_update_translation(bencher: &mut Bencher) {
751        Window::new(100, 100, "Loader");
752        let texture = Rc::new(Texture::from_path("examples/texture/test.jpg").unwrap());
753        let mut vec = Vec::with_capacity(1000);
754        (0..1000)
755            .into_iter()
756            .for_each(|i| vec.push(SpriteData::new(Vector::new((i * 10 + 10) as f32, 10.0))));
757        let mut batch = SpriteBatch::from(&texture);
758        batch.extend_from_slice(&mut vec);
759
760        bencher.iter(|| {
761            batch.translate(Vector::new(100.0, 0.0));
762            batch
763                .get_sprite_mut(0)
764                .map(|x| x.translate(Vector::new(100.0, 0.0)));
765            batch.update();
766        });
767    }
768
769    #[bench]
770    fn batch_update_content(bencher: &mut Bencher) {
771        Window::new(100, 100, "Loader");
772        let texture = Rc::new(Texture::from_path("examples/texture/Dirt.png").unwrap());
773        let mut vec = Vec::with_capacity(100000);
774        (0..100000)
775            .into_iter()
776            .for_each(|i| vec.push(SpriteData::new(Vector::new((i * 10 + 10) as f32, 10.0))));
777        let mut batch = SpriteBatch::from(&texture);
778        batch.extend_from_slice(&mut vec);
779
780        bencher.iter(|| {
781            batch
782                .sprites_mut()
783                .iter_mut()
784                .for_each(|x| x.translate(Vector::new(10.0, 0.0)));
785            batch.update();
786        });
787    }
788}