<commit_before>use gfx;
use gfx::traits::FactoryExt;

extern crate cgmath;
extern crate gfx_text;

pub static LIGHTING_VERTEX_SRC: &'static [u8] = b"

#version 150 core

layout (std140) uniform cb_CameraArgs {
    uniform mat4 u_Proj;
    uniform mat4 u_View;
};

layout (std140) uniform cb_ModelArgs {
    uniform mat4 u_Model;
};

in vec3 a_Pos;
in vec3 a_Normal;
in vec3 a_Tangent;
in vec2 a_TexCoord;

out VertexData {
    vec4 Position;
    vec3 Normal;
    mat3 TBN;
    vec2 TexCoord;
} v_Out;

void main() {
    v_Out.Position = u_Model * vec4(a_Pos, 1.0);
    mat3 model = mat3(u_Model);
	vec3 normal = normalize(model * a_Normal);

    v_Out.Normal = normal;
	vec3 tangent = normalize(model * a_Tangent);
	vec3 bitangent = cross(normal, tangent);

    v_Out.TBN = mat3(tangent, bitangent, normal);

    v_Out.TexCoord = a_TexCoord;
    gl_Position = u_Proj * u_View * v_Out.Position;
}

";
const MAX_NUM_TOTAL_LIGHTS: usize = 16;

pub static LIGHTING_FRAGMENT_SRC: &'static [u8] = b"

#version 150 core
#define MAX_NUM_TOTAL_LIGHTS 16

const float PI = 3.1415926535897932384626433832795;
const float PI_2 = 1.57079632679489661923;

layout (std140) uniform cb_FragmentArgs {
    int u_LightCount;
};

struct Light {
    vec4 propagation;
    vec4 center;
    vec4 color;
};

layout (std140) uniform u_Lights {
    Light light[MAX_NUM_TOTAL_LIGHTS];
};

layout (std140) uniform cb_MaterialArgs {
    uniform vec4 u_Emissive;
};

in VertexData {
    vec4 Position;
    vec3 Normal;
    mat3 TBN;
    vec2 TexCoord;
} v_In;

out vec4 o_Color;

void main() {
    vec4 kd = vec4(0.2, 0.2, 0.2, 1.0);
    vec4 ks = vec4(1.0, 1.0, 1.0, 1.0);
    vec4 kp = vec4(64.0, 32.0, 64.0, 64.0);
    vec4 ka = vec4(0.0, 0.0, 0.01, 0.0);

    vec4 color = (ka + u_Emissive);

    float dx = v_In.TexCoord.x - 0.5;
    float dy = v_In.TexCoord.y - 0.5;
	float r = dx * dx + dy * dy;
	vec3 normal_map = vec3(0., 0., 1.);
    if (r > 0.25) {
        discard;
    }
    else {
	    dx *= 2;
	    dy *= 2;

		float bump = sqrt(1. - dx * dx - dy * dy);
		normal_map = vec3(dx, dy, bump);
	};

	vec3 normal = v_In.TBN * normal_map;

	for (int i = 0; i < u_LightCount; i++) {
		vec4 delta = light[i].center - v_In.Position;
		float dist = length(delta);
		float inv_dist = 1. / dist;
		vec4 light_to_point_normal = delta * inv_dist;
		float intensity = dot(light[i].propagation.xyz, vec3(1., inv_dist, inv_dist * inv_dist));

		float lambert = max(0, dot(light_to_point_normal, vec4(normal, 0.0)));

		vec4 specular;
		if (lambert >= 0.0) {
//			Blinn-Phong:
			vec3 lightDir = light_to_point_normal.xyz;
            vec3 viewDir = vec3(0.0, 0.0, 1.0); // ortho, normalize(-v_In.Position.xyz); perspective
            vec3 halfDir = normalize(lightDir + viewDir); // can be done in vertex shader
			float specAngle = max(dot(halfDir, normal), 0.0);
//			Phong:
//	        vec3 reflectDir = reflect(-lightDir, v_In.Normal.xyz);
//	        float specAngle = max(dot(reflectDir, viewDir), 0.0);
			specular = pow(vec4(specAngle), kp);
		}
		else {
			specular = vec4(0.0);
		}
		color += light[i].color * intensity * (kd * lambert + ks * specular);
	}
	// gl_FragDepth = bump;
	o_Color = color;
}

";

gfx_vertex_struct!(VertexPosNormal {
	pos: [f32; 3] = "a_Pos",
	normal: [f32; 3] = "a_Normal",
	tangent: [f32; 3] = "a_Tangent",
	tex_coord: [f32; 2] = "a_TexCoord",
});

pub type Vertex = VertexPosNormal;
pub type HDRColorFormat = (gfx::format::R16_G16_B16_A16, gfx::format::Float);
pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub type GFormat = [f32; 4];

pub type M44 = cgmath::Matrix4<f32>;

gfx_defines!(
    constant PointLight {
        propagation: [f32; 4] = "propagation",
        center: [f32; 4] = "center",
        color: [f32; 4] = "color",
    }

    constant CameraArgs {
        proj: [[f32; 4]; 4] = "u_Proj",
        view: [[f32; 4]; 4] = "u_View",
    }

    constant ModelArgs {
        model: [[f32; 4]; 4] = "u_Model",
    }

    constant FragmentArgs {
        light_count: i32 = "u_LightCount",
    }

	constant MaterialArgs {
		emissive: [f32; 4] = "u_Emissive",
	}

    pipeline shaded {
        vbuf: gfx::VertexBuffer<VertexPosNormal> = (),
        camera_args: gfx::ConstantBuffer<CameraArgs> = "cb_CameraArgs",
        model_args: gfx::ConstantBuffer<ModelArgs> = "cb_ModelArgs",
        fragment_args: gfx::ConstantBuffer<FragmentArgs> = "cb_FragmentArgs",
        material_args: gfx::ConstantBuffer<MaterialArgs> = "cb_MaterialArgs",
        lights: gfx::ConstantBuffer<PointLight> = "u_Lights",
        color_target: gfx::RenderTarget<HDRColorFormat> = "o_Color",
        depth_target: gfx::DepthTarget<gfx::format::DepthStencil> = gfx::preset::depth::LESS_EQUAL_WRITE,
    }
);

use std::marker::PhantomData;
pub struct ForwardLighting<R: gfx::Resources, C: gfx::CommandBuffer<R>> {
	camera: gfx::handle::Buffer<R, CameraArgs>,
	model: gfx::handle::Buffer<R, ModelArgs>,
	fragment: gfx::handle::Buffer<R, FragmentArgs>,
	material: gfx::handle::Buffer<R, MaterialArgs>,
	lights: gfx::handle::Buffer<R, PointLight>,
	pso: gfx::pso::PipelineState<R, shaded::Meta>,
	_buffer: PhantomData<C>,
}

impl<R: gfx::Resources, C: gfx::CommandBuffer<R>> ForwardLighting<R, C> {
	pub fn new<F>(factory: &mut F) -> ForwardLighting<R, C>
		where F: gfx::Factory<R> {
		let lights = factory.create_constant_buffer(MAX_NUM_TOTAL_LIGHTS);
		let camera = factory.create_constant_buffer(1);
		let model = factory.create_constant_buffer(1);
		let fragment = factory.create_constant_buffer(1);
		let material = factory.create_constant_buffer(1);
		let pso = factory.create_pipeline_simple(LIGHTING_VERTEX_SRC, LIGHTING_FRAGMENT_SRC, shaded::new()).unwrap();

		ForwardLighting {
			camera: camera,
			model: model,
			fragment: fragment,
			material: material,
			lights: lights,
			pso: pso,
			_buffer: PhantomData,
		}
	}

	pub fn setup(&self,
	             encoder: &mut gfx::Encoder<R, C>,
	             camera_projection: M44,
	             camera_view: M44,
	             lights: &Vec<PointLight>) {

		let mut lights_buf = lights.clone();

		let count = lights_buf.len();
		while lights_buf.len() < MAX_NUM_TOTAL_LIGHTS {
			lights_buf.push(PointLight {
				propagation: [0., 0., 0., 0.],
				color: [0., 0., 0., 0.],
				center: [0., 0., 0., 0.],
			})
		}

		encoder.update_buffer(&self.lights, &lights_buf[..], 0).unwrap();

		encoder.update_constant_buffer(&self.camera,
		                               &CameraArgs {
			                               proj: camera_projection.into(),
			                               view: camera_view.into(),
		                               });

		encoder.update_constant_buffer(&self.fragment, &FragmentArgs { light_count: count as i32 });
	}

	pub fn draw_triangles(&self,
	                      encoder: &mut gfx::Encoder<R, C>,
	                      vertices: &gfx::handle::Buffer<R, VertexPosNormal>,
	                      indices: &gfx::Slice<R>,
	                      transform: &M44,
	                      color: [f32; 4],
	                      color_buffer: &gfx::handle::RenderTargetView<R, HDRColorFormat>,
	                      depth_buffer: &gfx::handle::DepthStencilView<R, DepthFormat>) {

		encoder.update_constant_buffer(&self.model, &ModelArgs { model: transform.clone().into() });

		encoder.update_constant_buffer(&self.material, &MaterialArgs { emissive: color });

		encoder.draw(&indices,
		             &self.pso,
		             &shaded::Data {
			             vbuf: vertices.clone(),
			             fragment_args: self.fragment.clone(),
			             material_args: self.material.clone(),
			             camera_args: self.camera.clone(),
			             model_args: self.model.clone(),
			             lights: self.lights.clone(),
			             color_target: color_buffer.clone(),
			             depth_target: depth_buffer.clone(),
		             });
	}
}
<commit_msg>Splitting shader<commit_after>use gfx;
use gfx::traits::FactoryExt;

