renderer.rs

  1use super::{atlas::AtlasAllocator, sprite_cache::SpriteCache};
  2use crate::{
  3    color::ColorU,
  4    geometry::{
  5        rect::RectF,
  6        vector::{vec2f, vec2i, Vector2F, Vector2I},
  7    },
  8    platform,
  9    scene::Layer,
 10    Scene,
 11};
 12use anyhow::{anyhow, Result};
 13use cocoa::foundation::NSUInteger;
 14use metal::{MTLPixelFormat, MTLResourceOptions, NSRange};
 15use shaders::{ToFloat2 as _, ToUchar4 as _};
 16use std::{collections::HashMap, ffi::c_void, mem, sync::Arc};
 17
 18const SHADERS_METALLIB: &'static [u8] =
 19    include_bytes!(concat!(env!("OUT_DIR"), "/shaders.metallib"));
 20const INSTANCE_BUFFER_SIZE: usize = 1024 * 1024; // This is an arbitrary decision. There's probably a more optimal value.
 21
 22pub struct Renderer {
 23    device: metal::Device,
 24    command_buffer: metal::CommandBuffer,
 25    sprite_cache: SpriteCache,
 26    path_stencils: AtlasAllocator,
 27    quad_pipeline_state: metal::RenderPipelineState,
 28    shadow_pipeline_state: metal::RenderPipelineState,
 29    sprite_pipeline_state: metal::RenderPipelineState,
 30    path_stencil_pipeline_state: metal::RenderPipelineState,
 31    unit_vertices: metal::Buffer,
 32    instances: metal::Buffer,
 33}
 34
 35struct PathSprite {
 36    layer_id: usize,
 37    atlas_id: usize,
 38    sprite: shaders::GPUISprite,
 39}
 40
 41impl Renderer {
 42    pub fn new(
 43        device: metal::Device,
 44        command_buffer: metal::CommandBuffer,
 45        pixel_format: metal::MTLPixelFormat,
 46        fonts: Arc<dyn platform::FontSystem>,
 47    ) -> Result<Self> {
 48        let library = device
 49            .new_library_with_data(SHADERS_METALLIB)
 50            .map_err(|message| anyhow!("error building metal library: {}", message))?;
 51
 52        let unit_vertices = [
 53            (0., 0.).to_float2(),
 54            (1., 0.).to_float2(),
 55            (0., 1.).to_float2(),
 56            (0., 1.).to_float2(),
 57            (1., 0.).to_float2(),
 58            (1., 1.).to_float2(),
 59        ];
 60        let unit_vertices = device.new_buffer_with_data(
 61            unit_vertices.as_ptr() as *const c_void,
 62            (unit_vertices.len() * mem::size_of::<shaders::vector_float2>()) as u64,
 63            MTLResourceOptions::StorageModeManaged,
 64        );
 65        let instances = device.new_buffer(
 66            INSTANCE_BUFFER_SIZE as u64,
 67            MTLResourceOptions::StorageModeManaged,
 68        );
 69
 70        let path_stencil_pixel_format = metal::MTLPixelFormat::Stencil8;
 71        let path_stencil_descriptor = metal::TextureDescriptor::new();
 72        path_stencil_descriptor.set_width(2048);
 73        path_stencil_descriptor.set_height(2048);
 74        path_stencil_descriptor.set_pixel_format(path_stencil_pixel_format);
 75        path_stencil_descriptor.set_usage(metal::MTLTextureUsage::RenderTarget);
 76        path_stencil_descriptor.set_storage_mode(metal::MTLStorageMode::Private);
 77
 78        Ok(Self {
 79            device,
 80            command_buffer,
 81            sprite_cache: SpriteCache::new(device.clone(), vec2i(1024, 768), fonts),
 82            path_stencils: AtlasAllocator::new(device.clone(), path_stencil_descriptor),
 83            quad_pipeline_state: build_pipeline_state(
 84                &device,
 85                &library,
 86                "quad",
 87                "quad_vertex",
 88                "quad_fragment",
 89                pixel_format,
 90            )?,
 91            shadow_pipeline_state: build_pipeline_state(
 92                &device,
 93                &library,
 94                "shadow",
 95                "shadow_vertex",
 96                "shadow_fragment",
 97                pixel_format,
 98            )?,
 99            sprite_pipeline_state: build_pipeline_state(
100                &device,
101                &library,
102                "sprite",
103                "sprite_vertex",
104                "sprite_fragment",
105                pixel_format,
106            )?,
107            path_stencil_pipeline_state: build_stencil_pipeline_state(
108                &device,
109                &library,
110                "path_winding",
111                "path_winding_vertex",
112                "path_winding_fragment",
113                path_stencil_pixel_format,
114            )?,
115            unit_vertices,
116            instances,
117        })
118    }
119
120    pub fn render(&mut self, scene: &Scene, drawable_size: Vector2F, output: &metal::TextureRef) {
121        let mut offset = 0;
122        self.render_path_stencils(scene, &mut offset, drawable_size);
123        self.render_layers(scene, &mut offset, drawable_size, output);
124    }
125
126    fn render_path_stencils(
127        &mut self,
128        scene: &Scene,
129        offset: &mut usize,
130        drawable_size: Vector2F,
131    ) -> Vec<PathSprite> {
132        let mut stencils = Vec::new();
133        let mut vertices = Vec::<shaders::GPUIPathVertex>::new();
134        let mut current_atlas_id = None;
135        for (layer_id, layer) in scene.layers().iter().enumerate() {
136            for path in layer.paths() {
137                // Push a PathStencil struct for use later when sampling from the atlas as we draw the content of the layers
138                let size = path.bounds.size().ceil().to_i32();
139                let (atlas_id, atlas_origin) = self.path_stencils.allocate(size).unwrap();
140                stencils.push(PathSprite {
141                    layer_id,
142                    atlas_id,
143                    sprite: shaders::GPUISprite {
144                        origin: path.bounds.origin().to_float2(),
145                        size: size.to_float2(),
146                        atlas_origin: atlas_origin.to_float2(),
147                        color: path.color.to_uchar4(),
148                    },
149                });
150
151                if current_atlas_id.map_or(false, |current_atlas_id| atlas_id != current_atlas_id) {
152                    self.render_path_stencils_for_atlas(
153                        scene,
154                        offset,
155                        drawable_size,
156                        vertices.as_slice(),
157                        self.path_stencils.texture(atlas_id).unwrap(),
158                    );
159                    vertices.clear();
160                }
161
162                current_atlas_id = Some(atlas_id);
163
164                // Populate the vertices by translating them to their appropriate location in the atlas.
165                for vertex in &path.vertices {
166                    vertices.push(todo!());
167                }
168            }
169        }
170
171        if let Some(atlas_id) = current_atlas_id {
172            self.render_path_stencils_for_atlas(
173                scene,
174                offset,
175                drawable_size,
176                vertices.as_slice(),
177                self.path_stencils.texture(atlas_id).unwrap(),
178            );
179        }
180
181        stencils
182    }
183
184    fn render_path_stencils_for_atlas(
185        &mut self,
186        scene: &Scene,
187        offset: &mut usize,
188        drawable_size: Vector2F,
189        vertices: &[shaders::GPUIPathVertex],
190        texture: &metal::TextureRef,
191    ) {
192        // let render_pass_descriptor = metal::RenderPassDescriptor::new();
193        // let stencil_attachment = render_pass_descriptor.stencil_attachment().unwrap();
194        // stencil_attachment.set_texture(Some(&self.path_winding_texture));
195        // stencil_attachment.set_load_action(metal::MTLLoadAction::Clear);
196        // stencil_attachment.set_store_action(metal::MTLStoreAction::Store);
197        // let winding_command_encoder = self
198        //     .command_buffer
199        //     .new_render_command_encoder(render_pass_descriptor);
200
201        // Dubious shit that may be valuable:
202
203        // for path in scene.paths() {
204        //     winding_command_encoder.set_render_pipeline_state(&self.path_stencil_pipeline_state);
205        //     winding_command_encoder.set_vertex_buffer(
206        //         shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexVertices
207        //             as u64,
208        //         Some(&self.instances),
209        //         *offset as u64,
210        //     );
211        //     winding_command_encoder.set_vertex_bytes(
212        //         shaders::GPUIPathWindingVertexInputIndex_GPUIPathWindingVertexInputIndexViewportSize
213        //             as u64,
214        //         mem::size_of::<shaders::vector_float2>() as u64,
215        //         [drawable_size.to_float2()].as_ptr() as *const c_void,
216        //     );
217
218        //     let buffer_contents = unsafe {
219        //         (self.instances.contents() as *mut u8).offset(*offset as isize)
220        //             as *mut shaders::GPUIPathVertex
221        //     };
222
223        //     for (ix, vertex) in paths.iter().flat_map(|p| &p.vertices).enumerate() {
224        //         let shader_vertex = shaders::GPUIPathVertex {
225        //             xy_position: vertex.xy_position.to_float2(),
226        //             st_position: vertex.st_position.to_float2(),
227        //         };
228        //         unsafe {
229        //             *(buffer_contents.offset(ix as isize)) = shader_vertex;
230        //         }
231        //     }
232
233        //     self.instances.did_modify_range(NSRange {
234        //         location: *offset as u64,
235        //         length: (next_offset - *offset) as u64,
236        //     });
237        //     *offset = next_offset;
238
239        //     winding_command_encoder.draw_primitives(
240        //         metal::MTLPrimitiveType::Triangle,
241        //         0,
242        //         vertex_count as u64,
243        //     );
244        //     winding_command_encoder.end_encoding();
245        // }
246    }
247
248    fn render_layers(
249        &mut self,
250        scene: &Scene,
251        offset: &mut usize,
252        drawable_size: Vector2F,
253        output: &metal::TextureRef,
254    ) {
255        let render_pass_descriptor = metal::RenderPassDescriptor::new();
256        let color_attachment = render_pass_descriptor
257            .color_attachments()
258            .object_at(0)
259            .unwrap();
260        color_attachment.set_texture(Some(output));
261        color_attachment.set_load_action(metal::MTLLoadAction::Clear);
262        color_attachment.set_store_action(metal::MTLStoreAction::Store);
263        color_attachment.set_clear_color(metal::MTLClearColor::new(0., 0., 0., 1.));
264        let command_encoder = self
265            .command_buffer
266            .new_render_command_encoder(render_pass_descriptor);
267
268        command_encoder.set_viewport(metal::MTLViewport {
269            originX: 0.0,
270            originY: 0.0,
271            width: drawable_size.x() as f64,
272            height: drawable_size.y() as f64,
273            znear: 0.0,
274            zfar: 1.0,
275        });
276
277        for layer in scene.layers() {
278            self.clip(scene, layer, drawable_size, command_encoder);
279            self.render_shadows(scene, layer, &mut offset, drawable_size, command_encoder);
280            self.render_quads(scene, layer, &mut offset, drawable_size, command_encoder);
281            self.render_sprites(scene, layer, &mut offset, drawable_size, command_encoder);
282        }
283
284        command_encoder.end_encoding();
285    }
286
287    fn clip(
288        &mut self,
289        scene: &Scene,
290        layer: &Layer,
291        drawable_size: Vector2F,
292        command_encoder: &metal::RenderCommandEncoderRef,
293    ) {
294        let clip_bounds = layer.clip_bounds().unwrap_or(RectF::new(
295            vec2f(0., 0.),
296            drawable_size / scene.scale_factor(),
297        )) * scene.scale_factor();
298        command_encoder.set_scissor_rect(metal::MTLScissorRect {
299            x: clip_bounds.origin_x() as NSUInteger,
300            y: clip_bounds.origin_y() as NSUInteger,
301            width: clip_bounds.width() as NSUInteger,
302            height: clip_bounds.height() as NSUInteger,
303        });
304    }
305
306    fn render_shadows(
307        &mut self,
308        scene: &Scene,
309        layer: &Layer,
310        offset: &mut usize,
311        drawable_size: Vector2F,
312        command_encoder: &metal::RenderCommandEncoderRef,
313    ) {
314        if layer.shadows().is_empty() {
315            return;
316        }
317
318        align_offset(offset);
319        let next_offset = *offset + layer.shadows().len() * mem::size_of::<shaders::GPUIShadow>();
320        assert!(
321            next_offset <= INSTANCE_BUFFER_SIZE,
322            "instance buffer exhausted"
323        );
324
325        command_encoder.set_render_pipeline_state(&self.shadow_pipeline_state);
326        command_encoder.set_vertex_buffer(
327            shaders::GPUIShadowInputIndex_GPUIShadowInputIndexVertices as u64,
328            Some(&self.unit_vertices),
329            0,
330        );
331        command_encoder.set_vertex_buffer(
332            shaders::GPUIShadowInputIndex_GPUIShadowInputIndexShadows as u64,
333            Some(&self.instances),
334            *offset as u64,
335        );
336        command_encoder.set_vertex_bytes(
337            shaders::GPUIShadowInputIndex_GPUIShadowInputIndexUniforms as u64,
338            mem::size_of::<shaders::GPUIUniforms>() as u64,
339            [shaders::GPUIUniforms {
340                viewport_size: drawable_size.to_float2(),
341            }]
342            .as_ptr() as *const c_void,
343        );
344
345        let buffer_contents = unsafe {
346            (self.instances.contents() as *mut u8).offset(*offset as isize)
347                as *mut shaders::GPUIShadow
348        };
349        for (ix, shadow) in layer.shadows().iter().enumerate() {
350            let shape_bounds = shadow.bounds * scene.scale_factor();
351            let shader_shadow = shaders::GPUIShadow {
352                origin: shape_bounds.origin().to_float2(),
353                size: shape_bounds.size().to_float2(),
354                corner_radius: shadow.corner_radius * scene.scale_factor(),
355                sigma: shadow.sigma,
356                color: shadow.color.to_uchar4(),
357            };
358            unsafe {
359                *(buffer_contents.offset(ix as isize)) = shader_shadow;
360            }
361        }
362
363        self.instances.did_modify_range(NSRange {
364            location: *offset as u64,
365            length: (next_offset - *offset) as u64,
366        });
367        *offset = next_offset;
368
369        command_encoder.draw_primitives_instanced(
370            metal::MTLPrimitiveType::Triangle,
371            0,
372            6,
373            layer.shadows().len() as u64,
374        );
375    }
376
377    fn render_quads(
378        &mut self,
379        scene: &Scene,
380        layer: &Layer,
381        offset: &mut usize,
382        drawable_size: Vector2F,
383        command_encoder: &metal::RenderCommandEncoderRef,
384    ) {
385        if layer.quads().is_empty() {
386            return;
387        }
388        align_offset(offset);
389        let next_offset = *offset + layer.quads().len() * mem::size_of::<shaders::GPUIQuad>();
390        assert!(
391            next_offset <= INSTANCE_BUFFER_SIZE,
392            "instance buffer exhausted"
393        );
394
395        command_encoder.set_render_pipeline_state(&self.quad_pipeline_state);
396        command_encoder.set_vertex_buffer(
397            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexVertices as u64,
398            Some(&self.unit_vertices),
399            0,
400        );
401        command_encoder.set_vertex_buffer(
402            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexQuads as u64,
403            Some(&self.instances),
404            *offset as u64,
405        );
406        command_encoder.set_vertex_bytes(
407            shaders::GPUIQuadInputIndex_GPUIQuadInputIndexUniforms as u64,
408            mem::size_of::<shaders::GPUIUniforms>() as u64,
409            [shaders::GPUIUniforms {
410                viewport_size: drawable_size.to_float2(),
411            }]
412            .as_ptr() as *const c_void,
413        );
414
415        let buffer_contents = unsafe {
416            (self.instances.contents() as *mut u8).offset(*offset as isize)
417                as *mut shaders::GPUIQuad
418        };
419        for (ix, quad) in layer.quads().iter().enumerate() {
420            let bounds = quad.bounds * scene.scale_factor();
421            let border_width = quad.border.width * scene.scale_factor();
422            let shader_quad = shaders::GPUIQuad {
423                origin: bounds.origin().to_float2(),
424                size: bounds.size().to_float2(),
425                background_color: quad
426                    .background
427                    .unwrap_or(ColorU::transparent_black())
428                    .to_uchar4(),
429                border_top: border_width * (quad.border.top as usize as f32),
430                border_right: border_width * (quad.border.right as usize as f32),
431                border_bottom: border_width * (quad.border.bottom as usize as f32),
432                border_left: border_width * (quad.border.left as usize as f32),
433                border_color: quad
434                    .border
435                    .color
436                    .unwrap_or(ColorU::transparent_black())
437                    .to_uchar4(),
438                corner_radius: quad.corner_radius * scene.scale_factor(),
439            };
440            unsafe {
441                *(buffer_contents.offset(ix as isize)) = shader_quad;
442            }
443        }
444
445        self.instances.did_modify_range(NSRange {
446            location: *offset as u64,
447            length: (next_offset - *offset) as u64,
448        });
449        *offset = next_offset;
450
451        command_encoder.draw_primitives_instanced(
452            metal::MTLPrimitiveType::Triangle,
453            0,
454            6,
455            layer.quads().len() as u64,
456        );
457    }
458
459    fn render_sprites(
460        &mut self,
461        scene: &Scene,
462        layer: &Layer,
463        offset: &mut usize,
464        drawable_size: Vector2F,
465        command_encoder: &metal::RenderCommandEncoderRef,
466    ) {
467        if layer.glyphs().is_empty() {
468            return;
469        }
470
471        let mut sprites_by_atlas = HashMap::new();
472        for glyph in layer.glyphs() {
473            if let Some(sprite) = self.sprite_cache.render_glyph(
474                glyph.font_id,
475                glyph.font_size,
476                glyph.id,
477                glyph.origin,
478                scene.scale_factor(),
479            ) {
480                // Snap sprite to pixel grid.
481                let origin = (glyph.origin * scene.scale_factor()).floor() + sprite.offset.to_f32();
482                sprites_by_atlas
483                    .entry(sprite.atlas_id)
484                    .or_insert_with(Vec::new)
485                    .push(shaders::GPUISprite {
486                        origin: origin.to_float2(),
487                        size: sprite.size.to_float2(),
488                        atlas_origin: sprite.atlas_origin.to_float2(),
489                        color: glyph.color.to_uchar4(),
490                    });
491            }
492        }
493
494        command_encoder.set_render_pipeline_state(&self.sprite_pipeline_state);
495        command_encoder.set_vertex_buffer(
496            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexVertices as u64,
497            Some(&self.unit_vertices),
498            0,
499        );
500        command_encoder.set_vertex_bytes(
501            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexViewportSize as u64,
502            mem::size_of::<shaders::vector_float2>() as u64,
503            [drawable_size.to_float2()].as_ptr() as *const c_void,
504        );
505        command_encoder.set_vertex_bytes(
506            shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexAtlasSize as u64,
507            mem::size_of::<shaders::vector_float2>() as u64,
508            [self.sprite_cache.atlas_size().to_float2()].as_ptr() as *const c_void,
509        );
510
511        for (atlas_id, sprites) in sprites_by_atlas {
512            align_offset(offset);
513            let next_offset = *offset + sprites.len() * mem::size_of::<shaders::GPUISprite>();
514            assert!(
515                next_offset <= INSTANCE_BUFFER_SIZE,
516                "instance buffer exhausted"
517            );
518
519            command_encoder.set_vertex_buffer(
520                shaders::GPUISpriteVertexInputIndex_GPUISpriteVertexInputIndexSprites as u64,
521                Some(&self.instances),
522                *offset as u64,
523            );
524
525            let texture = self.sprite_cache.atlas_texture(atlas_id).unwrap();
526            command_encoder.set_fragment_texture(
527                shaders::GPUISpriteFragmentInputIndex_GPUISpriteFragmentInputIndexAtlas as u64,
528                Some(texture),
529            );
530
531            unsafe {
532                let buffer_contents = (self.instances.contents() as *mut u8)
533                    .offset(*offset as isize)
534                    as *mut shaders::GPUISprite;
535                std::ptr::copy_nonoverlapping(sprites.as_ptr(), buffer_contents, sprites.len());
536            }
537            self.instances.did_modify_range(NSRange {
538                location: *offset as u64,
539                length: (next_offset - *offset) as u64,
540            });
541            *offset = next_offset;
542
543            command_encoder.draw_primitives_instanced(
544                metal::MTLPrimitiveType::Triangle,
545                0,
546                6,
547                sprites.len() as u64,
548            );
549        }
550    }
551}
552
553fn align_offset(offset: &mut usize) {
554    let r = *offset % 256;
555    if r > 0 {
556        *offset += 256 - r; // Align to a multiple of 256 to make Metal happy
557    }
558}
559
560fn build_pipeline_state(
561    device: &metal::DeviceRef,
562    library: &metal::LibraryRef,
563    label: &str,
564    vertex_fn_name: &str,
565    fragment_fn_name: &str,
566    pixel_format: metal::MTLPixelFormat,
567) -> Result<metal::RenderPipelineState> {
568    let vertex_fn = library
569        .get_function(vertex_fn_name, None)
570        .map_err(|message| anyhow!("error locating vertex function: {}", message))?;
571    let fragment_fn = library
572        .get_function(fragment_fn_name, None)
573        .map_err(|message| anyhow!("error locating fragment function: {}", message))?;
574
575    let descriptor = metal::RenderPipelineDescriptor::new();
576    descriptor.set_label(label);
577    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
578    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
579    let color_attachment = descriptor.color_attachments().object_at(0).unwrap();
580    color_attachment.set_pixel_format(pixel_format);
581    color_attachment.set_blending_enabled(true);
582    color_attachment.set_rgb_blend_operation(metal::MTLBlendOperation::Add);
583    color_attachment.set_alpha_blend_operation(metal::MTLBlendOperation::Add);
584    color_attachment.set_source_rgb_blend_factor(metal::MTLBlendFactor::SourceAlpha);
585    color_attachment.set_source_alpha_blend_factor(metal::MTLBlendFactor::SourceAlpha);
586    color_attachment.set_destination_rgb_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
587    color_attachment.set_destination_alpha_blend_factor(metal::MTLBlendFactor::OneMinusSourceAlpha);
588
589    device
590        .new_render_pipeline_state(&descriptor)
591        .map_err(|message| anyhow!("could not create render pipeline state: {}", message))
592}
593
594fn build_stencil_pipeline_state(
595    device: &metal::DeviceRef,
596    library: &metal::LibraryRef,
597    label: &str,
598    vertex_fn_name: &str,
599    fragment_fn_name: &str,
600    pixel_format: metal::MTLPixelFormat,
601) -> Result<metal::RenderPipelineState> {
602    let vertex_fn = library
603        .get_function(vertex_fn_name, None)
604        .map_err(|message| anyhow!("error locating vertex function: {}", message))?;
605    let fragment_fn = library
606        .get_function(fragment_fn_name, None)
607        .map_err(|message| anyhow!("error locating fragment function: {}", message))?;
608
609    let descriptor = metal::RenderPipelineDescriptor::new();
610    descriptor.set_label(label);
611    descriptor.set_vertex_function(Some(vertex_fn.as_ref()));
612    descriptor.set_fragment_function(Some(fragment_fn.as_ref()));
613    descriptor.set_stencil_attachment_pixel_format(pixel_format);
614
615    device
616        .new_render_pipeline_state(&descriptor)
617        .map_err(|message| anyhow!("could not create render pipeline state: {}", message))
618}
619
620mod shaders {
621    #![allow(non_upper_case_globals)]
622    #![allow(non_camel_case_types)]
623    #![allow(non_snake_case)]
624
625    use pathfinder_geometry::vector::Vector2I;
626
627    use crate::{color::ColorU, geometry::vector::Vector2F};
628    use std::mem;
629
630    include!(concat!(env!("OUT_DIR"), "/shaders.rs"));
631
632    pub trait ToFloat2 {
633        fn to_float2(&self) -> vector_float2;
634    }
635
636    pub trait ToUchar4 {
637        fn to_uchar4(&self) -> vector_uchar4;
638    }
639
640    impl ToFloat2 for (f32, f32) {
641        fn to_float2(&self) -> vector_float2 {
642            unsafe {
643                let mut output = mem::transmute::<_, u32>(self.1.to_bits()) as vector_float2;
644                output <<= 32;
645                output |= mem::transmute::<_, u32>(self.0.to_bits()) as vector_float2;
646                output
647            }
648        }
649    }
650
651    impl ToFloat2 for Vector2F {
652        fn to_float2(&self) -> vector_float2 {
653            unsafe {
654                let mut output = mem::transmute::<_, u32>(self.y().to_bits()) as vector_float2;
655                output <<= 32;
656                output |= mem::transmute::<_, u32>(self.x().to_bits()) as vector_float2;
657                output
658            }
659        }
660    }
661
662    impl ToFloat2 for Vector2I {
663        fn to_float2(&self) -> vector_float2 {
664            self.to_f32().to_float2()
665        }
666    }
667
668    impl ToUchar4 for ColorU {
669        fn to_uchar4(&self) -> vector_uchar4 {
670            let mut vec = self.a as vector_uchar4;
671            vec <<= 8;
672            vec |= self.b as vector_uchar4;
673            vec <<= 8;
674            vec |= self.g as vector_uchar4;
675            vec <<= 8;
676            vec |= self.r as vector_uchar4;
677            vec
678        }
679    }
680}