<commit_before>use byteorder::{LittleEndian, ReadBytesExt};
use cgmath::*;
use gfx;
use gfx::traits::FactoryExt;
use gfx_core;
use memmap::Mmap;
use vecmath;
use vec_map::VecMap;

use rshader;

use std::env;
use std::collections::VecDeque;

use terrain::material::MaterialSet;
use terrain::tile_cache::{LayerType, Priority, TileCache, TileHeader, NUM_LAYERS};

use sky::Skybox;
use ocean::Ocean;

pub(crate) mod id;
pub(crate) mod node;
pub(crate) mod render;

pub(crate) use terrain::quadtree::id::*;
pub(crate) use terrain::quadtree::node::*;
pub(crate) use terrain::quadtree::render::*;

pub struct QuadTree<R, F>
where
    R: gfx::Resources,
    F: gfx::Factory<R>,
{
    /// List of nodes in the `QuadTree`. The root is always at index 0.
    nodes: Vec<Node>,
    ocean: Ocean<R>,

    /// List of nodes that will be rendered.
    visible_nodes: Vec<NodeId>,
    partially_visible_nodes: Vec<(NodeId, u8)>,

    /// Cache holding nearby tiles for each layer.
    tile_cache_layers: VecMap<TileCache<R>>,

    factory: F,
    pso: gfx::PipelineState<R, pipe::Meta>,
    pipeline_data: pipe::Data<R>,
    sky_pso: gfx::PipelineState<R, sky_pipe::Meta>,
    sky_pipeline_data: sky_pipe::Data<R>,
    planet_mesh_pso: gfx::PipelineState<R, planet_mesh_pipe::Meta>,
    planet_mesh_pipeline_data: planet_mesh_pipe::Data<R>,
    shaders_watcher: rshader::ShaderDirectoryWatcher,
    shader: rshader::Shader<R>,
    sky_shader: rshader::Shader<R>,
    planet_mesh_shader: rshader::Shader<R>,
    num_planet_mesh_vertices: usize,
    node_states: Vec<NodeState>,
    _materials: MaterialSet<R>,
}

#[allow(unused)]
impl<R, F> QuadTree<R, F>
where
    R: gfx::Resources,
    F: gfx::Factory<R>,
{
    pub(crate) fn new(
        header: TileHeader,
        data_file: Mmap,
        materials: MaterialSet<R>,
        sky: Skybox<R>,
        mut factory: F,
        color_buffer: &gfx::handle::RenderTargetView<R, gfx::format::Srgba8>,
        depth_buffer: &gfx::handle::DepthStencilView<R, gfx::format::DepthStencil>,
    ) -> Self {
        let mut shaders_watcher = rshader::ShaderDirectoryWatcher::new(
            env::var("TERRA_SHADER_DIRECTORY").unwrap_or(".".to_string()),
        ).unwrap();

        let shader = rshader::Shader::simple(
            &mut factory,
            &mut shaders_watcher,
            shader_source!(
                "../../shaders/glsl",
                "version",
                "atmosphere",
                "terrain.glslv"
            ),
            shader_source!(
                "../../shaders/glsl",
                "version",
                "atmosphere",
                "terrain.glslf"
            ),
        ).unwrap();

        let sky_shader = rshader::Shader::simple(
            &mut factory,
            &mut shaders_watcher,
            shader_source!("../../shaders/glsl", "version", "sky.glslv"),
            shader_source!("../../shaders/glsl", "version", "atmosphere", "sky.glslf"),
        ).unwrap();

        let planet_mesh_shader = rshader::Shader::simple(
            &mut factory,
            &mut shaders_watcher,
            shader_source!("../../shaders/glsl", "version", "planet_mesh.glslv"),
            shader_source!(
                "../../shaders/glsl",
                "version",
                "atmosphere",
                "planet_mesh.glslf"
            ),
        ).unwrap();

        let mut data_view = data_file.into_view_sync();
        let mut tile_cache_layers = VecMap::new();
        for layer in header.layers.iter().cloned() {
            tile_cache_layers.insert(
                layer.layer_type as usize,
                TileCache::new(layer, unsafe { data_view.clone() }, &mut factory),
            );
        }

        let noise_start = header.noise.offset;
        let noise_end = noise_start + header.noise.bytes;
        let noise_data = unsafe { &data_view.as_slice()[noise_start..noise_end] };
        let (noise_texture, noise_texture_view) =
            factory.create_texture_immutable_u8
            ::<(gfx_core::format::R8_G8_B8_A8, gfx_core::format::Unorm)>(
                gfx::texture::Kind::D2(
                    header.noise.resolution as u16,
                    header.noise.resolution as u16,
                    gfx::texture::AaMode::Single,
                ),
                &[gfx::memory::cast_slice(noise_data)],
            )
            .unwrap();

        let planet_mesh_start = header.planet_mesh.offset;
        let planet_mesh_end = planet_mesh_start + header.planet_mesh.bytes;
        let planet_mesh_data =
            unsafe { &data_view.as_slice()[planet_mesh_start..planet_mesh_end] };
        let planet_mesh_vertices = gfx::memory::cast_slice(planet_mesh_data);

        let heights_texture_view = tile_cache_layers[LayerType::Heights.index()]
            .get_texture_view_f32()
            .unwrap()
            .clone();
        let colors_texture_view = tile_cache_layers[LayerType::Colors.index()]
            .get_texture_view_srgba()
            .unwrap()
            .clone();
        let normals_texture_view = tile_cache_layers[LayerType::Normals.index()]
            .get_texture_view_rgba8()
            .unwrap()
            .clone();
        let water_texture_view = tile_cache_layers[LayerType::Water.index()]
            .get_texture_view_rgba8()
            .unwrap()
            .clone();

        let ocean = Ocean::new(&mut factory);

        let sampler = factory.create_sampler(gfx::texture::SamplerInfo::new(
            gfx::texture::FilterMethod::Trilinear,
            gfx::texture::WrapMode::Clamp,
        ));

        let sampler_wrap = factory.create_sampler(gfx::texture::SamplerInfo::new(
            gfx::texture::FilterMethod::Trilinear,
            gfx::texture::WrapMode::Tile,
        ));

        Self {
            visible_nodes: Vec::new(),
            partially_visible_nodes: Vec::new(),
            pso: Self::make_pso(&mut factory, shader.as_shader_set()),
            pipeline_data: pipe::Data {
                instances: factory.create_constant_buffer::<NodeState>(header.nodes.len() * 3),
                model_view_projection: [[0.0; 4]; 4],
                camera_position: [0.0, 0.0, 0.0],
                sun_direction: [0.0, 0.70710678118, 0.70710678118],
                resolution: 0,
                heights: (heights_texture_view, sampler.clone()),
                colors: (colors_texture_view, sampler.clone()),
                normals: (normals_texture_view, sampler.clone()),
                water: (water_texture_view, sampler.clone()),
                materials: (materials.texture_view.clone(), sampler_wrap.clone()),
                sky: (sky.texture_view.clone(), sampler.clone()),
                ocean_surface: (ocean.texture_view.clone(), sampler_wrap.clone()),
                noise: (noise_texture_view, sampler_wrap),
                noise_wavelength: header.noise.wavelength,
                planet_radius: 6371000.0,
                atmosphere_radius: 6471000.0,
                color_buffer: color_buffer.clone(),
                depth_buffer: depth_buffer.clone(),
            },
            sky_pso: Self::make_sky_pso(&mut factory, sky_shader.as_shader_set()),
            sky_pipeline_data: sky_pipe::Data {
                inv_model_view_projection: [[0.0; 4]; 4],
                camera_position: [0.0, 0.0, 0.0],
                sun_direction: [0.0, 0.70710678118, 0.70710678118],
                sky: (sky.texture_view.clone(), sampler.clone()),
                planet_radius: 6371000.0,
                atmosphere_radius: 6471000.0,
                color_buffer: color_buffer.clone(),
                depth_buffer: depth_buffer.clone(),
            },
            planet_mesh_pso: Self::make_planet_mesh_pso(
                &mut factory,
                planet_mesh_shader.as_shader_set(),
            ),
            planet_mesh_pipeline_data: planet_mesh_pipe::Data {
                vertices: factory.create_vertex_buffer(planet_mesh_vertices),
                model_view_projection: [[0.0; 4]; 4],
                color_buffer: color_buffer.clone(),
                depth_buffer: depth_buffer.clone(),
            },
            ocean,
            factory,
            shaders_watcher,
            shader,
            sky_shader,
            planet_mesh_shader,
            num_planet_mesh_vertices: header.planet_mesh.num_vertices,
            nodes: header.nodes,
            node_states: Vec::new(),
            tile_cache_layers,
            _materials: materials,
        }
    }

    fn update_priorities(&mut self, camera: Point3<f32>) {
        for node in self.nodes.iter_mut() {
            node.priority = Priority::from_f32(
                (node.min_distance * node.min_distance)
                    / node.bounds.square_distance(camera).max(0.001),
            );
        }
        for (_, ref mut cache_layer) in self.tile_cache_layers.iter_mut() {
            cache_layer.update_priorities(&mut self.nodes);
        }
    }

    fn update_cache<C: gfx_core::command::Buffer<R>>(&mut self, encoder: &mut gfx::Encoder<R, C>) {
        self.breadth_first(|qt, id| {
            if qt.nodes[id].priority < Priority::cutoff() {
                return false;
            }

            for layer in 0..NUM_LAYERS {
                if qt.nodes[id].tile_indices[layer].is_some()
                    && !qt.tile_cache_layers[layer].contains(id)
                {
                    qt.tile_cache_layers[layer].add_missing((qt.nodes[id].priority, id));
                }
            }
            true
        });
        for (_, ref mut cache_layer) in self.tile_cache_layers.iter_mut() {
            cache_layer.load_missing(&mut self.nodes, encoder);
        }
    }

    fn update_visibility(&mut self) {
        self.visible_nodes.clear();
        self.partially_visible_nodes.clear();
        for node in self.nodes.iter_mut() {
            node.visible = false;
        }
        // Any node with all needed layers in cache is visible...
        self.breadth_first(|qt, id| {
            qt.nodes[id].visible = qt.nodes[id].priority >= Priority::cutoff();
            qt.nodes[id].visible
        });
        // ...Except if all its children are visible instead.
        self.breadth_first(|qt, id| {
            if qt.nodes[id].visible {
                let mut mask = 0;
                let mut has_visible_children = false;
                for (i, c) in qt.nodes[id].children.iter().enumerate() {
                    if c.is_none() || !qt.nodes[c.unwrap()].visible {
                        mask = mask | (1 << i);
                    }

                    if c.is_some() && qt.nodes[c.unwrap()].visible {
                        has_visible_children = true;
                    }
                }

                if mask == 0 {
                    qt.nodes[id].visible = false;
                } else if has_visible_children {
                    qt.partially_visible_nodes.push((id, mask));
                } else {
                    qt.visible_nodes.push(id);
                }

                true
            } else {
                false
            }
        });
    }

    pub fn update<C: gfx_core::command::Buffer<R>>(
        &mut self,
        mvp_mat: vecmath::Matrix4<f32>,
        camera: Point3<f32>,
        encoder: &mut gfx::Encoder<R, C>,
        dt: f32,
    ) {
        self.update_priorities(camera);
        self.update_cache(encoder);
        self.update_visibility();
        self.update_shaders();

        self.ocean.update(encoder, dt);

        self.pipeline_data.model_view_projection = mvp_mat;
        self.pipeline_data.camera_position = [camera.x, camera.y, camera.z];

        self.sky_pipeline_data.inv_model_view_projection = vecmath::mat4_inv(mvp_mat);
        self.sky_pipeline_data.camera_position = [camera.x, camera.y, camera.z];

        self.planet_mesh_pipeline_data.model_view_projection = mvp_mat;
    }

    fn breadth_first<Visit>(&mut self, mut visit: Visit)
    where
        Visit: FnMut(&mut Self, NodeId) -> bool,
    {
        let mut pending = VecDeque::new();
        if visit(self, NodeId::root()) {
            pending.push_back(NodeId::root());
        }
        while let Some(id) = pending.pop_front() {
            for i in 0..4 {
                if let Some(child) = self.nodes[id].children[i] {
                    if visit(self, child) {
                        pending.push_back(child);
                    }
                }
            }
        }
    }

    pub fn get_height(&self, p: Point2<f32>) -> Option<f32> {
        if self.nodes[0].bounds.min.x > p.x || self.nodes[0].bounds.max.x < p.x
            || self.nodes[0].bounds.min.z > p.y || self.nodes[0].bounds.max.z < p.y
        {
            return None;
        }

        let mut id = NodeId::root();
        while self.nodes[id].children.iter().any(|c| c.is_some()) {
            for c in self.nodes[id].children.iter() {
                if let Some(c) = *c {
                    if self.nodes[c].bounds.min.x <= p.x && self.nodes[c].bounds.max.x >= p.x
                        && self.nodes[c].bounds.min.z <= p.y
                        && self.nodes[c].bounds.max.z >= p.y
                    {
                        id = c;
                        break;
                    }
                }
            }
        }

        let layer = &self.tile_cache_layers[LayerType::Heights.index()];
        let resolution = (layer.resolution() - 1) as f32;
        let x = (p.x - self.nodes[id].bounds.min.x) / self.nodes[id].side_length * resolution;
        let y = (p.y - self.nodes[id].bounds.min.z) / self.nodes[id].side_length * resolution;

        let get_texel = |x, y| {
            layer
                .get_texel(&self.nodes[id], x, y)
                .read_f32::<LittleEndian>()
                .unwrap()
        };

        let (mut fx, mut fy) = (x.fract(), y.fract());
        let (mut ix, mut iy) = (x.floor() as usize, y.floor() as usize);
        if ix == layer.resolution() as usize - 1 {
            ix = layer.resolution() as usize - 2;
            fx = 1.0;
        }
        if iy == layer.resolution() as usize - 1 {
            iy = layer.resolution() as usize - 2;
            fy = 1.0;
        }

        if fx + fy < 1.0 {
            Some(
                (1.0 - fx - fy) * get_texel(ix, iy) + fx * get_texel(ix + 1, iy)
                    + fy * get_texel(ix, iy + 1),
            )
        } else {
            Some(
                (fx + fy - 1.0) * get_texel(ix + 1, iy + 1) + (1.0 - fx) * get_texel(ix, iy + 1)
                    + (1.0 - fy) * get_texel(ix + 1, iy),
            )
        }
    }
}
<commit_msg>Root node should never be invisible<commit_after>use byteorder::{LittleEndian, ReadBytesExt};
use cgmath::*;
use gfx;
use gfx::traits::FactoryExt;
use gfx_core;
use memmap::Mmap;
use vecmath;
use vec_map::VecMap;