extern crate cgmath;
extern crate gfx_text;

pub static LIGHTING_VERTEX_SRC: &'static [u8] = b"

#version 150 core

layout (std140) uniform cb_CameraArgs {
    uniform mat4 u_Proj;
    uniform mat4 u_View;
};

layout (std140) uniform cb_ModelArgs {
    uniform mat4 u_Model;
};

in vec3 a_Pos;
in vec3 a_Normal;
in vec3 a_Tangent;
in vec2 a_TexCoord;

out VertexData {
    vec4 Position;
    vec3 Normal;
    mat3 TBN;
    vec2 TexCoord;
} v_Out;

void main() {
    v_Out.Position = u_Model * vec4(a_Pos, 1.0);
    mat3 model = mat3(u_Model);
	vec3 normal = normalize(model * a_Normal);

    v_Out.Normal = normal;
	vec3 tangent = normalize(model * a_Tangent);
	vec3 bitangent = cross(normal, tangent);

    v_Out.TBN = mat3(tangent, bitangent, normal);

    v_Out.TexCoord = a_TexCoord;
    gl_Position = u_Proj * u_View * v_Out.Position;
}

";
const MAX_NUM_TOTAL_LIGHTS: usize = 16;

pub static LIGHTING_BALL_FRAGMENT_SRC: &'static [u8] = b"

#version 150 core
#define MAX_NUM_TOTAL_LIGHTS 16

const float PI = 3.1415926535897932384626433832795;
const float PI_2 = 1.57079632679489661923;

layout (std140) uniform cb_FragmentArgs {
    int u_LightCount;
};

struct Light {
    vec4 propagation;
    vec4 center;
    vec4 color;
};

layout (std140) uniform u_Lights {
    Light light[MAX_NUM_TOTAL_LIGHTS];
};

layout (std140) uniform cb_MaterialArgs {
    uniform vec4 u_Emissive;
};

in VertexData {
    vec4 Position;
    vec3 Normal;
    mat3 TBN;
    vec2 TexCoord;
} v_In;

out vec4 o_Color;

void main() {
    vec4 kd = vec4(0.2, 0.2, 0.2, 1.0);
    vec4 ks = vec4(1.0, 1.0, 1.0, 1.0);
    vec4 kp = vec4(64.0, 32.0, 64.0, 64.0);
    vec4 ka = vec4(0.0, 0.0, 0.01, 0.0);

    vec4 color = (ka + u_Emissive);

    float dx = v_In.TexCoord.x - 0.5;
    float dy = v_In.TexCoord.y - 0.5;
	float r = dx * dx + dy * dy;
	vec3 normal_map = vec3(0., 0., 1.);
    if (r > 0.25) {
        discard;
    }
    else {
	    dx *= 2;
	    dy *= 2;

		float bump = sqrt(1. - dx * dx - dy * dy);
		normal_map = vec3(dx, dy, bump);
	};

	vec3 normal = v_In.TBN * normal_map;

	for (int i = 0; i < u_LightCount; i++) {
		vec4 delta = light[i].center - v_In.Position;
		float dist = length(delta);
		float inv_dist = 1. / dist;
		vec4 light_to_point_normal = delta * inv_dist;
		float intensity = dot(light[i].propagation.xyz, vec3(1., inv_dist, inv_dist * inv_dist));

		float lambert = max(0, dot(light_to_point_normal, vec4(normal, 0.0)));

		vec4 specular;
		if (lambert >= 0.0) {
//			Blinn-Phong:
			vec3 lightDir = light_to_point_normal.xyz;
            vec3 viewDir = vec3(0.0, 0.0, 1.0); // ortho, normalize(-v_In.Position.xyz); perspective
            vec3 halfDir = normalize(lightDir + viewDir); // can be done in vertex shader
			float specAngle = max(dot(halfDir, normal), 0.0);
//			Phong:
//	        vec3 reflectDir = reflect(-lightDir, v_In.Normal.xyz);
//	        float specAngle = max(dot(reflectDir, viewDir), 0.0);
			specular = pow(vec4(specAngle), kp);
		}
		else {
			specular = vec4(0.0);
		}
		color += light[i].color * intensity * (kd * lambert + ks * specular);
	}
	// gl_FragDepth = bump;
	o_Color = color;
}

";

pub static LIGHTING_POLY_FRAGMENT_SRC: &'static [u8] = b"

#version 150 core
#define MAX_NUM_TOTAL_LIGHTS 16

const float PI = 3.1415926535897932384626433832795;
const float PI_2 = 1.57079632679489661923;

layout (std140) uniform cb_FragmentArgs {
    int u_LightCount;
};

struct Light {
    vec4 propagation;
    vec4 center;
    vec4 color;
};

layout (std140) uniform u_Lights {
    Light light[MAX_NUM_TOTAL_LIGHTS];
};

layout (std140) uniform cb_MaterialArgs {
    uniform vec4 u_Emissive;
};

in VertexData {
    vec4 Position;
    vec3 Normal;
    mat3 TBN;
    vec2 TexCoord;
} v_In;

out vec4 o_Color;

void main() {
    vec4 kd = vec4(0.2, 0.2, 0.2, 1.0);
    vec4 ks = vec4(1.0, 1.0, 1.0, 1.0);
    vec4 kp = vec4(64.0, 32.0, 64.0, 64.0);
    vec4 ka = vec4(0.0, 0.0, 0.01, 0.0);

    vec4 color = (ka + u_Emissive);

    float dx = v_In.TexCoord.x - 0.5;
    float dy = v_In.TexCoord.y - 0.5;
	float r = dx * dx + dy * dy;
	vec3 normal_map = vec3(0., 0., 1.);
    if (r > 0.25) {
        discard;
    }
    else {
	    dx *= 2;
	    dy *= 2;

		float bump = sqrt(1. - dx * dx - dy * dy);
		normal_map = vec3(dx, dy, bump);
	};

	vec3 normal = v_In.TBN * normal_map;

	for (int i = 0; i < u_LightCount; i++) {
		vec4 delta = light[i].center - v_In.Position;
		float dist = length(delta);
		float inv_dist = 1. / dist;
		vec4 light_to_point_normal = delta * inv_dist;
		float intensity = dot(light[i].propagation.xyz, vec3(1., inv_dist, inv_dist * inv_dist));

		float lambert = max(0, dot(light_to_point_normal, vec4(normal, 0.0)));

		vec4 specular;
		if (lambert >= 0.0) {
//			Blinn-Phong:
			vec3 lightDir = light_to_point_normal.xyz;
            vec3 viewDir = vec3(0.0, 0.0, 1.0); // ortho, normalize(-v_In.Position.xyz); perspective
            vec3 halfDir = normalize(lightDir + viewDir); // can be done in vertex shader
			float specAngle = max(dot(halfDir, normal), 0.0);
//			Phong:
//	        vec3 reflectDir = reflect(-lightDir, v_In.Normal.xyz);
//	        float specAngle = max(dot(reflectDir, viewDir), 0.0);
			specular = pow(vec4(specAngle), kp);
		}
		else {
			specular = vec4(0.0);
		}
		color += light[i].color * intensity * (kd * lambert + ks * specular);
	}
	// gl_FragDepth = bump;
	o_Color = color;
}

";

gfx_vertex_struct!(VertexPosNormal {
	pos: [f32; 3] = "a_Pos",
	normal: [f32; 3] = "a_Normal",
	tangent: [f32; 3] = "a_Tangent",
	tex_coord: [f32; 2] = "a_TexCoord",
});

pub type Vertex = VertexPosNormal;
pub type HDRColorFormat = (gfx::format::R16_G16_B16_A16, gfx::format::Float);
pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub type GFormat = [f32; 4];

pub type M44 = cgmath::Matrix4<f32>;

gfx_defines!(
    constant PointLight {
        propagation: [f32; 4] = "propagation",
        center: [f32; 4] = "center",
        color: [f32; 4] = "color",
    }

    constant CameraArgs {
        proj: [[f32; 4]; 4] = "u_Proj",
        view: [[f32; 4]; 4] = "u_View",
    }

    constant ModelArgs {
        model: [[f32; 4]; 4] = "u_Model",
    }

    constant FragmentArgs {
        light_count: i32 = "u_LightCount",
    }

	constant MaterialArgs {
		emissive: [f32; 4] = "u_Emissive",
	}

    pipeline shaded {
        vbuf: gfx::VertexBuffer<VertexPosNormal> = (),
        camera_args: gfx::ConstantBuffer<CameraArgs> = "cb_CameraArgs",
        model_args: gfx::ConstantBuffer<ModelArgs> = "cb_ModelArgs",
        fragment_args: gfx::ConstantBuffer<FragmentArgs> = "cb_FragmentArgs",
        material_args: gfx::ConstantBuffer<MaterialArgs> = "cb_MaterialArgs",
        lights: gfx::ConstantBuffer<PointLight> = "u_Lights",
        color_target: gfx::RenderTarget<HDRColorFormat> = "o_Color",
        depth_target: gfx::DepthTarget<gfx::format::DepthStencil> = gfx::preset::depth::LESS_EQUAL_WRITE,
    }
);

