webgl: finish, flush, detachShader, generateMipmap, Uniform1i

This commit is contained in:
Connor Brewster 2016-03-26 18:23:16 -06:00
parent d0f692b2c5
commit 3fd7634f54
14 changed files with 634 additions and 12 deletions

View file

@ -266,6 +266,14 @@ macro_rules! make_nonzero_dimension_setter(
/// For use on non-jsmanaged types
/// Use #[derive(JSTraceable)] on JS managed types
macro_rules! no_jsmanaged_fields(
([$ty:ident; $count:expr]) => (
impl $crate::dom::bindings::trace::JSTraceable for [$ty; $count] {
#[inline]
fn trace(&self, _: *mut ::js::jsapi::JSTracer) {
// Do nothing
}
}
);
($($ty:ident),+) => (
$(
impl $crate::dom::bindings::trace::JSTraceable for $ty {

View file

@ -94,7 +94,10 @@ impl WebGLProgram {
let shader_slot = match shader.gl_type() {
constants::FRAGMENT_SHADER => &self.fragment_shader,
constants::VERTEX_SHADER => &self.vertex_shader,
_ => return Err(WebGLError::InvalidOperation),
_ => {
error!("detachShader: Unexpected shader type");
return Err(WebGLError::InvalidValue);
}
};
// TODO(emilio): Differentiate between same shader already assigned and other previous
@ -110,6 +113,32 @@ impl WebGLProgram {
Ok(())
}
/// glDetachShader
pub fn detach_shader(&self, shader: &WebGLShader) -> WebGLResult<()> {
let shader_slot = match shader.gl_type() {
constants::FRAGMENT_SHADER => &self.fragment_shader,
constants::VERTEX_SHADER => &self.vertex_shader,
_ => {
error!("detachShader: Unexpected shader type");
return Err(WebGLError::InvalidValue);
}
};
match shader_slot.get() {
Some(ref attached_shader) if attached_shader.id() != shader.id() =>
return Err(WebGLError::InvalidOperation),
None =>
return Err(WebGLError::InvalidOperation),
_ => {}
}
shader_slot.set(None);
self.renderer.send(CanvasMsg::WebGL(WebGLCommand::DetachShader(self.id, shader.id()))).unwrap();
Ok(())
}
/// glBindAttribLocation
pub fn bind_attrib_location(&self, index: u32, name: DOMString) -> WebGLResult<()> {
if name.len() > MAX_UNIFORM_AND_ATTRIBUTE_LEN {

View file

@ -181,6 +181,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
Root::from_ref(&*self.canvas)
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
fn Flush(&self) {
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Flush))
.unwrap();
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.11
fn Finish(&self) {
let (sender, receiver) = ipc::channel().unwrap();
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Finish(sender)))
.unwrap();
receiver.recv().unwrap()
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.1
fn DrawingBufferWidth(&self) -> i32 {
let (sender, receiver) = ipc::channel().unwrap();
@ -331,6 +347,15 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn DetachShader(&self, program: Option<&WebGLProgram>, shader: Option<&WebGLShader>) {
if let Some(program) = program {
if let Some(shader) = shader {
handle_potential_webgl_error!(self, program.detach_shader(shader));
}
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.9
fn BindAttribLocation(&self, program: Option<&WebGLProgram>,
index: u32, name: DOMString) {
@ -414,6 +439,21 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
}
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.8
fn GenerateMipmap(&self, target: u32) {
let slot = match target {
constants::TEXTURE_2D => &self.bound_texture_2d,
constants::TEXTURE_CUBE_MAP => &self.bound_texture_cube_map,
_ => return self.webgl_error(InvalidEnum),
};
match slot.get() {
Some(texture) => handle_potential_webgl_error!(self, texture.generate_mipmap()),
None => self.webgl_error(InvalidOperation)
}
}
#[allow(unsafe_code)]
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.5
fn BufferData(&self, _cx: *mut JSContext, target: u32, data: Option<*mut JSObject>, usage: u32) {
@ -949,6 +989,25 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
.unwrap()
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn Uniform1i(&self,
uniform: Option<&WebGLUniformLocation>,
val: i32) {
let uniform = match uniform {
Some(uniform) => uniform,
None => return,
};
match self.current_program.get() {
Some(ref program) if program.id() == uniform.program_id() => {},
_ => return self.webgl_error(InvalidOperation),
};
self.ipc_renderer
.send(CanvasMsg::WebGL(WebGLCommand::Uniform1i(uniform.id(), val)))
.unwrap()
}
// https://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14.10
fn Uniform1fv(&self,
uniform: Option<&WebGLUniformLocation>,
@ -1107,7 +1166,7 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
internal_format: u32,
format: u32,
data_type: u32,
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement >) {
source: Option<ImageDataOrHTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement>) {
let texture = match target {
constants::TEXTURE_2D => self.bound_texture_2d.get(),
constants::TEXTURE_CUBE_MAP => self.bound_texture_cube_map.get(),
@ -1169,11 +1228,22 @@ impl WebGLRenderingContextMethods for WebGLRenderingContext {
=> unimplemented!(),
};
if size.width < 0 || size.height < 0 || level < 0 {
self.webgl_error(WebGLError::InvalidOperation);
}
// TODO(emilio): Invert axis, convert colorspace, premultiply alpha if requested
let msg = WebGLCommand::TexImage2D(target, level, internal_format as i32,
size.width, size.height,
format, data_type, pixels);
// depth is always 1 when coming from html elements
handle_potential_webgl_error!(self, texture.unwrap().initialize(size.width as u32,
size.height as u32,
1,
internal_format,
level as u32));
self.ipc_renderer
.send(CanvasMsg::WebGL(msg))
.unwrap()

View file

@ -99,7 +99,7 @@ impl WebGLShader {
let validator = ShaderValidator::for_webgl(self.gl_type,
SHADER_OUTPUT_FORMAT,
&BuiltInResources::default()).unwrap();
match validator.compile_and_translate(&[source.as_bytes()]) {
match validator.compile_and_translate(&[source]) {
Ok(translated_source) => {
// NOTE: At this point we should be pretty sure that the compilation in the paint thread
// will succeed.

View file

@ -4,6 +4,7 @@
// https://www.khronos.org/registry/webgl/specs/latest/1.0/webgl.idl
use canvas_traits::CanvasMsg;
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::WebGLRenderingContextBinding::WebGLRenderingContextConstants as constants;
use dom::bindings::codegen::Bindings::WebGLTextureBinding;
use dom::bindings::global::GlobalRef;
@ -12,6 +13,7 @@ use dom::bindings::reflector::reflect_dom_object;
use dom::webglobject::WebGLObject;
use ipc_channel::ipc::{self, IpcSender};
use std::cell::Cell;
use std::cmp;
use webrender_traits::{WebGLCommand, WebGLError, WebGLResult};
pub enum TexParameterValue {
@ -19,6 +21,11 @@ pub enum TexParameterValue {
Int(i32),
}
const MAX_LEVEL_COUNT: usize = 31;
const MAX_FACE_COUNT: usize = 6;
no_jsmanaged_fields!([ImageInfo; MAX_LEVEL_COUNT * MAX_FACE_COUNT]);
#[dom_struct]
pub struct WebGLTexture {
webgl_object: WebGLObject,
@ -26,6 +33,13 @@ pub struct WebGLTexture {
/// The target to which this texture was bound the first time
target: Cell<Option<u32>>,
is_deleted: Cell<bool>,
is_initialized: Cell<bool>,
/// Stores information about mipmap levels and cubemap faces.
#[ignore_heap_size_of = "Arrays are cumbersome"]
image_info_array: DOMRefCell<[ImageInfo; MAX_LEVEL_COUNT * MAX_FACE_COUNT]>,
/// Face count can only be 1 or 6
face_count: Cell<u8>,
base_mipmap_level: u32,
#[ignore_heap_size_of = "Defined in ipc-channel"]
renderer: IpcSender<CanvasMsg>,
}
@ -37,6 +51,10 @@ impl WebGLTexture {
id: id,
target: Cell::new(None),
is_deleted: Cell::new(false),
is_initialized: Cell::new(false),
face_count: Cell::new(0),
base_mipmap_level: 0,
image_info_array: DOMRefCell::new([ImageInfo::new(); MAX_LEVEL_COUNT * MAX_FACE_COUNT]),
renderer: renderer,
}
}
@ -63,11 +81,22 @@ impl WebGLTexture {
// NB: Only valid texture targets come here
pub fn bind(&self, target: u32) -> WebGLResult<()> {
if self.is_deleted.get() {
return Err(WebGLError::InvalidOperation);
}
if let Some(previous_target) = self.target.get() {
if target != previous_target {
return Err(WebGLError::InvalidOperation);
}
} else {
// This is the first time binding
let face_count = match target {
constants::TEXTURE_2D => 1,
constants::TEXTURE_CUBE_MAP => 6,
_ => return Err(WebGLError::InvalidOperation)
};
self.face_count.set(face_count);
self.target.set(Some(target));
}
@ -76,6 +105,58 @@ impl WebGLTexture {
Ok(())
}
pub fn initialize(&self, width: u32, height: u32, depth: u32, internal_format: u32, level: u32) -> WebGLResult<()> {
let image_info = ImageInfo {
width: width,
height: height,
depth: depth,
internal_format: Some(internal_format),
is_initialized: true,
};
self.set_image_infos_at_level(level, image_info);
self.is_initialized.set(true);
Ok(())
}
pub fn generate_mipmap(&self) -> WebGLResult<()> {
let target = match self.target.get() {
Some(target) => target,
None => {
error!("Cannot generate mipmap on texture that has no target!");
return Err(WebGLError::InvalidOperation);
}
};
let base_image_info = self.base_image_info().unwrap();
if !base_image_info.is_initialized() {
return Err(WebGLError::InvalidOperation);
}
if target == constants::TEXTURE_CUBE_MAP && !self.is_cube_complete() {
return Err(WebGLError::InvalidOperation);
}
if !base_image_info.is_power_of_two() {
return Err(WebGLError::InvalidOperation);
}
if base_image_info.is_compressed_format() {
return Err(WebGLError::InvalidOperation);
}
self.renderer.send(CanvasMsg::WebGL(WebGLCommand::GenerateMipmap(target))).unwrap();
if self.base_mipmap_level + base_image_info.get_max_mimap_levels() == 0 {
return Err(WebGLError::InvalidOperation);
}
let last_level = self.base_mipmap_level + base_image_info.get_max_mimap_levels() - 1;
self.populate_mip_chain(self.base_mipmap_level, last_level)
}
pub fn delete(&self) {
if !self.is_deleted.get() {
self.is_deleted.set(true);
@ -145,6 +226,84 @@ impl WebGLTexture {
_ => Err(WebGLError::InvalidEnum),
}
}
pub fn populate_mip_chain(&self, first_level: u32, last_level: u32) -> WebGLResult<()> {
let base_image_info = self.image_info_at_face(0, first_level);
if !base_image_info.is_initialized() {
return Err(WebGLError::InvalidOperation);
}
let mut ref_width = base_image_info.width;
let mut ref_height = base_image_info.height;
if ref_width == 0 || ref_height == 0 {
return Err(WebGLError::InvalidOperation);
}
for level in (first_level + 1)..last_level {
if ref_width == 1 && ref_height == 1 {
break;
}
ref_width = cmp::max(1, ref_width / 2);
ref_height = cmp::max(1, ref_height / 2);
let image_info = ImageInfo {
width: ref_width,
height: ref_height,
depth: 0,
internal_format: base_image_info.internal_format,
is_initialized: base_image_info.is_initialized(),
};
self.set_image_infos_at_level(level, image_info);
}
Ok(())
}
fn is_cube_complete(&self) -> bool {
let image_info = self.base_image_info().unwrap();
if !image_info.is_defined() {
return false;
}
let ref_width = image_info.width;
let ref_format = image_info.internal_format;
for face in 0..self.face_count.get() {
let current_image_info = self.image_info_at_face(face, self.base_mipmap_level);
if !current_image_info.is_defined() {
return false;
}
// Compares height with width to enforce square dimensions
if current_image_info.internal_format != ref_format ||
current_image_info.width != ref_width ||
current_image_info.height != ref_width {
return false;
}
}
true
}
fn image_info_at_face(&self, face: u8, level: u32) -> ImageInfo {
let pos = (level * self.face_count.get() as u32) + face as u32;
self.image_info_array.borrow()[pos as usize]
}
fn set_image_infos_at_level(&self, level: u32, image_info: ImageInfo) {
for face in 0..self.face_count.get() {
let pos = (level * self.face_count.get() as u32) + face as u32;
self.image_info_array.borrow_mut()[pos as usize] = image_info;
}
}
fn base_image_info(&self) -> Option<ImageInfo> {
assert!((self.base_mipmap_level as usize) < MAX_LEVEL_COUNT);
Some(self.image_info_at_face(0, self.base_mipmap_level))
}
}
impl Drop for WebGLTexture {
@ -152,3 +311,50 @@ impl Drop for WebGLTexture {
self.delete();
}
}
#[derive(Clone, Copy, PartialEq, Debug, JSTraceable, HeapSizeOf)]
struct ImageInfo {
width: u32,
height: u32,
depth: u32,
internal_format: Option<u32>,
is_initialized: bool,
}
impl ImageInfo {
fn new() -> ImageInfo {
ImageInfo {
width: 0,
height: 0,
depth: 0,
internal_format: None,
is_initialized: false,
}
}
fn is_power_of_two(&self) -> bool {
self.width.is_power_of_two() && self.height.is_power_of_two() && self.depth.is_power_of_two()
}
fn is_initialized(&self) -> bool {
self.is_initialized
}
fn is_defined(&self) -> bool {
!self.internal_format.is_none()
}
fn get_max_mimap_levels(&self) -> u32 {
let largest = cmp::max(cmp::max(self.width, self.height), self.depth);
if largest == 0 {
return 0;
}
// FloorLog2(largest) + 1
(largest as f64).log2() as u32 + 1
}
fn is_compressed_format(&self) -> bool {
// TODO: Once Servo supports compressed formats, check for them here
false
}
}

View file

@ -548,7 +548,7 @@ interface WebGLRenderingContextBase
void depthFunc(GLenum func);
void depthMask(GLboolean flag);
void depthRange(GLclampf zNear, GLclampf zFar);
//void detachShader(WebGLProgram? program, WebGLShader? shader);
void detachShader(WebGLProgram? program, WebGLShader? shader);
void disable(GLenum cap);
//void disableVertexAttribArray(GLuint index);
void drawArrays(GLenum mode, GLint first, GLsizei count);
@ -556,8 +556,8 @@ interface WebGLRenderingContextBase
void enable(GLenum cap);
void enableVertexAttribArray(GLuint index);
//void finish();
//void flush();
void finish();
void flush();
//void framebufferRenderbuffer(GLenum target, GLenum attachment,
// GLenum renderbuffertarget,
// WebGLRenderbuffer? renderbuffer);
@ -565,7 +565,7 @@ interface WebGLRenderingContextBase
// WebGLTexture? texture, GLint level);
void frontFace(GLenum mode);
//void generateMipmap(GLenum target);
void generateMipmap(GLenum target);
//WebGLActiveInfo? getActiveAttrib(WebGLProgram? program, GLuint index);
//WebGLActiveInfo? getActiveUniform(WebGLProgram? program, GLuint index);
@ -647,7 +647,7 @@ interface WebGLRenderingContextBase
void uniform1f(WebGLUniformLocation? location, GLfloat x);
//void uniform1fv(WebGLUniformLocation? location, Float32Array v);
void uniform1fv(WebGLUniformLocation? location, sequence<GLfloat> v);
//void uniform1i(WebGLUniformLocation? location, GLint x);
void uniform1i(WebGLUniformLocation? location, GLint x);
//void uniform1iv(WebGLUniformLocation? location, Int32Array v);
//void uniform1iv(WebGLUniformLocation? location, sequence<long> v);
//void uniform2f(WebGLUniformLocation? location, GLfloat x, GLfloat y);
@ -717,4 +717,3 @@ interface WebGLRenderingContext
{
};
WebGLRenderingContext implements WebGLRenderingContextBase;