use rshader;

use std::env;
use std::collections::VecDeque;

use terrain::material::MaterialSet;
use terrain::tile_cache::{LayerType, Priority, TileCache, TileHeader, NUM_LAYERS};

use sky::Skybox;
use ocean::Ocean;

pub(crate) mod id;
pub(crate) mod node;
pub(crate) mod render;

pub(crate) use terrain::quadtree::id::*;
pub(crate) use terrain::quadtree::node::*;
pub(crate) use terrain::quadtree::render::*;

pub struct QuadTree<R, F>
where
    R: gfx::Resources,
    F: gfx::Factory<R>,
{
    /// List of nodes in the `QuadTree`. The root is always at index 0.
    nodes: Vec<Node>,
    ocean: Ocean<R>,

    /// List of nodes that will be rendered.
    visible_nodes: Vec<NodeId>,
    partially_visible_nodes: Vec<(NodeId, u8)>,

    /// Cache holding nearby tiles for each layer.
    tile_cache_layers: VecMap<TileCache<R>>,

    factory: F,
    pso: gfx::PipelineState<R, pipe::Meta>,
    pipeline_data: pipe::Data<R>,
    sky_pso: gfx::PipelineState<R, sky_pipe::Meta>,
    sky_pipeline_data: sky_pipe::Data<R>,
    planet_mesh_pso: gfx::PipelineState<R, planet_mesh_pipe::Meta>,
    planet_mesh_pipeline_data: planet_mesh_pipe::Data<R>,
    shaders_watcher: rshader::ShaderDirectoryWatcher,
    shader: rshader::Shader<R>,
    sky_shader: rshader::Shader<R>,
    planet_mesh_shader: rshader::Shader<R>,
    num_planet_mesh_vertices: usize,
    node_states: Vec<NodeState>,
    _materials: MaterialSet<R>,
}