use std::marker::PhantomData;
pub struct ForwardLighting<R: gfx::Resources, C: gfx::CommandBuffer<R>> {
	camera: gfx::handle::Buffer<R, CameraArgs>,
	model: gfx::handle::Buffer<R, ModelArgs>,
	fragment: gfx::handle::Buffer<R, FragmentArgs>,
	material: gfx::handle::Buffer<R, MaterialArgs>,
	lights: gfx::handle::Buffer<R, PointLight>,
	ball_pso: gfx::pso::PipelineState<R, shaded::Meta>,
	poly_pso: gfx::pso::PipelineState<R, shaded::Meta>,
	_buffer: PhantomData<C>,
}

impl<R: gfx::Resources, C: gfx::CommandBuffer<R>> ForwardLighting<R, C> {
	pub fn new<F>(factory: &mut F) -> ForwardLighting<R, C>
		where F: gfx::Factory<R> {
		let lights = factory.create_constant_buffer(MAX_NUM_TOTAL_LIGHTS);
		let camera = factory.create_constant_buffer(1);
		let model = factory.create_constant_buffer(1);
		let fragment = factory.create_constant_buffer(1);
		let material = factory.create_constant_buffer(1);
		let ball_pso = factory.create_pipeline_simple(LIGHTING_VERTEX_SRC,
		                                              LIGHTING_BALL_FRAGMENT_SRC,
		                                              shaded::new())
		                      .unwrap();
		let poly_pso = factory.create_pipeline_simple(LIGHTING_VERTEX_SRC,
		                                              LIGHTING_POLY_FRAGMENT_SRC,
		                                              shaded::new())
		                      .unwrap();

		ForwardLighting {
			camera: camera,
			model: model,
			fragment: fragment,
			material: material,
			lights: lights,
			ball_pso: ball_pso,
			poly_pso: poly_pso,
			_buffer: PhantomData,
		}
	}

	pub fn setup(&self,
	             encoder: &mut gfx::Encoder<R, C>,
	             camera_projection: M44,
	             camera_view: M44,
	             lights: &Vec<PointLight>) {

		let mut lights_buf = lights.clone();

		let count = lights_buf.len();
		while lights_buf.len() < MAX_NUM_TOTAL_LIGHTS {
			lights_buf.push(PointLight {
				propagation: [0., 0., 0., 0.],
				color: [0., 0., 0., 0.],
				center: [0., 0., 0., 0.],
			})
		}

		encoder.update_buffer(&self.lights, &lights_buf[..], 0).unwrap();

		encoder.update_constant_buffer(&self.camera,
		                               &CameraArgs {
			                               proj: camera_projection.into(),
			                               view: camera_view.into(),
		                               });

		encoder.update_constant_buffer(&self.fragment, &FragmentArgs { light_count: count as i32 });
	}

	pub fn draw_triangles(&self,
	                      encoder: &mut gfx::Encoder<R, C>,
	                      vertices: &gfx::handle::Buffer<R, VertexPosNormal>,
	                      indices: &gfx::Slice<R>,
	                      transform: &M44,
	                      color: [f32; 4],
	                      color_buffer: &gfx::handle::RenderTargetView<R, HDRColorFormat>,
	                      depth_buffer: &gfx::handle::DepthStencilView<R, DepthFormat>) {
		self.draw_triangles_pso(encoder,
		                        &self.ball_pso,
		                        vertices,
		                        indices,
		                        transform,
		                        color,
		                        color_buffer,
		                        depth_buffer);
	}

	fn draw_triangles_pso(&self,
	                      encoder: &mut gfx::Encoder<R, C>,
	                      pso: &gfx::PipelineState<R, shaded::Meta>,
	                      vertices: &gfx::handle::Buffer<R, VertexPosNormal>,
	                      indices: &gfx::Slice<R>,
	                      transform: &M44,
	                      color: [f32; 4],
	                      color_buffer: &gfx::handle::RenderTargetView<R, HDRColorFormat>,
	                      depth_buffer: &gfx::handle::DepthStencilView<R, DepthFormat>) {

		encoder.update_constant_buffer(&self.model, &ModelArgs { model: transform.clone().into() });

		encoder.update_constant_buffer(&self.material, &MaterialArgs { emissive: color });

		encoder.draw(&indices,
		             &pso,
		             &shaded::Data {
			             vbuf: vertices.clone(),
			             fragment_args: self.fragment.clone(),
			             material_args: self.material.clone(),
			             camera_args: self.camera.clone(),
			             model_args: self.model.clone(),
			             lights: self.lights.clone(),
			             color_target: color_buffer.clone(),
			             depth_target: depth_buffer.clone(),
		             });
	}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use byteorder::{LittleEndian, ReadBytesExt};
use frame::Frame;
use internal_types::{FontTemplate, ResultMsg, RendererFrame};
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcReceiver};
use profiler::BackendProfileCounters;
use resource_cache::ResourceCache;
use scene::Scene;
use scoped_threadpool;
use std::collections::HashMap;
use std::io::{Cursor, Read};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace};
use webrender_traits::{PipelineId, RenderNotifier, WebGLContextId};
use batch::new_id;
use device::TextureId;
use tiling::FrameBuilderConfig;
use offscreen_gl_context::{ColorAttachmentType, GLContext};
use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle};

pub struct RenderBackend {
    api_rx: IpcReceiver<ApiMsg>,
    payload_rx: IpcBytesReceiver,
    payload_tx: IpcBytesSender,
    result_tx: Sender<ResultMsg>,

    device_pixel_ratio: f32,
    next_namespace_id: IdNamespace,

    thread_pool: scoped_threadpool::Pool,
    resource_cache: ResourceCache,

    scene: Scene,
    frame: Frame,

    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
    webrender_context_handle: Option<NativeGLContextHandle>,
    webgl_contexts: HashMap<WebGLContextId, GLContext<NativeGLContext>>,
    current_bound_webgl_context_id: Option<WebGLContextId>,
}

impl RenderBackend {
    pub fn new(api_rx: IpcReceiver<ApiMsg>,
               payload_rx: IpcBytesReceiver,
               payload_tx: IpcBytesSender,
               result_tx: Sender<ResultMsg>,
               device_pixel_ratio: f32,
               white_image_id: TextureCacheItemId,
               texture_cache: TextureCache,
               enable_aa: bool,
               notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
               webrender_context_handle: Option<NativeGLContextHandle>,
               config: FrameBuilderConfig,
               debug: bool) -> RenderBackend {
        let mut thread_pool = scoped_threadpool::Pool::new(8);

        let resource_cache = ResourceCache::new(&mut thread_pool,
                                                texture_cache,
                                                white_image_id,
                                                device_pixel_ratio,
                                                enable_aa);

        RenderBackend {
            thread_pool: thread_pool,
            api_rx: api_rx,
            payload_rx: payload_rx,
            payload_tx: payload_tx,
            result_tx: result_tx,
            device_pixel_ratio: device_pixel_ratio,
            resource_cache: resource_cache,
            scene: Scene::new(),
            frame: Frame::new(debug, config),
            next_namespace_id: IdNamespace(1),
            notifier: notifier,
            webrender_context_handle: webrender_context_handle,
            webgl_contexts: HashMap::new(),
            current_bound_webgl_context_id: None,
        }
    }

