Skip to content

Commit

Permalink
It implements enough WebGL spec to draw a triangle
Browse files Browse the repository at this point in the history
  • Loading branch information
dmarcos committed May 3, 2015
1 parent a346f95 commit 79b8e7a
Show file tree
Hide file tree
Showing 17 changed files with 741 additions and 70 deletions.
19 changes: 19 additions & 0 deletions components/canvas/canvas_msg.rs
Expand Up @@ -52,8 +52,27 @@ pub enum Canvas2dMsg {

#[derive(Clone)]
pub enum CanvasWebGLMsg {
AttachShader(u32, u32),
BindBuffer(u32, u32),
BufferData(u32, Vec<f32>, u32),
Clear(u32),
ClearColor(f32, f32, f32, f32),
CompileShader(u32),
CreateBuffer(Sender<u32>),
CreateProgram(Sender<u32>),
CreateShader(u32, Sender<u32>),
DrawArrays(u32, i32, i32),
EnableVertexAttribArray(u32),
GetAttribLocation(u32, String, Sender<i32>),
GetShaderInfoLog(u32, Sender<String>),
GetShaderParameter(u32, u32, Sender<i32>),
GetUniformLocation(u32, String, Sender<u32>),
LinkProgram(u32),
ShaderSource(u32, Vec<String>),
Uniform4fv(u32, Vec<f32>),
UseProgram(u32),
VertexAttribPointer2f(u32, i32, bool, i32, i64),
Viewport(i32, i32, i32, i32),
}

#[derive(Clone)]
Expand Down
125 changes: 122 additions & 3 deletions components/canvas/webgl_paint_task.rs
Expand Up @@ -11,6 +11,7 @@ use gleam::gl::types::{GLsizei};
use util::task::spawn_named;

use std::borrow::ToOwned;
use std::slice::bytes::copy_memory;
use std::sync::mpsc::{channel, Sender};
use util::vec::byte_swap;
use offscreen_gl_context::{GLContext, GLContextAttributes};
Expand Down Expand Up @@ -74,8 +75,29 @@ impl WebGLPaintTask {
match port.recv().unwrap() {
CanvasMsg::WebGL(message) => {
match message {
CanvasWebGLMsg::AttachShader(program_id, shader_id) => painter.attach_shader(program_id, shader_id),
CanvasWebGLMsg::BindBuffer(buffer_type, buffer_id) => painter.bind_buffer(buffer_type, buffer_id),
CanvasWebGLMsg::BufferData(buffer_type, data, usage) => painter.buffer_data(buffer_type, data, usage),
CanvasWebGLMsg::Clear(mask) => painter.clear(mask),
CanvasWebGLMsg::ClearColor(r, g, b, a) => painter.clear_color(r, g, b, a),
CanvasWebGLMsg::CreateBuffer(chan) => painter.create_buffer(chan),
CanvasWebGLMsg::DrawArrays(mode, first, count) => painter.draw_arrays(mode, first, count),
CanvasWebGLMsg::EnableVertexAttribArray(attrib_id) => painter.enable_vertex_attrib_array(attrib_id),
CanvasWebGLMsg::GetAttribLocation(program_id, name, chan) => painter.get_attrib_location(program_id, name, chan),
CanvasWebGLMsg::GetShaderInfoLog(shader_id, chan) => painter.get_shader_info_log(shader_id, chan),
CanvasWebGLMsg::GetShaderParameter(shader_id, param_id, chan) => painter.get_shader_parameter(shader_id, param_id, chan),
CanvasWebGLMsg::GetUniformLocation(program_id, name, chan) => painter.get_uniform_location(program_id, name, chan),
CanvasWebGLMsg::CompileShader(shader_id) => painter.compile_shader(shader_id),
CanvasWebGLMsg::CreateProgram(chan) => painter.create_program(chan),
CanvasWebGLMsg::CreateShader(shader_type, chan) => painter.create_shader(shader_type, chan),
CanvasWebGLMsg::LinkProgram(program_id) => painter.link_program(program_id),
CanvasWebGLMsg::ShaderSource(shader_id, source) => painter.shader_source(shader_id, source),
CanvasWebGLMsg::Uniform4fv(uniform_id, data) => painter.uniform_4fv(uniform_id, data),
CanvasWebGLMsg::UseProgram(program_id) => painter.use_program(program_id),
CanvasWebGLMsg::VertexAttribPointer2f(attrib_id, size, normalized, stride, offset) => {
painter.vertex_attrib_pointer_f32(attrib_id, size, normalized, stride, offset);
},
CanvasWebGLMsg::Viewport(x, y, width, height) => painter.viewport(x, y, width, height),
}
},
CanvasMsg::Common(message) => {
Expand All @@ -93,6 +115,18 @@ impl WebGLPaintTask {
Ok(chan)
}

fn attach_shader(&self, program_id: u32, shader_id: u32) {
gl::attach_shader(program_id, shader_id);
}

fn bind_buffer(&self, buffer_type: u32, buffer_id: u32) {
gl::bind_buffer(buffer_type, buffer_id);
}

fn buffer_data(&self, buffer_type: u32, data: Vec<f32>, usage: u32) {
gl::buffer_data(buffer_type, &data, usage);
}

fn clear(&self, mask: u32) {
gl::clear(mask);
}
Expand All @@ -101,19 +135,104 @@ impl WebGLPaintTask {
gl::clear_color(r, g, b, a);
}

fn create_buffer(&self, chan: Sender<u32>) {
let buffers = gl::gen_buffers(1);
chan.send(buffers[0]).unwrap();
}

fn compile_shader(&self, shader_id: u32) {
gl::compile_shader(shader_id);
}

fn create_program(&self, chan: Sender<u32>) {
let program = gl::create_program();
chan.send(program).unwrap();
}

fn create_shader(&self, shader_type: u32, chan: Sender<u32>) {
let shader = gl::create_shader(shader_type);
chan.send(shader).unwrap();
}

fn draw_arrays(&self, mode: u32, first: i32, count: i32) {
gl::draw_arrays(mode, first, count);
}

fn enable_vertex_attrib_array(&self, attrib_id: u32) {
gl::enable_vertex_attrib_array(attrib_id);
}

fn get_attrib_location(&self, program_id: u32, name: String, chan: Sender<i32> ) {
let attrib_location = gl::get_attrib_location(program_id, name.as_slice());
chan.send(attrib_location).unwrap();
}

fn get_shader_info_log(&self, shader_id: u32, chan: Sender<String>) {
let info = gl::get_shader_info_log(shader_id);
chan.send(info).unwrap();
}

fn get_shader_parameter(&self, shader_id: u32, param_id: u32, chan: Sender<i32>) {
let parameter = gl::get_shader_iv(shader_id, param_id);
chan.send(parameter as i32).unwrap();
}

fn get_uniform_location(&self, program_id: u32, name: String, chan: Sender<u32>) {
let uniform_location = gl::get_uniform_location(program_id, name.as_slice());
chan.send(uniform_location as u32).unwrap();
}

fn link_program(&self, program_id: u32) {
gl::link_program(program_id);
}

fn send_pixel_contents(&mut self, chan: Sender<Vec<u8>>) {
// FIXME(#5652, dmarcos) Instead of a readback strategy we have
// to layerize the canvas
let width = self.size.width as usize;
let height = self.size.height as usize;
let mut pixels = gl::read_pixels(0, 0,
self.size.width as gl::GLsizei,
self.size.height as gl::GLsizei,
gl::RGBA, gl::UNSIGNED_BYTE);
self.size.width as gl::GLsizei,
self.size.height as gl::GLsizei,
gl::RGBA, gl::UNSIGNED_BYTE);
// flip image vertically (texture is upside down)
let orig_pixels = pixels.clone();
let stride = width * 4;
for y in 0..height {
let dst_start = y * stride;
let src_start = (height - y - 1) * stride;
let src_slice = &orig_pixels[src_start .. src_start + stride];
copy_memory(&mut pixels[dst_start .. dst_start + stride],
&src_slice[..stride]);
}

// rgba -> bgra
byte_swap(&mut pixels);
chan.send(pixels).unwrap();
}

fn shader_source(&self, shader_id: u32, source_lines: Vec<String>) {
let mut lines: Vec<&[u8]> = source_lines.iter().map(|line| line.as_bytes()).collect();
gl::shader_source(shader_id, lines.as_mut_slice());
}

fn uniform_4fv(&self, uniform_id: u32, data: Vec<f32>) {
gl::uniform_4f(uniform_id as i32, data[0], data[1], data[2], data[3]);
}

fn use_program(&self, program_id: u32) {
gl::use_program(program_id);
}

fn vertex_attrib_pointer_f32(&self, attrib_id: u32, size: i32,
normalized: bool, stride: i32, offset: i64) {
gl::vertex_attrib_pointer_f32(attrib_id, size, normalized, stride, offset as u32);
}

fn viewport(&self, x: i32, y: i32, width: i32, height: i32) {
gl::viewport(x, y, width, height);
}

fn recreate(&mut self, size: Size2D<i32>) {
// TODO(ecoal95): GLContext should support a resize() method
if size.width > self.original_context_size.width ||
Expand Down
5 changes: 5 additions & 0 deletions components/script/dom/mod.rs
Expand Up @@ -325,7 +325,12 @@ pub mod urlsearchparams;
pub mod userscripts;
pub mod validitystate;
pub mod virtualmethods;
pub mod webglobject;
pub mod webglbuffer;
pub mod webglprogram;
pub mod webglrenderingcontext;
pub mod webglshader;
pub mod webgluniformlocation;
pub mod websocket;
pub mod window;
pub mod worker;
Expand Down
38 changes: 38 additions & 0 deletions components/script/dom/webglbuffer.rs
@@ -0,0 +1,38 @@
/* 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/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use dom::bindings::codegen::Bindings::WebGLBufferBinding;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{Temporary, JSRef};
use dom::bindings::utils::{Reflector, reflect_dom_object};

#[dom_struct]
pub struct WebGLBuffer {
reflector_: Reflector,
id: u32,
}

impl WebGLBuffer {
fn new_inherited(id: u32) -> WebGLBuffer {
WebGLBuffer {
reflector_: Reflector::new(),
id: id,
}
}

pub fn new(global: GlobalRef, id: u32) -> Temporary<WebGLBuffer> {
reflect_dom_object(box WebGLBuffer::new_inherited(id), global, WebGLBufferBinding::Wrap)
}
}

pub trait WebGLBufferHelpers {
fn get_id(&self) -> u32;
}

impl<'a> WebGLBufferHelpers for JSRef<'a, WebGLBuffer> {
fn get_id(&self) -> u32 {
self.id
}
}
26 changes: 26 additions & 0 deletions components/script/dom/webglobject.rs
@@ -0,0 +1,26 @@
/* 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/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use dom::bindings::codegen::Bindings::WebGLObjectBinding;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{Temporary};
use dom::bindings::utils::{Reflector, reflect_dom_object};

#[dom_struct]
pub struct WebGLObject {
reflector_: Reflector,
}

impl WebGLObject {
fn new_inherited() -> WebGLObject {
WebGLObject {
reflector_: Reflector::new(),
}
}

pub fn new(global: GlobalRef) -> Temporary<WebGLObject> {
reflect_dom_object(box WebGLObject::new_inherited(), global, WebGLObjectBinding::Wrap)
}
}
38 changes: 38 additions & 0 deletions components/script/dom/webglprogram.rs
@@ -0,0 +1,38 @@
/* 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/. */

// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use dom::bindings::codegen::Bindings::WebGLProgramBinding;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{Temporary, JSRef};
use dom::bindings::utils::{Reflector, reflect_dom_object};

#[dom_struct]
pub struct WebGLProgram {
reflector_: Reflector,
id: u32,
}

impl WebGLProgram {
fn new_inherited(id: u32) -> WebGLProgram {
WebGLProgram {
reflector_: Reflector::new(),
id: id,
}
}

pub fn new(global: GlobalRef, id: u32) -> Temporary<WebGLProgram> {
reflect_dom_object(box WebGLProgram::new_inherited(id), global, WebGLProgramBinding::Wrap)
}
}

pub trait WebGLProgramHelpers {
fn get_id(&self) -> u32;
}

impl<'a> WebGLProgramHelpers for JSRef<'a, WebGLProgram> {
fn get_id(&self) -> u32 {
self.id
}
}

0 comments on commit 79b8e7a

Please sign in to comment.