#[allow(unused)]
impl<R, F> QuadTree<R, F>
where
    R: gfx::Resources,
    F: gfx::Factory<R>,
{
    pub(crate) fn new(
        header: TileHeader,
        data_file: Mmap,
        materials: MaterialSet<R>,
        sky: Skybox<R>,
        mut factory: F,
        color_buffer: &gfx::handle::RenderTargetView<R, gfx::format::Srgba8>,
        depth_buffer: &gfx::handle::DepthStencilView<R, gfx::format::DepthStencil>,
    ) -> Self {
        let mut shaders_watcher = rshader::ShaderDirectoryWatcher::new(
            env::var("TERRA_SHADER_DIRECTORY").unwrap_or(".".to_string()),
        ).unwrap();

        let shader = rshader::Shader::simple(
            &mut factory,
            &mut shaders_watcher,
            shader_source!(
                "../../shaders/glsl",
                "version",
                "atmosphere",
                "terrain.glslv"
            ),
            shader_source!(
                "../../shaders/glsl",
                "version",
                "atmosphere",
                "terrain.glslf"
            ),
        ).unwrap();

        let sky_shader = rshader::Shader::simple(
            &mut factory,
            &mut shaders_watcher,
            shader_source!("../../shaders/glsl", "version", "sky.glslv"),
            shader_source!("../../shaders/glsl", "version", "atmosphere", "sky.glslf"),
        ).unwrap();

        let planet_mesh_shader = rshader::Shader::simple(
            &mut factory,
            &mut shaders_watcher,
            shader_source!("../../shaders/glsl", "version", "planet_mesh.glslv"),
            shader_source!(
                "../../shaders/glsl",
                "version",
                "atmosphere",
                "planet_mesh.glslf"
            ),
        ).unwrap();

        let mut data_view = data_file.into_view_sync();
        let mut tile_cache_layers = VecMap::new();
        for layer in header.layers.iter().cloned() {
            tile_cache_layers.insert(
                layer.layer_type as usize,
                TileCache::new(layer, unsafe { data_view.clone() }, &mut factory),
            );
        }

        let noise_start = header.noise.offset;
        let noise_end = noise_start + header.noise.bytes;
        let noise_data = unsafe { &data_view.as_slice()[noise_start..noise_end] };
        let (noise_texture, noise_texture_view) =
            factory.create_texture_immutable_u8
            ::<(gfx_core::format::R8_G8_B8_A8, gfx_core::format::Unorm)>(
                gfx::texture::Kind::D2(
                    header.noise.resolution as u16,
                    header.noise.resolution as u16,
                    gfx::texture::AaMode::Single,
                ),
                &[gfx::memory::cast_slice(noise_data)],
            )
            .unwrap();

        let planet_mesh_start = header.planet_mesh.offset;
        let planet_mesh_end = planet_mesh_start + header.planet_mesh.bytes;
        let planet_mesh_data =
            unsafe { &data_view.as_slice()[planet_mesh_start..planet_mesh_end] };
        let planet_mesh_vertices = gfx::memory::cast_slice(planet_mesh_data);

        let heights_texture_view = tile_cache_layers[LayerType::Heights.index()]
            .get_texture_view_f32()
            .unwrap()
            .clone();
        let colors_texture_view = tile_cache_layers[LayerType::Colors.index()]
            .get_texture_view_srgba()
            .unwrap()
            .clone();
        let normals_texture_view = tile_cache_layers[LayerType::Normals.index()]
            .get_texture_view_rgba8()
            .unwrap()
            .clone();
        let water_texture_view = tile_cache_layers[LayerType::Water.index()]
            .get_texture_view_rgba8()
            .unwrap()
            .clone();

        let ocean = Ocean::new(&mut factory);

        let sampler = factory.create_sampler(gfx::texture::SamplerInfo::new(
            gfx::texture::FilterMethod::Trilinear,
            gfx::texture::WrapMode::Clamp,
        ));

        let sampler_wrap = factory.create_sampler(gfx::texture::SamplerInfo::new(
            gfx::texture::FilterMethod::Trilinear,
            gfx::texture::WrapMode::Tile,
        ));

        Self {
            visible_nodes: Vec::new(),
            partially_visible_nodes: Vec::new(),
            pso: Self::make_pso(&mut factory, shader.as_shader_set()),
            pipeline_data: pipe::Data {
                instances: factory.create_constant_buffer::<NodeState>(header.nodes.len() * 3),
                model_view_projection: [[0.0; 4]; 4],
                camera_position: [0.0, 0.0, 0.0],
                sun_direction: [0.0, 0.70710678118, 0.70710678118],
                resolution: 0,
                heights: (heights_texture_view, sampler.clone()),
                colors: (colors_texture_view, sampler.clone()),
                normals: (normals_texture_view, sampler.clone()),
                water: (water_texture_view, sampler.clone()),
                materials: (materials.texture_view.clone(), sampler_wrap.clone()),
                sky: (sky.texture_view.clone(), sampler.clone()),
                ocean_surface: (ocean.texture_view.clone(), sampler_wrap.clone()),
                noise: (noise_texture_view, sampler_wrap),
                noise_wavelength: header.noise.wavelength,
                planet_radius: 6371000.0,
                atmosphere_radius: 6471000.0,
                color_buffer: color_buffer.clone(),
                depth_buffer: depth_buffer.clone(),
            },
            sky_pso: Self::make_sky_pso(&mut factory, sky_shader.as_shader_set()),
            sky_pipeline_data: sky_pipe::Data {
                inv_model_view_projection: [[0.0; 4]; 4],
                camera_position: [0.0, 0.0, 0.0],
                sun_direction: [0.0, 0.70710678118, 0.70710678118],
                sky: (sky.texture_view.clone(), sampler.clone()),
                planet_radius: 6371000.0,
                atmosphere_radius: 6471000.0,
                color_buffer: color_buffer.clone(),
                depth_buffer: depth_buffer.clone(),
            },
            planet_mesh_pso: Self::make_planet_mesh_pso(
                &mut factory,
                planet_mesh_shader.as_shader_set(),
            ),
            planet_mesh_pipeline_data: planet_mesh_pipe::Data {
                vertices: factory.create_vertex_buffer(planet_mesh_vertices),
                model_view_projection: [[0.0; 4]; 4],
                color_buffer: color_buffer.clone(),
                depth_buffer: depth_buffer.clone(),
            },
            ocean,
            factory,
            shaders_watcher,
            shader,
            sky_shader,
            planet_mesh_shader,
            num_planet_mesh_vertices: header.planet_mesh.num_vertices,
            nodes: header.nodes,
            node_states: Vec::new(),
            tile_cache_layers,
            _materials: materials,
        }
    }

    fn update_priorities(&mut self, camera: Point3<f32>) {
        for node in self.nodes.iter_mut() {
            node.priority = Priority::from_f32(
                (node.min_distance * node.min_distance)
                    / node.bounds.square_distance(camera).max(0.001),
            );
        }
        for (_, ref mut cache_layer) in self.tile_cache_layers.iter_mut() {
            cache_layer.update_priorities(&mut self.nodes);
        }
    }

    fn update_cache<C: gfx_core::command::Buffer<R>>(&mut self, encoder: &mut gfx::Encoder<R, C>) {
        self.breadth_first(|qt, id| {
            if qt.nodes[id].priority < Priority::cutoff() {
                return false;
            }

            for layer in 0..NUM_LAYERS {
                if qt.nodes[id].tile_indices[layer].is_some()
                    && !qt.tile_cache_layers[layer].contains(id)
                {
                    qt.tile_cache_layers[layer].add_missing((qt.nodes[id].priority, id));
                }
            }
            true
        });
        for (_, ref mut cache_layer) in self.tile_cache_layers.iter_mut() {
            cache_layer.load_missing(&mut self.nodes, encoder);
        }
    }

    fn update_visibility(&mut self) {
        self.visible_nodes.clear();
        self.partially_visible_nodes.clear();
        for node in self.nodes.iter_mut() {
            node.visible = false;
        }
        // Any node with all needed layers in cache is visible...
        self.breadth_first(|qt, id| {
            qt.nodes[id].visible =
                id == NodeId::root() || qt.nodes[id].priority >= Priority::cutoff();
            qt.nodes[id].visible
        });
        // ...Except if all its children are visible instead.
        self.breadth_first(|qt, id| {
            if qt.nodes[id].visible {
                let mut mask = 0;
                let mut has_visible_children = false;
                for (i, c) in qt.nodes[id].children.iter().enumerate() {
                    if c.is_none() || !qt.nodes[c.unwrap()].visible {
                        mask = mask | (1 << i);
                    }

                    if c.is_some() && qt.nodes[c.unwrap()].visible {
                        has_visible_children = true;
                    }
                }

                if mask == 0 {
                    qt.nodes[id].visible = false;
                } else if has_visible_children {
                    qt.partially_visible_nodes.push((id, mask));
                } else {
                    qt.visible_nodes.push(id);
                }

                true
            } else {
                false
            }
        });
    }

    pub fn update<C: gfx_core::command::Buffer<R>>(
        &mut self,
        mvp_mat: vecmath::Matrix4<f32>,
        camera: Point3<f32>,
        encoder: &mut gfx::Encoder<R, C>,
        dt: f32,
    ) {
        self.update_priorities(camera);
        self.update_cache(encoder);
        self.update_visibility();
        self.update_shaders();

        self.ocean.update(encoder, dt);

        self.pipeline_data.model_view_projection = mvp_mat;
        self.pipeline_data.camera_position = [camera.x, camera.y, camera.z];

        self.sky_pipeline_data.inv_model_view_projection = vecmath::mat4_inv(mvp_mat);
        self.sky_pipeline_data.camera_position = [camera.x, camera.y, camera.z];

        self.planet_mesh_pipeline_data.model_view_projection = mvp_mat;
    }

    fn breadth_first<Visit>(&mut self, mut visit: Visit)
    where
        Visit: FnMut(&mut Self, NodeId) -> bool,
    {
        let mut pending = VecDeque::new();
        if visit(self, NodeId::root()) {
            pending.push_back(NodeId::root());
        }
        while let Some(id) = pending.pop_front() {
            for i in 0..4 {
                if let Some(child) = self.nodes[id].children[i] {
                    if visit(self, child) {
                        pending.push_back(child);
                    }
                }
            }
        }
    }

    pub fn get_height(&self, p: Point2<f32>) -> Option<f32> {
        if self.nodes[0].bounds.min.x > p.x || self.nodes[0].bounds.max.x < p.x
            || self.nodes[0].bounds.min.z > p.y || self.nodes[0].bounds.max.z < p.y
        {
            return None;
        }

        let mut id = NodeId::root();
        while self.nodes[id].children.iter().any(|c| c.is_some()) {
            for c in self.nodes[id].children.iter() {
                if let Some(c) = *c {
                    if self.nodes[c].bounds.min.x <= p.x && self.nodes[c].bounds.max.x >= p.x
                        && self.nodes[c].bounds.min.z <= p.y
                        && self.nodes[c].bounds.max.z >= p.y
                    {
                        id = c;
                        break;
                    }
                }
            }
        }

        let layer = &self.tile_cache_layers[LayerType::Heights.index()];
        let resolution = (layer.resolution() - 1) as f32;
        let x = (p.x - self.nodes[id].bounds.min.x) / self.nodes[id].side_length * resolution;
        let y = (p.y - self.nodes[id].bounds.min.z) / self.nodes[id].side_length * resolution;

        let get_texel = |x, y| {
            layer
                .get_texel(&self.nodes[id], x, y)
                .read_f32::<LittleEndian>()
                .unwrap()
        };

        let (mut fx, mut fy) = (x.fract(), y.fract());
        let (mut ix, mut iy) = (x.floor() as usize, y.floor() as usize);
        if ix == layer.resolution() as usize - 1 {
            ix = layer.resolution() as usize - 2;
            fx = 1.0;
        }
        if iy == layer.resolution() as usize - 1 {
            iy = layer.resolution() as usize - 2;
            fy = 1.0;
        }

        if fx + fy < 1.0 {
            Some(
                (1.0 - fx - fy) * get_texel(ix, iy) + fx * get_texel(ix + 1, iy)
                    + fy * get_texel(ix, iy + 1),
            )
        } else {
            Some(
                (fx + fy - 1.0) * get_texel(ix + 1, iy + 1) + (1.0 - fx) * get_texel(ix, iy + 1)
                    + (1.0 - fy) * get_texel(ix + 1, iy),
            )
        }
    }
}
<|endoftext|>