    pub fn run(&mut self) {
        let mut profile_counters = BackendProfileCounters::new();

        loop {
            let msg = self.api_rx.recv();
            match msg {
                Ok(msg) => {
                    match msg {
                        ApiMsg::AddRawFont(id, bytes) => {
                            profile_counters.font_templates.inc(bytes.len());
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                        }
                        ApiMsg::AddNativeFont(id, native_font_handle) => {
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Native(native_font_handle));
                        }
                        ApiMsg::AddImage(id, width, height, format, bytes) => {
                            profile_counters.image_templates.inc(bytes.len());
                            self.resource_cache.add_image_template(id,
                                                                   width,
                                                                   height,
                                                                   format,
                                                                   bytes);
                        }
                        ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                            self.resource_cache.update_image_template(id,
                                                                      width,
                                                                      height,
                                                                      format,
                                                                      bytes);
                        }
                        ApiMsg::DeleteImage(id) => {
                            self.resource_cache.delete_image_template(id);
                        }
                        ApiMsg::CloneApi(sender) => {
                            let result = self.next_namespace_id;

                            let IdNamespace(id_namespace) = self.next_namespace_id;
                            self.next_namespace_id = IdNamespace(id_namespace + 1);

                            sender.send(result).unwrap();
                        }
                        ApiMsg::SetRootStackingContext(stacking_context_id,
                                                       background_color,
                                                       epoch,
                                                       pipeline_id,
                                                       viewport_size,
                                                       stacking_contexts,
                                                       display_lists,
                                                       auxiliary_lists_descriptor) => {
                            for (id, stacking_context) in stacking_contexts.into_iter() {
                                self.scene.add_stacking_context(id,
                                                                pipeline_id,
                                                                epoch,
                                                                stacking_context);
                            }

                            let mut leftover_auxiliary_data = vec![];
                            let mut auxiliary_data;
                            loop {
                                auxiliary_data = self.payload_rx.recv().unwrap();
                                {
                                    let mut payload_reader = Cursor::new(&auxiliary_data[..]);
                                    let payload_stacking_context_id =
                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                    let payload_epoch =
                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                    if payload_epoch == epoch.0 &&
                                            payload_stacking_context_id == stacking_context_id.0 {
                                        break
                                    }
                                }
                                leftover_auxiliary_data.push(auxiliary_data)
                            }
                            for leftover_auxiliary_data in leftover_auxiliary_data {
                                self.payload_tx.send(&leftover_auxiliary_data[..]).unwrap()
                            }

                            let mut auxiliary_data = Cursor::new(&mut auxiliary_data[8..]);
                            for (display_list_id,
                                 display_list_descriptor) in display_lists.into_iter() {
                                let mut built_display_list_data =
                                    vec![0; display_list_descriptor.size()];
                                auxiliary_data.read_exact(&mut built_display_list_data[..])
                                              .unwrap();
                                let built_display_list =
                                    BuiltDisplayList::from_data(built_display_list_data,
                                                                display_list_descriptor);
                                self.scene.add_display_list(display_list_id,
                                                            pipeline_id,
                                                            epoch,
                                                            built_display_list,
                                                            &mut self.resource_cache);
                            }

                            let mut auxiliary_lists_data =
                                vec![0; auxiliary_lists_descriptor.size()];
                            auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
                            let auxiliary_lists =
                                AuxiliaryLists::from_data(auxiliary_lists_data,
                                                          auxiliary_lists_descriptor);
//                            hprof::start_frame();
                            let frame = profile_counters.total_time.profile(|| {
//                                let _pf = hprof::enter("root1");
                                self.scene.set_root_stacking_context(pipeline_id,
                                                                     epoch,
                                                                     stacking_context_id,
                                                                     background_color,
                                                                     viewport_size,
                                                                     &mut self.resource_cache,
                                                                     auxiliary_lists);

                                self.build_scene();
                                self.render()
                            });
//                            hprof::end_frame();
                            //hprof::profiler().print_timing();

                            self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                        }
                        ApiMsg::SetRootPipeline(pipeline_id) => {
//                            hprof::start_frame();
                            let frame = profile_counters.total_time.profile(|| {
//                                let _pf = hprof::enter("root2");
                                self.scene.set_root_pipeline_id(pipeline_id);

                                self.build_scene();
                                self.render()
                            });
//                            hprof::end_frame();
                            //hprof::profiler().print_timing();

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::Scroll(delta, cursor, move_phase) => {
//                            hprof::start_frame();
                            let frame = profile_counters.total_time.profile(|| {
                                if self.frame.scroll(delta, cursor, move_phase) {
                                    self.build_scene();
                                    Some(self.render())
                                } else {
                                    None
                                }
                            });
//                            hprof::end_frame();
                            //hprof::profiler().print_timing();

                            match frame {
                                Some(frame) => {
                                    self.publish_frame(frame, &mut profile_counters);
                                    self.notify_compositor_of_new_scroll_frame(true)
                                }
                                None => self.notify_compositor_of_new_scroll_frame(false),
                            }
                        }
                        ApiMsg::TickScrollingBounce => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.frame.tick_scrolling_bounce_animations();
                                self.render()
                            });

                            self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                        }
                        ApiMsg::TranslatePointToLayerSpace(point, tx) => {
                            // First, find the specific layer that contains the point.
                            let point = point / self.device_pixel_ratio;
                            if let (Some(..), Some(root_scroll_layer_id)) =
                                    (self.scene.root_pipeline_id,
                                     self.frame.root_scroll_layer_id) {
                                if let Some(scroll_layer_id) =
                                        self.frame.get_scroll_layer(&point, root_scroll_layer_id) {
                                    if let Some(layer) = self.frame.layers.get(&scroll_layer_id) {
                                        // Now, because we send a *pipeline ID*, not a layer ID, as
                                        // a response, we need the translated point to be relative
                                        // to the origin of that pipeline in the scene. So we need
                                        // to find the root layer for the pipeline. (We don't send
                                        // layer IDs because layer IDs are internal to WebRender;
                                        // Servo won't know what to do with them.)
                                        if let Some(layer_id) =
                                                self.frame.root_scroll_layer_for_pipeline(
                                                    layer.pipeline_id) {
                                            if let Some(layer) = self.frame.layers.get(&layer_id) {
                                                let point = point - layer.world_origin -
                                                    layer.scrolling.offset;
                                                tx.send((point, layer.pipeline_id)).unwrap();
                                                continue
                                            }
                                        }
                                    }
                                }
                            }
                            tx.send((point, PipelineId(0, 0))).unwrap()
                        }
                        ApiMsg::GetScrollLayerState(tx) => {
                            tx.send(self.frame.get_scroll_layer_state())
                              .unwrap()
                        }
                        ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                            if let Some(ref handle) = self.webrender_context_handle {
                                match GLContext::<NativeGLContext>::new(size, attributes, ColorAttachmentType::Texture, Some(handle)) {
                                    Ok(ctx) => {
                                        let id = WebGLContextId(new_id());

                                        let (real_size, texture_id) = {
                                            let draw_buffer = ctx.borrow_draw_buffer().unwrap();
                                            (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap())
                                        };

                                        let limits = ctx.borrow_limits().clone();

                                        self.webgl_contexts.insert(id, ctx);

                                        self.resource_cache
                                            .add_webgl_texture(id, TextureId(texture_id), real_size);

                                        tx.send(Ok((id, limits))).unwrap();
                                    },
                                    Err(msg) => {
                                        tx.send(Err(msg.to_owned())).unwrap();
                                    }
                                }
                            } else {
                                tx.send(Err("Not implemented yet".to_owned())).unwrap();
                            }
                        }
                        ApiMsg::WebGLCommand(context_id, command) => {
                            // TODO: Buffer the commands and only apply them here if they need to
                            // be synchronous.
                            let ctx = self.webgl_contexts.get(&context_id).unwrap();
                            ctx.make_current().unwrap();
                            command.apply(ctx);
                            self.current_bound_webgl_context_id = Some(context_id);
                        }
                    }
                }
                Err(..) => {
                    break;
                }
            }
        }
    }

    fn build_scene(&mut self) {
        // Flatten the stacking context hierarchy
        let mut new_pipeline_sizes = HashMap::new();

        if let Some(id) = self.current_bound_webgl_context_id {
            self.webgl_contexts.get(&id).unwrap().unbind().unwrap();
        }

        self.frame.create(&self.scene,
                          &mut self.resource_cache,
                          &mut new_pipeline_sizes,
                          self.device_pixel_ratio);

        let mut updated_pipeline_sizes = HashMap::new();

        for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
            let new_size = new_pipeline_sizes.remove(&pipeline_id);

            match new_size {
                Some(new_size) => {
                    // Exists in both old and new -> check if size changed
                    if new_size != old_size {
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, Some(new_size));
                    }

                    // Re-insert
                    updated_pipeline_sizes.insert(pipeline_id, new_size);
                }
                None => {
                    // Was existing, not in current frame anymore
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, None);
                }
            }
        }

        // Any remaining items are new pipelines
        for (pipeline_id, new_size) in new_pipeline_sizes.drain() {
            let mut notifier = self.notifier.lock();
            notifier.as_mut()
                    .unwrap()
                    .as_mut()
                    .unwrap()
                    .pipeline_size_changed(pipeline_id, Some(new_size));
            updated_pipeline_sizes.insert(pipeline_id, new_size);
        }

        self.scene.pipeline_sizes = updated_pipeline_sizes;
    }

    fn render(&mut self) -> RendererFrame {
        let frame = self.frame.build(&mut self.resource_cache,
                                     &mut self.thread_pool,
                                     self.device_pixel_ratio);

        let pending_update = self.resource_cache.pending_updates();
        if !pending_update.updates.is_empty() {
            self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap();
        }

        frame
    }

    fn publish_frame(&mut self,
                     frame: RendererFrame,
                     profile_counters: &mut BackendProfileCounters) {
        let msg = ResultMsg::NewFrame(frame, profile_counters.clone());
        self.result_tx.send(msg).unwrap();
        profile_counters.reset();
    }

    fn publish_frame_and_notify_compositor(&mut self,
                                           frame: RendererFrame,
                                           profile_counters: &mut BackendProfileCounters) {
        self.publish_frame(frame, profile_counters);

        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready();
    }

    fn notify_compositor_of_new_scroll_frame(&mut self, composite_needed: bool) {
        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_scroll_frame_ready(composite_needed);
    }
}