<commit_before>use llvm_sys::core::LLVMVoidType;
use llvm_sys::prelude::LLVMTypeRef;

use crate::AddressSpace;
use crate::context::ContextRef;
use crate::support::LLVMString;
use crate::types::traits::AsTypeRef;
use crate::types::{Type, BasicTypeEnum, FunctionType, PointerType};

/// A `VoidType` is a special type with no possible direct instances. It's particularly
/// useful as a pointer element type or a function return type.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct VoidType {
    void_type: Type,
}

impl VoidType {
    pub(crate) fn new(void_type: LLVMTypeRef) -> Self {
        assert!(!void_type.is_null());

        VoidType {
            void_type: Type::new(void_type),
        }
    }

    // REVIEW: Always false -> const fn?
    /// Gets whether or not this `VectorType` is sized or not. This may always
    /// be false and as such this function may be removed in the future.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    ///
    /// let context = Context::create();
    /// let void_type = context.void_type();
    ///
    /// assert!(void_type.is_sized());
    /// ```
    pub fn is_sized(&self) -> bool {
        self.void_type.is_sized()
    }

    /// Gets a reference to the `Context` this `VoidType` was created in.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    ///
    /// let context = Context::create();
    /// let void_type = context.void_type();
    ///
    /// assert_eq!(*void_type.get_context(), context);
    /// ```
    pub fn get_context(&self) -> ContextRef {
        self.void_type.get_context()
    }

    /// Creates a `PointerType` with this `VoidType` for its element type.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    /// use inkwell::AddressSpace;
    ///
    /// let context = Context::create();
    /// let void_type = context.void_type();
    /// let void_ptr_type = void_type.ptr_type(AddressSpace::Generic);
    ///
    /// assert_eq!(void_ptr_type.get_element_type().into_void_type(), void_type);
    /// ```
    pub fn ptr_type(&self, address_space: AddressSpace) -> PointerType {
        self.void_type.ptr_type(address_space)
    }

    /// Creates a `FunctionType` with this `VoidType` for its return type.
    /// This means the function does not return.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    ///
    /// let context = Context::create();
    /// let void_type = context.void_type();
    /// let fn_type = void_type.fn_type(&[], false);
    /// ```
    pub fn fn_type(&self, param_types: &[BasicTypeEnum], is_var_args: bool) -> FunctionType {
        self.void_type.fn_type(param_types, is_var_args)
    }

    /// Gets the `VoidType`. It will be assigned the global context.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    /// use inkwell::types::VoidType;
    ///
    /// let void_type = VoidType::void_type();
    ///
    /// assert_eq!(void_type.get_context(), Context::get_global());
    /// ```
    pub fn void_type() -> Self {
        let void_type = unsafe {
            LLVMVoidType()
        };

        VoidType::new(void_type)
    }

    /// Prints the definition of a `VoidType` to a `LLVMString`.
    pub fn print_to_string(&self) -> LLVMString {
        self.void_type.print_to_string()
    }

    // See Type::print_to_stderr note on 5.0+ status
    /// Prints the definition of a `VoidType` to stderr. Not available in newer LLVM versions.
    #[llvm_versions(3.7 => 4.0)]
    pub fn print_to_stderr(&self) {
        self.void_type.print_to_stderr()
    }
}

impl AsTypeRef for VoidType {
    fn as_type_ref(&self) -> LLVMTypeRef {
        self.void_type.type_
    }
}
<commit_msg>Remove ptr_type() from void_type. Fix up comments.<commit_after>use llvm_sys::core::LLVMVoidType;
use llvm_sys::prelude::LLVMTypeRef;

use crate::AddressSpace;
use crate::context::ContextRef;
use crate::support::LLVMString;
use crate::types::traits::AsTypeRef;
use crate::types::{Type, BasicTypeEnum, FunctionType, PointerType};

/// A `VoidType` is a special type with no possible direct instances. It's only
/// useful as a function return type.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct VoidType {
    void_type: Type,
}

impl VoidType {
    pub(crate) fn new(void_type: LLVMTypeRef) -> Self {
        assert!(!void_type.is_null());

        VoidType {
            void_type: Type::new(void_type),
        }
    }

    // REVIEW: Always false -> const fn?
    /// Gets whether or not this `VoidType` is sized or not. This may always
    /// be false and as such this function may be removed in the future.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    ///
    /// let context = Context::create();
    /// let void_type = context.void_type();
    ///
    /// assert!(void_type.is_sized());
    /// ```
    pub fn is_sized(&self) -> bool {
        self.void_type.is_sized()
    }

    /// Gets a reference to the `Context` this `VoidType` was created in.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    ///
    /// let context = Context::create();
    /// let void_type = context.void_type();
    ///
    /// assert_eq!(*void_type.get_context(), context);
    /// ```
    pub fn get_context(&self) -> ContextRef {
        self.void_type.get_context()
    }

    /// Creates a `FunctionType` with this `VoidType` for its return type.
    /// This means the function does not return.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    ///
    /// let context = Context::create();
    /// let void_type = context.void_type();
    /// let fn_type = void_type.fn_type(&[], false);
    /// ```
    pub fn fn_type(&self, param_types: &[BasicTypeEnum], is_var_args: bool) -> FunctionType {
        self.void_type.fn_type(param_types, is_var_args)
    }

    /// Gets the `VoidType`. It will be assigned the global context.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use inkwell::context::Context;
    /// use inkwell::types::VoidType;
    ///
    /// let void_type = VoidType::void_type();
    ///
    /// assert_eq!(void_type.get_context(), Context::get_global());
    /// ```
    pub fn void_type() -> Self {
        let void_type = unsafe {
            LLVMVoidType()
        };

        VoidType::new(void_type)
    }

    /// Prints the definition of a `VoidType` to a `LLVMString`.
    pub fn print_to_string(&self) -> LLVMString {
        self.void_type.print_to_string()
    }

    // See Type::print_to_stderr note on 5.0+ status
    /// Prints the definition of a `VoidType` to stderr. Not available in newer LLVM versions.
    #[llvm_versions(3.7 => 4.0)]
    pub fn print_to_stderr(&self) {
        self.void_type.print_to_stderr()
    }
}

impl AsTypeRef for VoidType {
    fn as_type_ref(&self) -> LLVMTypeRef {
        self.void_type.type_
    }
}
<|endoftext|>


<commit_before>extern crate baal;
extern crate yaml_rust;

use yaml_rust::yaml::YamlLoader;
use std::thread;
use std::time::Duration;

static YAML_CONFIG: &'static str =
"---
channels: 1
sample_rate: 44100.
frames_per_buffer: 64

effect_dir: assets/effects
music_dir: assets/musics

global_volume: 0.
music_volume: 0.8
effect_volume: 0.3

distance_model: [Pow2,10.,110.]
music_loop: true

effect:
    - [shoot.ogg,10]
    - [hit.ogg,10]

music:
    - village.ogg
...
";

#[test]
fn test() {
    let yaml_config = YamlLoader::load_from_str(YAML_CONFIG).unwrap();

    let setting = baal::Setting::from_yaml(&yaml_config[0]);

    for _ in 0..2 {
        baal::init(setting.clone());
        baal::music::play(0);

        for i in 0..7 {
            let p = (i*20) as f64;
            baal::effect::play(0,&[p,0.,0.]);
            thread::sleep(Duration::from_millis(1));
            baal::effect::play(1,&[p,0.,0.]);
            thread::sleep(Duration::from_millis(1));
        }

        baal::close();
    }
}
<commit_msg>expect<commit_after>extern crate baal;
extern crate yaml_rust;

use yaml_rust::yaml::YamlLoader;
use std::thread;
use std::time::Duration;

static YAML_CONFIG: &'static str =
"---
channels: 1
sample_rate: 44100.
frames_per_buffer: 64

effect_dir: assets/effects
music_dir: assets/musics

global_volume: 0.
music_volume: 0.8
effect_volume: 0.3

distance_model: [Pow2,10.,110.]
music_loop: true

effect:
    - [shoot.ogg,10]
    - [hit.ogg,10]

music:
    - village.ogg
...
";

#[test]
fn test() {
    let yaml_config = YamlLoader::load_from_str(YAML_CONFIG).expect("fail to load yaml config");

    let setting = baal::Setting::from_yaml(&yaml_config[0]);

    for _ in 0..2 {
        baal::init(setting.clone());
        baal::music::play(0);

        for i in 0..7 {
            let p = (i*20) as f64;
            baal::effect::play(0,&[p,0.,0.]);
            thread::sleep(Duration::from_millis(1));
            baal::effect::play(1,&[p,0.,0.]);
            thread::sleep(Duration::from_millis(1));
        }

        baal::close();
    }
}
<|endoftext|>


<commit_before>// Copyright (C) 2021 Red Hat, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause

//! Kernel-based vhost-vdpa backend.

use std::fs::{File, OpenOptions};
use std::os::raw::c_int;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};

use vm_memory::GuestAddressSpace;
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};

use std::alloc::{alloc, dealloc, Layout};
use std::mem;

use super::vhost_binding::*;
use super::{ioctl_result, Error, Result, VhostKernBackend, VhostKernFeatures};
use crate::vdpa::*;
use crate::{VhostAccess, VhostIotlbBackend, VhostIotlbMsg, VhostIotlbType};

/// Handle for running VHOST_VDPA ioctls.
pub struct VhostKernVdpa<AS: GuestAddressSpace> {
    fd: File,
    mem: AS,
    backend_features_acked: u64,
}

impl<AS: GuestAddressSpace> VhostKernVdpa<AS> {
    /// Open a handle to a new VHOST-VDPA instance.
    pub fn new(path: &str, mem: AS) -> Result<Self> {
        Ok(VhostKernVdpa {
            fd: OpenOptions::new()
                .read(true)
                .write(true)
                .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
                .open(path)
                .map_err(Error::VhostOpen)?,
            mem,
            backend_features_acked: 0,
        })
    }
}

impl<AS: GuestAddressSpace> VhostVdpa for VhostKernVdpa<AS> {
    fn get_device_id(&self) -> Result<u32> {
        let mut device_id: u32 = 0;
        let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_DEVICE_ID(), &mut device_id) };
        ioctl_result(ret, device_id)
    }

    fn get_status(&self) -> Result<u8> {
        let mut status: u8 = 0;
        let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_STATUS(), &mut status) };
        ioctl_result(ret, status)
    }

    fn set_status(&self, status: u8) -> Result<()> {
        let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_STATUS(), &status) };
        ioctl_result(ret, ())
    }

    fn get_config(&self, offset: u32, buffer: &mut [u8]) -> Result<()> {
        let buffer_len = buffer.len();
        let layout =
            Layout::from_size_align(mem::size_of::<vhost_vdpa_config>() + buffer_len, 1).unwrap();
        let ret: c_int;

        unsafe {
            let ptr = alloc(layout);
            let config = ptr as *mut vhost_vdpa_config;
            (*config).off = offset;
            (*config).len = buffer_len as u32;

            ret = ioctl_with_ptr(self, VHOST_VDPA_GET_CONFIG(), ptr);

            buffer.copy_from_slice((*config).buf.as_slice(buffer_len));

            dealloc(ptr, layout);
        };

        ioctl_result(ret, ())
    }

    fn set_config(&self, offset: u32, buffer: &[u8]) -> Result<()> {
        let buffer_len = buffer.len();
        let layout =
            Layout::from_size_align(mem::size_of::<vhost_vdpa_config>() + buffer_len, 1).unwrap();
        let ret: c_int;

        unsafe {
            let ptr = alloc(layout);
            let config = ptr as *mut vhost_vdpa_config;
            (*config).off = offset;
            (*config).len = buffer_len as u32;

            (*config)
                .buf
                .as_mut_slice(buffer_len)
                .copy_from_slice(&buffer);

            ret = ioctl_with_ptr(self, VHOST_VDPA_SET_CONFIG(), ptr);

            dealloc(ptr, layout);
        };

        ioctl_result(ret, ())
    }

    fn set_vring_enable(&self, queue_index: usize, enabled: bool) -> Result<()> {
        let vring_state = vhost_vring_state {
            index: queue_index as u32,
            num: enabled as u32,
        };

        let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_VRING_ENABLE(), &vring_state) };
        ioctl_result(ret, ())
    }

    fn get_vring_num(&self) -> Result<u16> {
        let mut vring_num: u16 = 0;
        let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VRING_NUM(), &mut vring_num) };
        ioctl_result(ret, vring_num)
    }

    fn set_config_call(&self, fd: &EventFd) -> Result<()> {
        let event_fd: ::std::os::raw::c_int = fd.as_raw_fd();
        let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_CONFIG_CALL(), &event_fd) };
        ioctl_result(ret, ())
    }

    fn get_iova_range(&self) -> Result<VhostVdpaIovaRange> {
        let mut low_iova_range = vhost_vdpa_iova_range { first: 0, last: 0 };

        let ret =
            unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VRING_NUM(), &mut low_iova_range) };

        let iova_range = VhostVdpaIovaRange {
            first: low_iova_range.first,
            last: low_iova_range.last,
        };

        ioctl_result(ret, iova_range)
    }

    fn dma_map(&self, iova: u64, size: u64, vaddr: *const u8, readonly: bool) -> Result<()> {
        let iotlb = VhostIotlbMsg {
            iova,
            size,
            userspace_addr: vaddr as u64,
            perm: match readonly {
                true => VhostAccess::ReadOnly,
                false => VhostAccess::ReadWrite,
            },
            msg_type: VhostIotlbType::Update,
        };

        self.send_iotlb_msg(&iotlb)
    }

    fn dma_unmap(&self, iova: u64, size: u64) -> Result<()> {
        let iotlb = VhostIotlbMsg {
            iova,
            size,
            msg_type: VhostIotlbType::Invalidate,
            ..Default::default()
        };

        self.send_iotlb_msg(&iotlb)
    }
}

impl<AS: GuestAddressSpace> VhostKernBackend for VhostKernVdpa<AS> {
    type AS = AS;

    fn mem(&self) -> &Self::AS {
        &self.mem
    }
}

impl<AS: GuestAddressSpace> AsRawFd for VhostKernVdpa<AS> {
    fn as_raw_fd(&self) -> RawFd {
        self.fd.as_raw_fd()
    }
}

impl<AS: GuestAddressSpace> VhostKernFeatures for VhostKernVdpa<AS> {
    fn get_backend_features_acked(&self) -> u64 {
        self.backend_features_acked
    }

    fn set_backend_features_acked(&mut self, features: u64) {
        self.backend_features_acked = features;
    }
}

#[cfg(test)]
mod tests {
    const VHOST_VDPA_PATH: &str = "/dev/vhost-vdpa-0";

    use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
    use vmm_sys_util::eventfd::EventFd;

    use super::*;
    use crate::{
        VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData,
    };
    use serial_test::serial;

    #[test]
    #[serial]
    fn test_vdpa_kern_new_device() {
        let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
        let vdpa = VhostKernVdpa::new(VHOST_VDPA_PATH, &m).unwrap();

        assert!(vdpa.as_raw_fd() >= 0);
        assert!(vdpa.mem().find_region(GuestAddress(0x100)).is_some());
        assert!(vdpa.mem().find_region(GuestAddress(0x10_0000)).is_none());
    }

    #[test]
    #[serial]
    fn test_vdpa_kern_is_valid() {
        let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
        let vdpa = VhostKernVdpa::new(VHOST_VDPA_PATH, &m).unwrap();

        let mut config = VringConfigData {
            queue_max_size: 32,
            queue_size: 32,
            flags: 0,
            desc_table_addr: 0x1000,
            used_ring_addr: 0x2000,
            avail_ring_addr: 0x3000,
            log_addr: None,
        };
        assert_eq!(vdpa.is_valid(&config), true);

        config.queue_size = 0;
        assert_eq!(vdpa.is_valid(&config), false);
        config.queue_size = 31;
        assert_eq!(vdpa.is_valid(&config), false);
        config.queue_size = 33;
        assert_eq!(vdpa.is_valid(&config), false);
    }