<commit_msg>Fix overscroll in WR2.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use byteorder::{LittleEndian, ReadBytesExt};
use frame::Frame;
use internal_types::{FontTemplate, ResultMsg, RendererFrame};
use ipc_channel::ipc::{IpcBytesReceiver, IpcBytesSender, IpcReceiver};
use profiler::BackendProfileCounters;
use resource_cache::ResourceCache;
use scene::Scene;
use scoped_threadpool;
use std::collections::HashMap;
use std::io::{Cursor, Read};
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;
use texture_cache::{TextureCache, TextureCacheItemId};
use webrender_traits::{ApiMsg, AuxiliaryLists, BuiltDisplayList, IdNamespace};
use webrender_traits::{PipelineId, RenderNotifier, WebGLContextId};
use batch::new_id;
use device::TextureId;
use tiling::FrameBuilderConfig;
use offscreen_gl_context::{ColorAttachmentType, GLContext};
use offscreen_gl_context::{NativeGLContext, NativeGLContextHandle};

pub struct RenderBackend {
    api_rx: IpcReceiver<ApiMsg>,
    payload_rx: IpcBytesReceiver,
    payload_tx: IpcBytesSender,
    result_tx: Sender<ResultMsg>,

    device_pixel_ratio: f32,
    next_namespace_id: IdNamespace,

    thread_pool: scoped_threadpool::Pool,
    resource_cache: ResourceCache,

    scene: Scene,
    frame: Frame,

    notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
    webrender_context_handle: Option<NativeGLContextHandle>,
    webgl_contexts: HashMap<WebGLContextId, GLContext<NativeGLContext>>,
    current_bound_webgl_context_id: Option<WebGLContextId>,
}

impl RenderBackend {
    pub fn new(api_rx: IpcReceiver<ApiMsg>,
               payload_rx: IpcBytesReceiver,
               payload_tx: IpcBytesSender,
               result_tx: Sender<ResultMsg>,
               device_pixel_ratio: f32,
               white_image_id: TextureCacheItemId,
               texture_cache: TextureCache,
               enable_aa: bool,
               notifier: Arc<Mutex<Option<Box<RenderNotifier>>>>,
               webrender_context_handle: Option<NativeGLContextHandle>,
               config: FrameBuilderConfig,
               debug: bool) -> RenderBackend {
        let mut thread_pool = scoped_threadpool::Pool::new(8);

        let resource_cache = ResourceCache::new(&mut thread_pool,
                                                texture_cache,
                                                white_image_id,
                                                device_pixel_ratio,
                                                enable_aa);

        RenderBackend {
            thread_pool: thread_pool,
            api_rx: api_rx,
            payload_rx: payload_rx,
            payload_tx: payload_tx,
            result_tx: result_tx,
            device_pixel_ratio: device_pixel_ratio,
            resource_cache: resource_cache,
            scene: Scene::new(),
            frame: Frame::new(debug, config),
            next_namespace_id: IdNamespace(1),
            notifier: notifier,
            webrender_context_handle: webrender_context_handle,
            webgl_contexts: HashMap::new(),
            current_bound_webgl_context_id: None,
        }
    }

    pub fn run(&mut self) {
        let mut profile_counters = BackendProfileCounters::new();

        loop {
            let msg = self.api_rx.recv();
            match msg {
                Ok(msg) => {
                    match msg {
                        ApiMsg::AddRawFont(id, bytes) => {
                            profile_counters.font_templates.inc(bytes.len());
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Raw(Arc::new(bytes)));
                        }
                        ApiMsg::AddNativeFont(id, native_font_handle) => {
                            self.resource_cache
                                .add_font_template(id, FontTemplate::Native(native_font_handle));
                        }
                        ApiMsg::AddImage(id, width, height, format, bytes) => {
                            profile_counters.image_templates.inc(bytes.len());
                            self.resource_cache.add_image_template(id,
                                                                   width,
                                                                   height,
                                                                   format,
                                                                   bytes);
                        }
                        ApiMsg::UpdateImage(id, width, height, format, bytes) => {
                            self.resource_cache.update_image_template(id,
                                                                      width,
                                                                      height,
                                                                      format,
                                                                      bytes);
                        }
                        ApiMsg::DeleteImage(id) => {
                            self.resource_cache.delete_image_template(id);
                        }
                        ApiMsg::CloneApi(sender) => {
                            let result = self.next_namespace_id;

                            let IdNamespace(id_namespace) = self.next_namespace_id;
                            self.next_namespace_id = IdNamespace(id_namespace + 1);

                            sender.send(result).unwrap();
                        }
                        ApiMsg::SetRootStackingContext(stacking_context_id,
                                                       background_color,
                                                       epoch,
                                                       pipeline_id,
                                                       viewport_size,
                                                       stacking_contexts,
                                                       display_lists,
                                                       auxiliary_lists_descriptor) => {
                            for (id, stacking_context) in stacking_contexts.into_iter() {
                                self.scene.add_stacking_context(id,
                                                                pipeline_id,
                                                                epoch,
                                                                stacking_context);
                            }

                            let mut leftover_auxiliary_data = vec![];
                            let mut auxiliary_data;
                            loop {
                                auxiliary_data = self.payload_rx.recv().unwrap();
                                {
                                    let mut payload_reader = Cursor::new(&auxiliary_data[..]);
                                    let payload_stacking_context_id =
                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                    let payload_epoch =
                                        payload_reader.read_u32::<LittleEndian>().unwrap();
                                    if payload_epoch == epoch.0 &&
                                            payload_stacking_context_id == stacking_context_id.0 {
                                        break
                                    }
                                }
                                leftover_auxiliary_data.push(auxiliary_data)
                            }
                            for leftover_auxiliary_data in leftover_auxiliary_data {
                                self.payload_tx.send(&leftover_auxiliary_data[..]).unwrap()
                            }

                            let mut auxiliary_data = Cursor::new(&mut auxiliary_data[8..]);
                            for (display_list_id,
                                 display_list_descriptor) in display_lists.into_iter() {
                                let mut built_display_list_data =
                                    vec![0; display_list_descriptor.size()];
                                auxiliary_data.read_exact(&mut built_display_list_data[..])
                                              .unwrap();
                                let built_display_list =
                                    BuiltDisplayList::from_data(built_display_list_data,
                                                                display_list_descriptor);
                                self.scene.add_display_list(display_list_id,
                                                            pipeline_id,
                                                            epoch,
                                                            built_display_list,
                                                            &mut self.resource_cache);
                            }

                            let mut auxiliary_lists_data =
                                vec![0; auxiliary_lists_descriptor.size()];
                            auxiliary_data.read_exact(&mut auxiliary_lists_data[..]).unwrap();
                            let auxiliary_lists =
                                AuxiliaryLists::from_data(auxiliary_lists_data,
                                                          auxiliary_lists_descriptor);
//                            hprof::start_frame();
                            let frame = profile_counters.total_time.profile(|| {
//                                let _pf = hprof::enter("root1");
                                self.scene.set_root_stacking_context(pipeline_id,
                                                                     epoch,
                                                                     stacking_context_id,
                                                                     background_color,
                                                                     viewport_size,
                                                                     &mut self.resource_cache,
                                                                     auxiliary_lists);

                                self.build_scene();
                                self.render()
                            });
//                            hprof::end_frame();
                            //hprof::profiler().print_timing();

                            self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                        }
                        ApiMsg::SetRootPipeline(pipeline_id) => {
//                            hprof::start_frame();
                            let frame = profile_counters.total_time.profile(|| {
//                                let _pf = hprof::enter("root2");
                                self.scene.set_root_pipeline_id(pipeline_id);

                                self.build_scene();
                                self.render()
                            });
//                            hprof::end_frame();
                            //hprof::profiler().print_timing();

                            self.publish_frame(frame, &mut profile_counters);
                        }
                        ApiMsg::Scroll(delta, cursor, move_phase) => {
//                            hprof::start_frame();
                            let frame = profile_counters.total_time.profile(|| {
                                if self.frame.scroll(delta, cursor, move_phase) {
                                    self.build_scene();
                                    Some(self.render())
                                } else {
                                    None
                                }
                            });
//                            hprof::end_frame();
                            //hprof::profiler().print_timing();

                            match frame {
                                Some(frame) => {
                                    self.publish_frame(frame, &mut profile_counters);
                                    self.notify_compositor_of_new_scroll_frame(true)
                                }
                                None => self.notify_compositor_of_new_scroll_frame(false),
                            }
                        }
                        ApiMsg::TickScrollingBounce => {
                            let frame = profile_counters.total_time.profile(|| {
                                self.frame.tick_scrolling_bounce_animations();
                                self.build_scene();
                                self.render()
                            });

                            self.publish_frame_and_notify_compositor(frame, &mut profile_counters);
                        }
                        ApiMsg::TranslatePointToLayerSpace(point, tx) => {
                            // First, find the specific layer that contains the point.
                            let point = point / self.device_pixel_ratio;
                            if let (Some(..), Some(root_scroll_layer_id)) =
                                    (self.scene.root_pipeline_id,
                                     self.frame.root_scroll_layer_id) {
                                if let Some(scroll_layer_id) =
                                        self.frame.get_scroll_layer(&point, root_scroll_layer_id) {
                                    if let Some(layer) = self.frame.layers.get(&scroll_layer_id) {
                                        // Now, because we send a *pipeline ID*, not a layer ID, as
                                        // a response, we need the translated point to be relative
                                        // to the origin of that pipeline in the scene. So we need
                                        // to find the root layer for the pipeline. (We don't send
                                        // layer IDs because layer IDs are internal to WebRender;
                                        // Servo won't know what to do with them.)
                                        if let Some(layer_id) =
                                                self.frame.root_scroll_layer_for_pipeline(
                                                    layer.pipeline_id) {
                                            if let Some(layer) = self.frame.layers.get(&layer_id) {
                                                let point = point - layer.world_origin -
                                                    layer.scrolling.offset;
                                                tx.send((point, layer.pipeline_id)).unwrap();
                                                continue
                                            }
                                        }
                                    }
                                }
                            }
                            tx.send((point, PipelineId(0, 0))).unwrap()
                        }
                        ApiMsg::GetScrollLayerState(tx) => {
                            tx.send(self.frame.get_scroll_layer_state())
                              .unwrap()
                        }
                        ApiMsg::RequestWebGLContext(size, attributes, tx) => {
                            if let Some(ref handle) = self.webrender_context_handle {
                                match GLContext::<NativeGLContext>::new(size, attributes, ColorAttachmentType::Texture, Some(handle)) {
                                    Ok(ctx) => {
                                        let id = WebGLContextId(new_id());

                                        let (real_size, texture_id) = {
                                            let draw_buffer = ctx.borrow_draw_buffer().unwrap();
                                            (draw_buffer.size(), draw_buffer.get_bound_texture_id().unwrap())
                                        };

                                        let limits = ctx.borrow_limits().clone();

                                        self.webgl_contexts.insert(id, ctx);

                                        self.resource_cache
                                            .add_webgl_texture(id, TextureId(texture_id), real_size);

                                        tx.send(Ok((id, limits))).unwrap();
                                    },
                                    Err(msg) => {
                                        tx.send(Err(msg.to_owned())).unwrap();
                                    }
                                }
                            } else {
                                tx.send(Err("Not implemented yet".to_owned())).unwrap();
                            }
                        }
                        ApiMsg::WebGLCommand(context_id, command) => {
                            // TODO: Buffer the commands and only apply them here if they need to
                            // be synchronous.
                            let ctx = self.webgl_contexts.get(&context_id).unwrap();
                            ctx.make_current().unwrap();
                            command.apply(ctx);
                            self.current_bound_webgl_context_id = Some(context_id);
                        }
                    }
                }
                Err(..) => {
                    break;
                }
            }
        }
    }

    fn build_scene(&mut self) {
        // Flatten the stacking context hierarchy
        let mut new_pipeline_sizes = HashMap::new();

        if let Some(id) = self.current_bound_webgl_context_id {
            self.webgl_contexts.get(&id).unwrap().unbind().unwrap();
        }

        self.frame.create(&self.scene,
                          &mut self.resource_cache,
                          &mut new_pipeline_sizes,
                          self.device_pixel_ratio);

        let mut updated_pipeline_sizes = HashMap::new();

        for (pipeline_id, old_size) in self.scene.pipeline_sizes.drain() {
            let new_size = new_pipeline_sizes.remove(&pipeline_id);

            match new_size {
                Some(new_size) => {
                    // Exists in both old and new -> check if size changed
                    if new_size != old_size {
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, Some(new_size));
                    }

                    // Re-insert
                    updated_pipeline_sizes.insert(pipeline_id, new_size);
                }
                None => {
                    // Was existing, not in current frame anymore
                        let mut notifier = self.notifier.lock();
                        notifier.as_mut()
                                .unwrap()
                                .as_mut()
                                .unwrap()
                                .pipeline_size_changed(pipeline_id, None);
                }
            }
        }

        // Any remaining items are new pipelines
        for (pipeline_id, new_size) in new_pipeline_sizes.drain() {
            let mut notifier = self.notifier.lock();
            notifier.as_mut()
                    .unwrap()
                    .as_mut()
                    .unwrap()
                    .pipeline_size_changed(pipeline_id, Some(new_size));
            updated_pipeline_sizes.insert(pipeline_id, new_size);
        }

        self.scene.pipeline_sizes = updated_pipeline_sizes;
    }

    fn render(&mut self) -> RendererFrame {
        let frame = self.frame.build(&mut self.resource_cache,
                                     &mut self.thread_pool,
                                     self.device_pixel_ratio);

        let pending_update = self.resource_cache.pending_updates();
        if !pending_update.updates.is_empty() {
            self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap();
        }

        frame
    }

    fn publish_frame(&mut self,
                     frame: RendererFrame,
                     profile_counters: &mut BackendProfileCounters) {
        let msg = ResultMsg::NewFrame(frame, profile_counters.clone());
        self.result_tx.send(msg).unwrap();
        profile_counters.reset();
    }

    fn publish_frame_and_notify_compositor(&mut self,
                                           frame: RendererFrame,
                                           profile_counters: &mut BackendProfileCounters) {
        self.publish_frame(frame, profile_counters);

        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_frame_ready();
    }

    fn notify_compositor_of_new_scroll_frame(&mut self, composite_needed: bool) {
        // TODO(gw): This is kindof bogus to have to lock the notifier
        //           each time it's used. This is due to some nastiness
        //           in initialization order for Servo. Perhaps find a
        //           cleaner way to do this, or use the OnceMutex on crates.io?
        let mut notifier = self.notifier.lock();
        notifier.as_mut().unwrap().as_mut().unwrap().new_scroll_frame_ready(composite_needed);
    }
}

<|endoftext|>


<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::HashMap;

use crate::term::{ SizeInfo, RenderableCell};
use crate::term::cell::Flags;
use crate::renderer::Rect;
use font::Metrics;
use crate::Rgb;

/// Lines for underline and strikeout.
pub struct Lines<'a> {
    inner: Vec<(Rect<f32>, Rgb)>,
    last_starts: HashMap<Flags, Option<RenderableCell>>,
    last_cell: Option<RenderableCell>,
    metrics: &'a Metrics,
    size: &'a SizeInfo,
}

impl<'a> Lines<'a> {
    pub fn new(metrics: &'a Metrics, size: &'a SizeInfo) -> Self {
        let mut last_starts = HashMap::new();
        last_starts.insert(Flags::UNDERLINE, None);
        last_starts.insert(Flags::STRIKEOUT, None);

        Self {
            inner: Vec::new(),
            last_cell: None,
            last_starts,
            metrics,
            size,
        }
    }

    /// Convert the stored lines to rectangles for the renderer.
    pub fn rects(mut self) -> Vec<(Rect<f32>, Rgb)> {
        // If there's still a line pending, draw it until the last cell
        for (flag, start_cell) in self.last_starts.iter_mut() {
            if let Some(start) = start_cell {
                self.inner.push(
                    create_rect(
                        &start,
                        &self.last_cell.unwrap(),
                        *flag,
                        &self.metrics,
                        &self.size,
                    )
                );
            }
        }

        self.inner
    }

    /// Update the stored lines with the next cell info.
    pub fn update_lines(&mut self, cell: &RenderableCell) {
        for (flag, start_cell) in self.last_starts.iter_mut() {
            let flag = *flag;
            *start_cell = match *start_cell {
                // Check for end if line is present
                Some(ref mut start) => {
                    // No change in line
                    if cell.line == start.line && cell.flags.contains(flag) && cell.fg == start.fg {
                        continue;
                    }

                    self.inner.push(
                        create_rect(
                            &start,
                            &self.last_cell.unwrap(),
                            flag,
                            &self.metrics,
                            &self.size,
                        )
                    );

                    // Start a new line if the flag is present
                    if cell.flags.contains(flag) {
                        Some(*cell)
                    } else {
                        None
                    }
                }
                // Check for new start of line
                None => if cell.flags.contains(flag) {
                    Some(*cell)
                } else {
                    None
                },
            };
        }

        self.last_cell = Some(*cell);
    }
}

/// Create a rectangle between two cells.
fn create_rect(
    start: &RenderableCell,
    end: &RenderableCell,
    flag: Flags,
    metrics: &Metrics,
    size: &SizeInfo,
) -> (Rect<f32>, Rgb) {
    let start_x = start.column.0 as f32 * size.cell_width;
    let end_x = (end.column.0 + 1) as f32 * size.cell_width;
    let width = end_x - start_x;

    let (position, height) = match flag {
        Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness),
        Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness),
        _ => unimplemented!("Invalid flag for cell line drawing specified"),
    };

    let cell_bottom = (start.line.0 as f32 + 1.) * size.cell_height;
    let baseline = cell_bottom + metrics.descent;

    let mut y = baseline - position - height / 2.;
    let max_y = cell_bottom - height;
    if y > max_y {
        y = max_y;
    }

    let rect = Rect::new(
        start_x + size.padding_x,
        y + size.padding_y,
        width,
        height,
    );

    (rect, start.fg)
}
<commit_msg>Fix `create_rect` function documentation<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::collections::HashMap;

use crate::term::{ SizeInfo, RenderableCell};
use crate::term::cell::Flags;
use crate::renderer::Rect;
use font::Metrics;
use crate::Rgb;

/// Lines for underline and strikeout.
pub struct Lines<'a> {
    inner: Vec<(Rect<f32>, Rgb)>,
    last_starts: HashMap<Flags, Option<RenderableCell>>,
    last_cell: Option<RenderableCell>,
    metrics: &'a Metrics,
    size: &'a SizeInfo,
}