    #[test]
    #[serial]
    fn test_vdpa_kern_ioctls() {
        let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
        let vdpa = VhostKernVdpa::new(VHOST_VDPA_PATH, &m).unwrap();

        let features = vdpa.get_features().unwrap();
        // VIRTIO_F_VERSION_1 (bit 32) should be set
        assert_ne!(features & (1 << 32), 0);
        vdpa.set_features(features).unwrap();

        vdpa.set_owner().unwrap();

        vdpa.set_mem_table(&[]).unwrap_err();

        let region = VhostUserMemoryRegionInfo {
            guest_phys_addr: 0x0,
            memory_size: 0x10_0000,
            userspace_addr: m.get_host_address(GuestAddress(0x0)).unwrap() as u64,
            mmap_offset: 0,
            mmap_handle: -1,
        };
        vdpa.set_mem_table(&[region]).unwrap();

        assert!(vdpa.get_device_id().unwrap() > 0);

        assert_eq!(vdpa.get_status().unwrap(), 0x0);
        vdpa.set_status(0x1).unwrap();
        assert_eq!(vdpa.get_status().unwrap(), 0x1);

        let mut vec = vec![0u8; 8];
        vdpa.get_config(0, &mut vec).unwrap();
        vdpa.set_config(0, &vec).unwrap();

        let eventfd = EventFd::new(0).unwrap();

        // set_log_base() and set_log_fd() are not supported by vhost-vdpa
        vdpa.set_log_base(
            0x4000,
            Some(VhostUserDirtyLogRegion {
                mmap_size: 0x1000,
                mmap_offset: 0x10,
                mmap_handle: 1,
            }),
        )
        .unwrap_err();
        vdpa.set_log_base(0x4000, None).unwrap_err();
        vdpa.set_log_fd(eventfd.as_raw_fd()).unwrap_err();

        let max_queues = vdpa.get_vring_num().unwrap();
        vdpa.set_vring_num(0, max_queues + 1).unwrap_err();

        vdpa.set_vring_num(0, 32).unwrap();

        let config = VringConfigData {
            queue_max_size: 32,
            queue_size: 32,
            flags: 0,
            desc_table_addr: 0x1000,
            used_ring_addr: 0x2000,
            avail_ring_addr: 0x3000,
            log_addr: None,
        };
        vdpa.set_vring_addr(0, &config).unwrap();
        vdpa.set_vring_base(0, 1).unwrap();
        vdpa.set_vring_call(0, &eventfd).unwrap();
        vdpa.set_vring_kick(0, &eventfd).unwrap();
        vdpa.set_vring_err(0, &eventfd).unwrap();

        vdpa.set_config_call(&eventfd).unwrap();

        assert_eq!(vdpa.get_vring_base(0).unwrap(), 1);

        vdpa.set_vring_enable(0, true).unwrap();
        vdpa.set_vring_enable(0, false).unwrap();
    }

    #[test]
    #[serial]
    fn test_vdpa_kern_dma() {
        let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
        let mut vdpa = VhostKernVdpa::new(VHOST_VDPA_PATH, &m).unwrap();

        let features = vdpa.get_features().unwrap();
        // VIRTIO_F_VERSION_1 (bit 32) should be set
        assert_ne!(features & (1 << 32), 0);
        vdpa.set_features(features).unwrap();

        let backend_features = vdpa.get_backend_features().unwrap();
        assert_ne!(backend_features & (1 << VHOST_BACKEND_F_IOTLB_MSG_V2), 0);
        vdpa.set_backend_features(backend_features).unwrap();

        vdpa.set_owner().unwrap();

        vdpa.dma_map(0xFFFF_0000, 0xFFFF, std::ptr::null::<u8>(), false)
            .unwrap_err();

        unsafe {
            let layout = Layout::from_size_align(0xFFFF, 1).unwrap();
            let ptr = alloc(layout);

            vdpa.dma_map(0xFFFF_0000, 0xFFFF, ptr, false).unwrap();
            vdpa.dma_unmap(0xFFFF_0000, 0xFFFF).unwrap();

            dealloc(ptr, layout);
        };
    }
}
<commit_msg>vhost_kern/vdpa: skip tests if device is not found<commit_after>// Copyright (C) 2021 Red Hat, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 or BSD-3-Clause

//! Kernel-based vhost-vdpa backend.

use std::fs::{File, OpenOptions};
use std::os::raw::c_int;
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::{AsRawFd, RawFd};

use vm_memory::GuestAddressSpace;
use vmm_sys_util::eventfd::EventFd;
use vmm_sys_util::ioctl::{ioctl_with_mut_ref, ioctl_with_ptr, ioctl_with_ref};

use std::alloc::{alloc, dealloc, Layout};
use std::mem;

use super::vhost_binding::*;
use super::{ioctl_result, Error, Result, VhostKernBackend, VhostKernFeatures};
use crate::vdpa::*;
use crate::{VhostAccess, VhostIotlbBackend, VhostIotlbMsg, VhostIotlbType};

/// Handle for running VHOST_VDPA ioctls.
pub struct VhostKernVdpa<AS: GuestAddressSpace> {
    fd: File,
    mem: AS,
    backend_features_acked: u64,
}

impl<AS: GuestAddressSpace> VhostKernVdpa<AS> {
    /// Open a handle to a new VHOST-VDPA instance.
    pub fn new(path: &str, mem: AS) -> Result<Self> {
        Ok(VhostKernVdpa {
            fd: OpenOptions::new()
                .read(true)
                .write(true)
                .custom_flags(libc::O_CLOEXEC | libc::O_NONBLOCK)
                .open(path)
                .map_err(Error::VhostOpen)?,
            mem,
            backend_features_acked: 0,
        })
    }
}

impl<AS: GuestAddressSpace> VhostVdpa for VhostKernVdpa<AS> {
    fn get_device_id(&self) -> Result<u32> {
        let mut device_id: u32 = 0;
        let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_DEVICE_ID(), &mut device_id) };
        ioctl_result(ret, device_id)
    }

    fn get_status(&self) -> Result<u8> {
        let mut status: u8 = 0;
        let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_STATUS(), &mut status) };
        ioctl_result(ret, status)
    }

    fn set_status(&self, status: u8) -> Result<()> {
        let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_STATUS(), &status) };
        ioctl_result(ret, ())
    }

    fn get_config(&self, offset: u32, buffer: &mut [u8]) -> Result<()> {
        let buffer_len = buffer.len();
        let layout =
            Layout::from_size_align(mem::size_of::<vhost_vdpa_config>() + buffer_len, 1).unwrap();
        let ret: c_int;

        unsafe {
            let ptr = alloc(layout);
            let config = ptr as *mut vhost_vdpa_config;
            (*config).off = offset;
            (*config).len = buffer_len as u32;

            ret = ioctl_with_ptr(self, VHOST_VDPA_GET_CONFIG(), ptr);

            buffer.copy_from_slice((*config).buf.as_slice(buffer_len));

            dealloc(ptr, layout);
        };

        ioctl_result(ret, ())
    }

    fn set_config(&self, offset: u32, buffer: &[u8]) -> Result<()> {
        let buffer_len = buffer.len();
        let layout =
            Layout::from_size_align(mem::size_of::<vhost_vdpa_config>() + buffer_len, 1).unwrap();
        let ret: c_int;

        unsafe {
            let ptr = alloc(layout);
            let config = ptr as *mut vhost_vdpa_config;
            (*config).off = offset;
            (*config).len = buffer_len as u32;

            (*config)
                .buf
                .as_mut_slice(buffer_len)
                .copy_from_slice(&buffer);

            ret = ioctl_with_ptr(self, VHOST_VDPA_SET_CONFIG(), ptr);

            dealloc(ptr, layout);
        };

        ioctl_result(ret, ())
    }

    fn set_vring_enable(&self, queue_index: usize, enabled: bool) -> Result<()> {
        let vring_state = vhost_vring_state {
            index: queue_index as u32,
            num: enabled as u32,
        };

        let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_VRING_ENABLE(), &vring_state) };
        ioctl_result(ret, ())
    }

    fn get_vring_num(&self) -> Result<u16> {
        let mut vring_num: u16 = 0;
        let ret = unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VRING_NUM(), &mut vring_num) };
        ioctl_result(ret, vring_num)
    }

    fn set_config_call(&self, fd: &EventFd) -> Result<()> {
        let event_fd: ::std::os::raw::c_int = fd.as_raw_fd();
        let ret = unsafe { ioctl_with_ref(self, VHOST_VDPA_SET_CONFIG_CALL(), &event_fd) };
        ioctl_result(ret, ())
    }

    fn get_iova_range(&self) -> Result<VhostVdpaIovaRange> {
        let mut low_iova_range = vhost_vdpa_iova_range { first: 0, last: 0 };

        let ret =
            unsafe { ioctl_with_mut_ref(self, VHOST_VDPA_GET_VRING_NUM(), &mut low_iova_range) };

        let iova_range = VhostVdpaIovaRange {
            first: low_iova_range.first,
            last: low_iova_range.last,
        };

        ioctl_result(ret, iova_range)
    }

    fn dma_map(&self, iova: u64, size: u64, vaddr: *const u8, readonly: bool) -> Result<()> {
        let iotlb = VhostIotlbMsg {
            iova,
            size,
            userspace_addr: vaddr as u64,
            perm: match readonly {
                true => VhostAccess::ReadOnly,
                false => VhostAccess::ReadWrite,
            },
            msg_type: VhostIotlbType::Update,
        };

        self.send_iotlb_msg(&iotlb)
    }

    fn dma_unmap(&self, iova: u64, size: u64) -> Result<()> {
        let iotlb = VhostIotlbMsg {
            iova,
            size,
            msg_type: VhostIotlbType::Invalidate,
            ..Default::default()
        };

        self.send_iotlb_msg(&iotlb)
    }
}

impl<AS: GuestAddressSpace> VhostKernBackend for VhostKernVdpa<AS> {
    type AS = AS;