impl<'a> Lines<'a> {
    pub fn new(metrics: &'a Metrics, size: &'a SizeInfo) -> Self {
        let mut last_starts = HashMap::new();
        last_starts.insert(Flags::UNDERLINE, None);
        last_starts.insert(Flags::STRIKEOUT, None);

        Self {
            inner: Vec::new(),
            last_cell: None,
            last_starts,
            metrics,
            size,
        }
    }

    /// Convert the stored lines to rectangles for the renderer.
    pub fn rects(mut self) -> Vec<(Rect<f32>, Rgb)> {
        // If there's still a line pending, draw it until the last cell
        for (flag, start_cell) in self.last_starts.iter_mut() {
            if let Some(start) = start_cell {
                self.inner.push(
                    create_rect(
                        &start,
                        &self.last_cell.unwrap(),
                        *flag,
                        &self.metrics,
                        &self.size,
                    )
                );
            }
        }

        self.inner
    }

    /// Update the stored lines with the next cell info.
    pub fn update_lines(&mut self, cell: &RenderableCell) {
        for (flag, start_cell) in self.last_starts.iter_mut() {
            let flag = *flag;
            *start_cell = match *start_cell {
                // Check for end if line is present
                Some(ref mut start) => {
                    // No change in line
                    if cell.line == start.line && cell.flags.contains(flag) && cell.fg == start.fg {
                        continue;
                    }

                    self.inner.push(
                        create_rect(
                            &start,
                            &self.last_cell.unwrap(),
                            flag,
                            &self.metrics,
                            &self.size,
                        )
                    );

                    // Start a new line if the flag is present
                    if cell.flags.contains(flag) {
                        Some(*cell)
                    } else {
                        None
                    }
                }
                // Check for new start of line
                None => if cell.flags.contains(flag) {
                    Some(*cell)
                } else {
                    None
                },
            };
        }

        self.last_cell = Some(*cell);
    }
}

/// Create a rectangle that starts on the left of `start` and ends on the right
/// of `end`, based on the given flag and size metrics.
fn create_rect(
    start: &RenderableCell,
    end: &RenderableCell,
    flag: Flags,
    metrics: &Metrics,
    size: &SizeInfo,
) -> (Rect<f32>, Rgb) {
    let start_x = start.column.0 as f32 * size.cell_width;
    let end_x = (end.column.0 + 1) as f32 * size.cell_width;
    let width = end_x - start_x;

    let (position, height) = match flag {
        Flags::UNDERLINE => (metrics.underline_position, metrics.underline_thickness),
        Flags::STRIKEOUT => (metrics.strikeout_position, metrics.strikeout_thickness),
        _ => unimplemented!("Invalid flag for cell line drawing specified"),
    };

    let cell_bottom = (start.line.0 as f32 + 1.) * size.cell_height;
    let baseline = cell_bottom + metrics.descent;

    let mut y = baseline - position - height / 2.;
    let max_y = cell_bottom - height;
    if y > max_y {
        y = max_y;
    }

    let rect = Rect::new(
        start_x + size.padding_x,
        y + size.padding_y,
        width,
        height,
    );

    (rect, start.fg)
}
<|endoftext|>


<commit_before>extern crate sdl2;
extern crate sdl2_ttf;

use std::env;
use std::path::Path;

use sdl2::*;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::rect::Rect;
use sdl2::render::TextureQuery;
use sdl2::pixels::Color;

static SCREEN_WIDTH : u32 = 800;
static SCREEN_HEIGHT : u32 = 600;

// handle the annoying Rect i32
macro_rules! rect(
    ($x:expr, $y:expr, $w:expr, $h:expr) => (
        Rect::new_unwrap($x as i32, $y as i32, $w as u32, $h as u32)
    )
);

// Scale fonts to a reasonable size when they're too big (though they might look less smooth)
fn get_centered_rect(rect_width: u32, rect_height: u32, cons_width: u32, cons_height: u32) -> Rect {
    let wr = rect_width as f32 / cons_width as f32;
    let hr = rect_height as f32 / cons_height as f32;

    let (w, h) = if wr > 1f32 || hr > 1f32 {
        if wr > hr {
            println!("Scaling down! The text will look worse!");
            let h = (rect_height as f32 / wr) as i32;
            (cons_width as i32, h)
        } else {
            println!("Scaling down! The text will look worse!");
            let w = (rect_width as f32 / hr) as i32;
            (w, cons_height as i32)
        }
    } else {
        (rect_width as i32, rect_height as i32)
    };

    let cx = (SCREEN_WIDTH as i32 - w) / 2;
    let cy = (SCREEN_HEIGHT as i32 - h) / 2;
    rect!(cx, cy, w, h)
}

fn run(filename: &Path) {
    let sdl_context = sdl2::init().unwrap();
    let video_subsys = sdl_context.video().unwrap();
    let _ttf_context = sdl2_ttf::init();

    let window = video_subsys.window("SDL2_TTF Example", SCREEN_WIDTH, SCREEN_HEIGHT)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    // Load a font
    let font = sdl2_ttf::Font::from_file(filename, 128).unwrap();

    // render a surface, and convert it to a texture bound to the renderer
    let surface = font.render("Hello Rust!",
        sdl2_ttf::blended(Color::RGBA(255, 0, 0, 255))).unwrap();
    let mut texture = renderer.create_texture_from_surface(&surface).unwrap();

    renderer.set_draw_color(Color::RGBA(195, 217, 255, 255));
    renderer.clear();

    let TextureQuery { width, height, .. } = texture.query();

    // If the example text is too big for the screen, downscale it (and center irregardless)
    let padding = 64;
    let target = get_centered_rect(width, height, SCREEN_WIDTH - padding, SCREEN_HEIGHT - padding);

    renderer.copy(&mut texture, None, Some(target));
    renderer.present();

    'mainloop: loop {
        for event in sdl_context.event_pump().unwrap().poll_iter() {
            match event {
                Event::Quit{..} => break 'mainloop,
                Event::KeyDown {keycode: Some(Keycode::Escape), ..} => break 'mainloop,
                _ => {}
            }
        }
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();

    println!("linked sdl2_ttf: {}", sdl2_ttf::get_linked_version());

    if args.len() < 2 {
        println!("Usage: ./demo font.[ttf|ttc|fon]")
    } else {
        let path: &Path = Path::new(&args[1]);
        run(path);
    }
}
<commit_msg>fix demo compile warning<commit_after>extern crate sdl2;
extern crate sdl2_ttf;

use std::env;
use std::path::Path;

use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::rect::Rect;
use sdl2::render::TextureQuery;
use sdl2::pixels::Color;

static SCREEN_WIDTH : u32 = 800;
static SCREEN_HEIGHT : u32 = 600;

// handle the annoying Rect i32
macro_rules! rect(
    ($x:expr, $y:expr, $w:expr, $h:expr) => (
        Rect::new_unwrap($x as i32, $y as i32, $w as u32, $h as u32)
    )
);

// Scale fonts to a reasonable size when they're too big (though they might look less smooth)
fn get_centered_rect(rect_width: u32, rect_height: u32, cons_width: u32, cons_height: u32) -> Rect {
    let wr = rect_width as f32 / cons_width as f32;
    let hr = rect_height as f32 / cons_height as f32;

    let (w, h) = if wr > 1f32 || hr > 1f32 {
        if wr > hr {
            println!("Scaling down! The text will look worse!");
            let h = (rect_height as f32 / wr) as i32;
            (cons_width as i32, h)
        } else {
            println!("Scaling down! The text will look worse!");
            let w = (rect_width as f32 / hr) as i32;
            (w, cons_height as i32)
        }
    } else {
        (rect_width as i32, rect_height as i32)
    };

    let cx = (SCREEN_WIDTH as i32 - w) / 2;
    let cy = (SCREEN_HEIGHT as i32 - h) / 2;
    rect!(cx, cy, w, h)
}

fn run(filename: &Path) {
    let sdl_context = sdl2::init().unwrap();
    let video_subsys = sdl_context.video().unwrap();
    let _ttf_context = sdl2_ttf::init();

    let window = video_subsys.window("SDL2_TTF Example", SCREEN_WIDTH, SCREEN_HEIGHT)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    // Load a font
    let font = sdl2_ttf::Font::from_file(filename, 128).unwrap();

    // render a surface, and convert it to a texture bound to the renderer
    let surface = font.render("Hello Rust!",
        sdl2_ttf::blended(Color::RGBA(255, 0, 0, 255))).unwrap();
    let mut texture = renderer.create_texture_from_surface(&surface).unwrap();

    renderer.set_draw_color(Color::RGBA(195, 217, 255, 255));
    renderer.clear();

    let TextureQuery { width, height, .. } = texture.query();

    // If the example text is too big for the screen, downscale it (and center irregardless)
    let padding = 64;
    let target = get_centered_rect(width, height, SCREEN_WIDTH - padding, SCREEN_HEIGHT - padding);

    renderer.copy(&mut texture, None, Some(target));
    renderer.present();

    'mainloop: loop {
        for event in sdl_context.event_pump().unwrap().poll_iter() {
            match event {
                Event::Quit{..} => break 'mainloop,
                Event::KeyDown {keycode: Some(Keycode::Escape), ..} => break 'mainloop,
                _ => {}
            }
        }
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();

    println!("linked sdl2_ttf: {}", sdl2_ttf::get_linked_version());

    if args.len() < 2 {
        println!("Usage: ./demo font.[ttf|ttc|fon]")
    } else {
        let path: &Path = Path::new(&args[1]);
        run(path);
    }
}
<|endoftext|>