    fn mem(&self) -> &Self::AS {
        &self.mem
    }
}

impl<AS: GuestAddressSpace> AsRawFd for VhostKernVdpa<AS> {
    fn as_raw_fd(&self) -> RawFd {
        self.fd.as_raw_fd()
    }
}

impl<AS: GuestAddressSpace> VhostKernFeatures for VhostKernVdpa<AS> {
    fn get_backend_features_acked(&self) -> u64 {
        self.backend_features_acked
    }

    fn set_backend_features_acked(&mut self, features: u64) {
        self.backend_features_acked = features;
    }
}

#[cfg(test)]
mod tests {
    const VHOST_VDPA_PATH: &str = "/dev/vhost-vdpa-0";

    use vm_memory::{GuestAddress, GuestMemory, GuestMemoryMmap};
    use vmm_sys_util::eventfd::EventFd;

    use super::*;
    use crate::{
        VhostBackend, VhostUserDirtyLogRegion, VhostUserMemoryRegionInfo, VringConfigData,
    };
    use serial_test::serial;
    use std::io::ErrorKind;

    /// macro to skip test if vhost-vdpa device path is not found.
    ///
    /// vDPA simulators are available since Linux 5.7, but the CI may have
    /// an older kernel, so for now we skip the test if we don't find
    /// the device.
    macro_rules! unwrap_not_found {
        ( $e:expr ) => {
            match $e {
                Ok(v) => v,
                Err(error) => match error {
                    Error::VhostOpen(ref e) if e.kind() == ErrorKind::NotFound => {
                        println!("Err: {:?} SKIPPED", e);
                        return;
                    }
                    e => panic!("Err: {:?}", e),
                },
            }
        };
    }

    #[test]
    #[serial]
    fn test_vdpa_kern_new_device() {
        let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
        let vdpa = unwrap_not_found!(VhostKernVdpa::new(VHOST_VDPA_PATH, &m));

        assert!(vdpa.as_raw_fd() >= 0);
        assert!(vdpa.mem().find_region(GuestAddress(0x100)).is_some());
        assert!(vdpa.mem().find_region(GuestAddress(0x10_0000)).is_none());
    }

    #[test]
    #[serial]
    fn test_vdpa_kern_is_valid() {
        let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
        let vdpa = unwrap_not_found!(VhostKernVdpa::new(VHOST_VDPA_PATH, &m));

        let mut config = VringConfigData {
            queue_max_size: 32,
            queue_size: 32,
            flags: 0,
            desc_table_addr: 0x1000,
            used_ring_addr: 0x2000,
            avail_ring_addr: 0x3000,
            log_addr: None,
        };
        assert_eq!(vdpa.is_valid(&config), true);

        config.queue_size = 0;
        assert_eq!(vdpa.is_valid(&config), false);
        config.queue_size = 31;
        assert_eq!(vdpa.is_valid(&config), false);
        config.queue_size = 33;
        assert_eq!(vdpa.is_valid(&config), false);
    }

    #[test]
    #[serial]
    fn test_vdpa_kern_ioctls() {
        let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
        let vdpa = unwrap_not_found!(VhostKernVdpa::new(VHOST_VDPA_PATH, &m));

        let features = vdpa.get_features().unwrap();
        // VIRTIO_F_VERSION_1 (bit 32) should be set
        assert_ne!(features & (1 << 32), 0);
        vdpa.set_features(features).unwrap();

        vdpa.set_owner().unwrap();

        vdpa.set_mem_table(&[]).unwrap_err();

        let region = VhostUserMemoryRegionInfo {
            guest_phys_addr: 0x0,
            memory_size: 0x10_0000,
            userspace_addr: m.get_host_address(GuestAddress(0x0)).unwrap() as u64,
            mmap_offset: 0,
            mmap_handle: -1,
        };
        vdpa.set_mem_table(&[region]).unwrap();

        assert!(vdpa.get_device_id().unwrap() > 0);

        assert_eq!(vdpa.get_status().unwrap(), 0x0);
        vdpa.set_status(0x1).unwrap();
        assert_eq!(vdpa.get_status().unwrap(), 0x1);

        let mut vec = vec![0u8; 8];
        vdpa.get_config(0, &mut vec).unwrap();
        vdpa.set_config(0, &vec).unwrap();

        let eventfd = EventFd::new(0).unwrap();

        // set_log_base() and set_log_fd() are not supported by vhost-vdpa
        vdpa.set_log_base(
            0x4000,
            Some(VhostUserDirtyLogRegion {
                mmap_size: 0x1000,
                mmap_offset: 0x10,
                mmap_handle: 1,
            }),
        )
        .unwrap_err();
        vdpa.set_log_base(0x4000, None).unwrap_err();
        vdpa.set_log_fd(eventfd.as_raw_fd()).unwrap_err();

        let max_queues = vdpa.get_vring_num().unwrap();
        vdpa.set_vring_num(0, max_queues + 1).unwrap_err();

        vdpa.set_vring_num(0, 32).unwrap();

        let config = VringConfigData {
            queue_max_size: 32,
            queue_size: 32,
            flags: 0,
            desc_table_addr: 0x1000,
            used_ring_addr: 0x2000,
            avail_ring_addr: 0x3000,
            log_addr: None,
        };
        vdpa.set_vring_addr(0, &config).unwrap();
        vdpa.set_vring_base(0, 1).unwrap();
        vdpa.set_vring_call(0, &eventfd).unwrap();
        vdpa.set_vring_kick(0, &eventfd).unwrap();
        vdpa.set_vring_err(0, &eventfd).unwrap();

        vdpa.set_config_call(&eventfd).unwrap();

        assert_eq!(vdpa.get_vring_base(0).unwrap(), 1);

        vdpa.set_vring_enable(0, true).unwrap();
        vdpa.set_vring_enable(0, false).unwrap();
    }

    #[test]
    #[serial]
    fn test_vdpa_kern_dma() {
        let m = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10_0000)]).unwrap();
        let mut vdpa = unwrap_not_found!(VhostKernVdpa::new(VHOST_VDPA_PATH, &m));

        let features = vdpa.get_features().unwrap();
        // VIRTIO_F_VERSION_1 (bit 32) should be set
        assert_ne!(features & (1 << 32), 0);
        vdpa.set_features(features).unwrap();

        let backend_features = vdpa.get_backend_features().unwrap();
        assert_ne!(backend_features & (1 << VHOST_BACKEND_F_IOTLB_MSG_V2), 0);
        vdpa.set_backend_features(backend_features).unwrap();

        vdpa.set_owner().unwrap();

        vdpa.dma_map(0xFFFF_0000, 0xFFFF, std::ptr::null::<u8>(), false)
            .unwrap_err();

        unsafe {
            let layout = Layout::from_size_align(0xFFFF, 1).unwrap();
            let ptr = alloc(layout);

            vdpa.dma_map(0xFFFF_0000, 0xFFFF, ptr, false).unwrap();
            vdpa.dma_unmap(0xFFFF_0000, 0xFFFF).unwrap();

            dealloc(ptr, layout);
        };
    }
}
<|endoftext|>


<commit_before>extern crate glium;
extern crate libc;

use std::ptr;
use std::rc::Rc;

/// Builds a context with dummy OpenGL functions.
pub fn build_context() -> Rc<glium::backend::Context> {
    unsafe {
        glium::backend::Context::new::<_, ()>(DummyBackend, false, Default::default()).unwrap()
    }
}

struct DummyBackend;

unsafe impl glium::backend::Backend for DummyBackend {
    fn swap_buffers(&self) -> Result<(), glium::SwapBuffersError> {
        Ok(())
    }