<commit_before>#![feature(globs)]
#![allow(unreachable_code, unused_assignments)]

extern crate portaudio;

use portaudio::*;
use std::error::Error;

const SAMPLE_RATE: f64 = 44_100.0;
const FRAMES: u32 = 256;

fn main() -> () {

    println!("PortAudio version : {}", pa::get_version());
    println!("PortAudio version text : {}", pa::get_version_text());

    match pa::initialize() {
        Ok(()) => println!("Successfully initialized PortAudio"),
        Err(err) => println!("An error occurred while initializing PortAudio: {}", err.description()),
    }

    println!("PortAudio host count : {}", pa::host::get_api_count() as int);

    let default_host = pa::host::get_default_api();
    println!("PortAudio default host : {}", default_host as int);

    match pa::host::get_api_info(default_host) {
        None => println!("Couldn't retrieve api info for the default host."),
        Some(info) => println!("PortAudio host name : {}", info.name),
    }

    let type_id = pa::host::api_type_id_to_host_api_index(pa::HostApiTypeId::CoreAudio) as int;
    println!("PortAudio type id : {}", type_id)

    let def_input = pa::device::get_default_input();
    let input_info = match pa::device::get_info(def_input) {
        Ok(info) => info,
        Err(err) => panic!("An error occurred while retrieving input info: {}", err.description()),
    };
    println!("Default input device info :");
    println!("\tversion : {}", input_info.struct_version);
    println!("\tname : {}", input_info.name);
    println!("\tmax input channels : {}", input_info.max_input_channels);
    println!("\tmax output channels : {}", input_info.max_output_channels);
    println!("\tdefault sample rate : {}", input_info.default_sample_rate);

    // Construct the input stream parameters.
    let input_stream_params = pa::StreamParameters {
        device : def_input,
        channel_count : 2,
        sample_format : pa::SampleFormat::Float32,
        suggested_latency : input_info.default_low_input_latency
    };

    let def_output = pa::device::get_default_output();
    let output_info = match pa::device::get_info(def_output) {
        Ok(info) => info,
        Err(err) => panic!("An error occurred while retrieving output info: {}", err.description()),
    };

    println!("Default output device name : {}", output_info.name);

    // Construct the output stream parameters.
    let output_stream_params = pa::StreamParameters {
        device : def_output,
        channel_count : 2,
        sample_format : pa::SampleFormat::Float32,
        suggested_latency : output_info.default_low_output_latency
    };

    let mut stream : pa::Stream<f32, f32> = pa::Stream::new();

    match stream.open(Some(&input_stream_params),
                      Some(&output_stream_params),
                      SAMPLE_RATE,
                      FRAMES,
                      pa::StreamFlags::ClipOff) {
        Ok(()) => println!("Successfully opened the stream."),
        Err(err) => println!("An error occurred while opening the stream: {}", err.description()),
    }

    match stream.start() {
        Ok(()) => println!("Successfully started the stream."),
        Err(err) => println!("An error occurred while starting the stream: {}", err.description()),
    }

    // We'll use this function to wait for read/write availability.
    let wait_for_stream = |f: || -> Result<Option<i64>, pa::error::Error>, name: &str| {
        'waiting_for_stream: loop {
            match f() {
                Ok(None) => (),
                Ok(Some(frames)) => {
                    println!("{} stream available with {} frames.", name, frames);
                    break 'waiting_for_stream
                },
                Err(err) => panic!("An error occurred while waiting for the {} stream: {}", name, err.description()),
            }
        }
    };

    // Now start the main read/write loop! In this example, we pass the input buffer directly to
    // the output buffer, so watch out for feedback.
    'stream: loop {
        wait_for_stream(|| stream.get_stream_read_available(), "Read");
        match stream.read(FRAMES) {
            Ok(input_samples)  => {
                wait_for_stream(|| stream.get_stream_write_available(), "Write");
                match stream.write(input_samples, FRAMES) {
                    Ok(()) => (),
                    Err(err) => {
                        println!("An error occurred while writing to the output stream: {}", err.description());
                        break 'stream
                    },
                }
            },
            Err(err) => {
                println!("An error occurred while reading from the input stream: {}", err.description());
                break 'stream
            }
        };
    }

    match stream.close() {
        Ok(()) => println!("Successfully closed the stream."),
        Err(err) => println!("An error occurred while closing the stream: {}", err.description()),
    }

    println!("");

    match pa::terminate() {
        Ok(()) => println!("Successfully terminated PortAudio."),
        Err(err) => println!("An error occurred while terminating PortAudio: {}", err.description()),
    }

}
<commit_msg>Adds semicolon after macro<commit_after>#![feature(globs)]
#![allow(unreachable_code, unused_assignments)]

extern crate portaudio;

use portaudio::*;
use std::error::Error;

const SAMPLE_RATE: f64 = 44_100.0;
const FRAMES: u32 = 256;

fn main() -> () {

    println!("PortAudio version : {}", pa::get_version());
    println!("PortAudio version text : {}", pa::get_version_text());

    match pa::initialize() {
        Ok(()) => println!("Successfully initialized PortAudio"),
        Err(err) => println!("An error occurred while initializing PortAudio: {}", err.description()),
    }

    println!("PortAudio host count : {}", pa::host::get_api_count() as int);

    let default_host = pa::host::get_default_api();
    println!("PortAudio default host : {}", default_host as int);

    match pa::host::get_api_info(default_host) {
        None => println!("Couldn't retrieve api info for the default host."),
        Some(info) => println!("PortAudio host name : {}", info.name),
    }

    let type_id = pa::host::api_type_id_to_host_api_index(pa::HostApiTypeId::CoreAudio) as int;
    println!("PortAudio type id : {}", type_id);

    let def_input = pa::device::get_default_input();
    let input_info = match pa::device::get_info(def_input) {
        Ok(info) => info,
        Err(err) => panic!("An error occurred while retrieving input info: {}", err.description()),
    };
    println!("Default input device info :");
    println!("\tversion : {}", input_info.struct_version);
    println!("\tname : {}", input_info.name);
    println!("\tmax input channels : {}", input_info.max_input_channels);
    println!("\tmax output channels : {}", input_info.max_output_channels);
    println!("\tdefault sample rate : {}", input_info.default_sample_rate);

    // Construct the input stream parameters.
    let input_stream_params = pa::StreamParameters {
        device : def_input,
        channel_count : 2,
        sample_format : pa::SampleFormat::Float32,
        suggested_latency : input_info.default_low_input_latency
    };

    let def_output = pa::device::get_default_output();
    let output_info = match pa::device::get_info(def_output) {
        Ok(info) => info,
        Err(err) => panic!("An error occurred while retrieving output info: {}", err.description()),
    };

    println!("Default output device name : {}", output_info.name);

    // Construct the output stream parameters.
    let output_stream_params = pa::StreamParameters {
        device : def_output,
        channel_count : 2,
        sample_format : pa::SampleFormat::Float32,
        suggested_latency : output_info.default_low_output_latency
    };

    let mut stream : pa::Stream<f32, f32> = pa::Stream::new();

    match stream.open(Some(&input_stream_params),
                      Some(&output_stream_params),
                      SAMPLE_RATE,
                      FRAMES,
                      pa::StreamFlags::ClipOff) {
        Ok(()) => println!("Successfully opened the stream."),
        Err(err) => println!("An error occurred while opening the stream: {}", err.description()),
    }

    match stream.start() {
        Ok(()) => println!("Successfully started the stream."),
        Err(err) => println!("An error occurred while starting the stream: {}", err.description()),
    }

    // We'll use this function to wait for read/write availability.
    let wait_for_stream = |f: || -> Result<Option<i64>, pa::error::Error>, name: &str| {
        'waiting_for_stream: loop {
            match f() {
                Ok(None) => (),
                Ok(Some(frames)) => {
                    println!("{} stream available with {} frames.", name, frames);
                    break 'waiting_for_stream
                },
                Err(err) => panic!("An error occurred while waiting for the {} stream: {}", name, err.description()),
            }
        }
    };

    // Now start the main read/write loop! In this example, we pass the input buffer directly to
    // the output buffer, so watch out for feedback.
    'stream: loop {
        wait_for_stream(|| stream.get_stream_read_available(), "Read");
        match stream.read(FRAMES) {
            Ok(input_samples)  => {
                wait_for_stream(|| stream.get_stream_write_available(), "Write");
                match stream.write(input_samples, FRAMES) {
                    Ok(()) => (),
                    Err(err) => {
                        println!("An error occurred while writing to the output stream: {}", err.description());
                        break 'stream
                    },
                }
            },
            Err(err) => {
                println!("An error occurred while reading from the input stream: {}", err.description());
                break 'stream
            }
        };
    }

    match stream.close() {
        Ok(()) => println!("Successfully closed the stream."),
        Err(err) => println!("An error occurred while closing the stream: {}", err.description()),
    }

    println!("");

    match pa::terminate() {
        Ok(()) => println!("Successfully terminated PortAudio."),
        Err(err) => println!("An error occurred while terminating PortAudio: {}", err.description()),
    }

}
<|endoftext|>