    unsafe fn get_proc_address(&self, symbol: &str) -> *const libc::c_void {
        match symbol {
            "glAttachShader" => {
                extern "system" fn attach(_: u32, _: u32) {}
                attach as *const _
            },

            "glBindBuffer" | "glBindTexture" | "glBindSampler" | "glBindFramebuffer" |
            "glBindVertexArray" => {
                extern "system" fn bind(_: u32, _: u32) {}
                bind as *const _
            },

            "glBufferData" => {
                extern "system" fn buffer_data(_: u32, _: isize, _: *const (), _: u32) {}
                buffer_data as *const _
            },

            "glCompileShader" => {
                extern "system" fn compile(_: u32) {}
                compile as *const _
            },

            "glClearColor" => {
                extern "system" fn clear_color(_: f32, _: f32, _: f32, _: f32) {}       // TOD
                clear_color as *const _
            },

            "glClear" => {
                extern "system" fn clear(_: u32) {}
                clear as *const _
            },

            "glCreateProgram" => {
                extern "system" fn create() -> u32 { 1 }
                create as *const _
            },

            "glCreateShader" => {
                extern "system" fn create_sh(_: u32) -> u32 { 1 }
                create_sh as *const _
            },

            "glDeleteFramebuffers" | "glDeleteBuffers" | "glDeleteSamplers" |
            "glDeleteTextures" | "glDeleteVertexArrays" => {
                extern "system" fn delete_mult(_: isize, _: *const u32) {}
                delete_mult as *const _
            },

            "glDeleteProgram" | "glDeleteShader" => {
                extern "system" fn delete(_: u32) {}
                delete as *const _
            },

            "glEnable" | "glDisable" => {
                extern "system" fn enable(_: u32) {}
                enable as *const _
            },

            "glFinish" => {
                extern "system" fn finish() {}
                finish as *const _
            },

            "glGenBuffers" | "glGenTextures" | "glGenFramebuffers" | "glGenRenderbuffers" |
            "glGenVertexArrays" | "glGenSamplers" => {
                extern "system" fn gen(num: usize, bufs: *mut u32) {
                    for i in (0 .. num) { unsafe { *bufs.offset(i as isize) = 1; } }
                }
                gen as *const _
            },

            "glGetBooleanv" => {
                extern "system" fn get_booleanv(name: u32, out: *mut u8) {
                    match name {
                        _ => unsafe { *out = 0; },
                    }
                }
                get_booleanv as *const _
            },

            "glGetBufferParameteriv" => {
                extern "system" fn get_buf_paramiv(_: u32, param: u32, out: *mut i32) {
                    match param {
                        0x8764 /* GL_BUFFER_SIZE */ => unsafe { *out = 60 },      // FIXME:
                        _ => unsafe { *out = 0; }
                    }
                }
                get_buf_paramiv as *const _
            }

            "glGetError" => {
                extern "system" fn get_error() -> u32 { 0 }
                get_error as *const _
            },

            "glGetFramebufferAttachmentParameteriv" => {
                extern "system" fn get_fbap(target: u32, atch: u32, pname: u32, params: *mut i32) {
                }
                get_fbap as *const _
            },

            "glGetIntegerv" => {
                extern "system" fn get_integerv(name: u32, out: *mut i32) {
                    match name {
                        0x821D /* GL_NUM_EXTENSIONS */ => unsafe { *out = 0; },
                        _ => unsafe { *out = 0; },
                    }
                }
                get_integerv as *const _
            },

            "glGetProgramiv" => {
                extern "system" fn get_progiv(_: u32, param: u32, out: *mut i32) {
                    match param {
                        0x8B82 /* GL_LINK_STATUS */ => unsafe { *out = 1; },
                        0x8918 /* GL_GEOMETRY_OUTPUT_TYPE */ => unsafe { *out = 4; /* GL_TRIANGLES */ },
                        _ => unsafe { *out = 0; }
                    }
                }
                get_progiv as *const _
            },

            "glGetShaderiv" => {
                extern "system" fn get_shaderiv(_: u32, param: u32, out: *mut i32) {
                    match param {
                        0x8B81 /* GL_COMPILE_STATUS */ => unsafe { *out = 1 },
                        _ => unsafe { *out = 0; }
                    }
                }
                get_shaderiv as *const _
            },

            "glGetString" => {
                extern "system" fn get_string(name: u32) -> *const i8 {
                    match name {
                        0x1F02 /* GL_VERSION */ => b"3.3 3.3.0\0".as_ptr() as *const _,
                        _ => b"\0".as_ptr() as *const _,
                    }
                }
                get_string as *const _
            },

            "glLinkProgram" => {
                extern "system" fn link(_: u32) {}
                link as *const _
            },

            "glShaderSource" => {
                extern "system" fn shader_source(_: u32, _: isize,
                                                 _: *const *const i8, _: *const i32) {}
                shader_source as *const _
            }

            "glUseProgram" => {
                extern "system" fn use_program(_: u32) {}
                use_program as *const _
            },

            name => ptr::null()
        }
    }

    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        (800, 600)
    }

    fn is_current(&self) -> bool {
        true
    }

    unsafe fn make_current(&self) {
    }
}
<commit_msg>fix context template<commit_after>extern crate glium;
extern crate libc;

use std::ptr;
use std::rc::Rc;

/// Builds a context with dummy OpenGL functions.
pub fn build_context() -> Rc<glium::backend::Context> {
    unsafe {
        glium::backend::Context::new::<_>(DummyBackend, false, Default::default()).unwrap()
    }
}

struct DummyBackend;

unsafe impl glium::backend::Backend for DummyBackend {
    fn swap_buffers(&self) -> Result<(), glium::SwapBuffersError> {
        Ok(())
    }

    unsafe fn get_proc_address(&self, symbol: &str) -> *const libc::c_void {
        match symbol {
            "glAttachShader" => {
                extern "system" fn attach(_: u32, _: u32) {}
                attach as *const _
            },

            "glBindBuffer" | "glBindTexture" | "glBindSampler" | "glBindFramebuffer" |
            "glBindVertexArray" => {
                extern "system" fn bind(_: u32, _: u32) {}
                bind as *const _
            },

            "glBufferData" => {
                extern "system" fn buffer_data(_: u32, _: isize, _: *const (), _: u32) {}
                buffer_data as *const _
            },

            "glCompileShader" => {
                extern "system" fn compile(_: u32) {}
                compile as *const _
            },

            "glClearColor" => {
                extern "system" fn clear_color(_: f32, _: f32, _: f32, _: f32) {}       // TOD
                clear_color as *const _
            },

            "glClear" => {
                extern "system" fn clear(_: u32) {}
                clear as *const _
            },

            "glCreateProgram" => {
                extern "system" fn create() -> u32 { 1 }
                create as *const _
            },

            "glCreateShader" => {
                extern "system" fn create_sh(_: u32) -> u32 { 1 }
                create_sh as *const _
            },

            "glDeleteFramebuffers" | "glDeleteBuffers" | "glDeleteSamplers" |
            "glDeleteTextures" | "glDeleteVertexArrays" => {
                extern "system" fn delete_mult(_: isize, _: *const u32) {}
                delete_mult as *const _
            },

            "glDeleteProgram" | "glDeleteShader" => {
                extern "system" fn delete(_: u32) {}
                delete as *const _
            },

            "glEnable" | "glDisable" => {
                extern "system" fn enable(_: u32) {}
                enable as *const _
            },

            "glFinish" => {
                extern "system" fn finish() {}
                finish as *const _
            },

            "glGenBuffers" | "glGenTextures" | "glGenFramebuffers" | "glGenRenderbuffers" |
            "glGenVertexArrays" | "glGenSamplers" => {
                extern "system" fn gen(num: usize, bufs: *mut u32) {
                    for i in (0 .. num) { unsafe { *bufs.offset(i as isize) = 1; } }
                }
                gen as *const _
            },

            "glGetBooleanv" => {
                extern "system" fn get_booleanv(name: u32, out: *mut u8) {
                    match name {
                        _ => unsafe { *out = 0; },
                    }
                }
                get_booleanv as *const _
            },

            "glGetBufferParameteriv" => {
                extern "system" fn get_buf_paramiv(_: u32, param: u32, out: *mut i32) {
                    match param {
                        0x8764 /* GL_BUFFER_SIZE */ => unsafe { *out = 60 },      // FIXME:
                        _ => unsafe { *out = 0; }
                    }
                }
                get_buf_paramiv as *const _
            }

            "glGetError" => {
                extern "system" fn get_error() -> u32 { 0 }
                get_error as *const _
            },

            "glGetFramebufferAttachmentParameteriv" => {
                extern "system" fn get_fbap(target: u32, atch: u32, pname: u32, params: *mut i32) {
                }
                get_fbap as *const _
            },

            "glGetIntegerv" => {
                extern "system" fn get_integerv(name: u32, out: *mut i32) {
                    match name {
                        0x821D /* GL_NUM_EXTENSIONS */ => unsafe { *out = 0; },
                        _ => unsafe { *out = 0; },
                    }
                }
                get_integerv as *const _
            },

            "glGetProgramiv" => {
                extern "system" fn get_progiv(_: u32, param: u32, out: *mut i32) {
                    match param {
                        0x8B82 /* GL_LINK_STATUS */ => unsafe { *out = 1; },
                        0x8918 /* GL_GEOMETRY_OUTPUT_TYPE */ => unsafe { *out = 4; /* GL_TRIANGLES */ },
                        _ => unsafe { *out = 0; }
                    }
                }
                get_progiv as *const _
            },

            "glGetShaderiv" => {
                extern "system" fn get_shaderiv(_: u32, param: u32, out: *mut i32) {
                    match param {
                        0x8B81 /* GL_COMPILE_STATUS */ => unsafe { *out = 1 },
                        _ => unsafe { *out = 0; }
                    }
                }
                get_shaderiv as *const _
            },

            "glGetString" => {
                extern "system" fn get_string(name: u32) -> *const i8 {
                    match name {
                        0x1F02 /* GL_VERSION */ => b"3.3 3.3.0\0".as_ptr() as *const _,
                        _ => b"\0".as_ptr() as *const _,
                    }
                }
                get_string as *const _
            },

            "glLinkProgram" => {
                extern "system" fn link(_: u32) {}
                link as *const _
            },

            "glShaderSource" => {
                extern "system" fn shader_source(_: u32, _: isize,
                                                 _: *const *const i8, _: *const i32) {}
                shader_source as *const _
            }

            "glUseProgram" => {
                extern "system" fn use_program(_: u32) {}
                use_program as *const _
            },

            name => ptr::null()
        }
    }

    fn get_framebuffer_dimensions(&self) -> (u32, u32) {
        (800, 600)
    }

    fn is_current(&self) -> bool {
        true
    }

    unsafe fn make_current(&self) {
    }
}
<|endoftext|>